diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..02ec259 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Bootstrap +app/bootstrap* + +# Symfony directories +vendor/* +*/logs/* +*/cache/* +web/uploads/* +web/bundles/* + +# Configuration files +app/config/parameters.ini +!/app/bootstrap.php.cache \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index fb474b7..0000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -php -=== - -php diff --git a/app/.htaccess b/app/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/app/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/app/AppCache.php b/app/AppCache.php new file mode 100644 index 0000000..ddb51db --- /dev/null +++ b/app/AppCache.php @@ -0,0 +1,9 @@ +getEnvironment(), array('dev', 'test'))) { + $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); + $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); + $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); + } + + return $bundles; + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(__DIR__.'/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..96e0e94 --- /dev/null +++ b/app/Resources/views/base.html.twig @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + {% block title %}Samuel Berthelot Photographie{% endblock %} + + {% block stylesheets %} + + {% stylesheets + 'css/bootstrap.css' + 'css/theme.css' + 'css/plugins.css' + 'css/responsive.css' + 'css/theme_settings.css' + 'css/skin.css' + 'css/color_theme.css' + 'css/fs_gallery.css' + 'css/custom.css' + filter='cssrewrite' + %} + + {% endstylesheets %} + {% endblock %} + + + + + + +
+
+ + {% block navigation %} + + {% endblock %} +
+
+ + + {% block body %}{% endblock %} + + {# {% block footer %} + Symfony2 blog tutorial - created by dsyph3r + {% endblock %} #} + + + + + + {% javascripts + 'js/theme.js' + 'js/sorting.js' + 'js/jquery.isotope.min.js' + 'js/masonry.pkgd.min.js' + %} + + {% endjavascripts %} + + {% block javascripts %} + + {% endblock %} + + \ No newline at end of file diff --git a/app/SymfonyRequirements.php b/app/SymfonyRequirements.php new file mode 100644 index 0000000..939360a --- /dev/null +++ b/app/SymfonyRequirements.php @@ -0,0 +1,669 @@ + + * + * 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 overriden 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 Boolean $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 Boolean $optional Whether this is only an optional recommendation not a mandatory requirement + */ + public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false) + { + $this->fulfilled = (Boolean) $fulfilled; + $this->testMessage = (string) $testMessage; + $this->helpHtml = (string) $helpHtml; + $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText; + $this->optional = (Boolean) $optional; + } + + /** + * Returns whether the requirement is fulfilled. + * + * @return Boolean 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 Boolean 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 Boolean|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 Boolean $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 Boolean $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 Boolean $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 Boolean $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 Boolean|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 Boolean $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 Boolean|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 Boolean $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 recommmendations. + * + * @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 Boolean 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.' + ); + + $baseDir = basename(__DIR__); + + $this->addRequirement( + is_writable(__DIR__.'/cache'), + "$baseDir/cache/ directory must be writable", + "Change the permissions of the \"$baseDir/cache/\" directory so that the web server can write into it." + ); + + $this->addRequirement( + is_writable(__DIR__.'/logs'), + "$baseDir/logs/ directory must be writable", + "Change the permissions of the \"$baseDir/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('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+).' + ); + + /* optional recommendations follow */ + + $this->addRecommendation( + file_get_contents(__FILE__) === file_get_contents(__DIR__.'/../vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/SymfonyRequirements.php'), + '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.' + ); + + 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-XML module should be installed', + 'Install and enable the PHP-XML module.' + ); + + $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.' + ); + + 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( + class_exists('Locale'), + 'intl extension should be available', + 'Install and enable the intl extension (used for validators).' + ); + + if (class_exists('Collator')) { + $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.' + ); + } + + if (class_exists('Locale')) { + 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+).' + ); + } + + $accelerator = + (function_exists('apc_store') && ini_get('apc.enabled')) + || + function_exists('eaccelerator_put') && ini_get('eaccelerator.enable') + || + function_exists('xcache_set') + ; + + $this->addRecommendation( + $accelerator, + 'a PHP accelerator should be installed', + 'Install and enable a PHP accelerator like APC (highly recommended).' + ); + + $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), + sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'), + 'Install PDO drivers (mandatory for Doctrine).' + ); + } + } +} diff --git a/app/autoload.php b/app/autoload.php new file mode 100644 index 0000000..4eec4b1 --- /dev/null +++ b/app/autoload.php @@ -0,0 +1,13 @@ +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($path, $default = null, $deep = false) +{ +if (!$deep || false === $pos = strpos($path,'[')) { +return array_key_exists($path, $this->parameters) ? $this->parameters[$path] : $default; +} +$root = substr($path, 0, $pos); +if (!array_key_exists($root, $this->parameters)) { +return $default; +} +$value = $this->parameters[$root]; +$currentKey = null; +for ($i = $pos, $c = strlen($path); $i < $c; $i++) { +$char = $path[$i]; +if ('['=== $char) { +if (null !== $currentKey) { +throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "[" at position %d.', $i)); +} +$currentKey =''; +} elseif (']'=== $char) { +if (null === $currentKey) { +throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "]" at position %d.', $i)); +} +if (!is_array($value) || !array_key_exists($currentKey, $value)) { +return $default; +} +$value = $value[$currentKey]; +$currentKey = null; +} else { +if (null === $currentKey) { +throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "%s" at position %d.', $char, $i)); +} +$currentKey .= $char; +} +} +if (null !== $currentKey) { +throw new \InvalidArgumentException(sprintf('Malformed path. Path must end with "]".')); +} +return $value; +} +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 ='', $deep = false) +{ +return preg_replace('/[^[:alpha:]]/','', $this->get($key, $default, $deep)); +} +public function getAlnum($key, $default ='', $deep = false) +{ +return preg_replace('/[^[:alnum:]]/','', $this->get($key, $default, $deep)); +} +public function getDigits($key, $default ='', $deep = false) +{ +return str_replace(array('-','+'),'', $this->filter($key, $default, $deep, FILTER_SANITIZE_NUMBER_INT)); +} +public function getInt($key, $default = 0, $deep = false) +{ +return (int) $this->get($key, $default, $deep); +} +public function filter($key, $default = null, $deep = false, $filter=FILTER_DEFAULT, $options=array()) +{ +$value = $this->get($key, $default, $deep); +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; +protected $cacheControl; +public function __construct(array $headers = array()) +{ +$this->cacheControl = array(); +$this->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 (array_keys($data['name']) as $key) { +$files[$key] = $this->fixPhpFilesArray(array('error'=> $data['error'][$key],'name'=> $data['name'][$key],'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) && (0 === stripos($authorizationHeader,'basic'))) { +$exploded = explode(':', base64_decode(substr($authorizationHeader, 6))); +if (count($exploded) == 2) { +list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded; +} +} +} +if (isset($headers['PHP_AUTH_USER'])) { +$headers['AUTHORIZATION'] ='Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']); +} +return $headers; +} +} +} +namespace Symfony\Component\HttpFoundation +{ +use Symfony\Component\HttpFoundation\Session\SessionInterface; +class Request +{ +const HEADER_CLIENT_IP ='client_ip'; +const HEADER_CLIENT_HOST ='client_host'; +const HEADER_CLIENT_PROTO ='client_proto'; +const HEADER_CLIENT_PORT ='client_port'; +protected static $trustedProxies = array(); +protected static $trustedHeaders = array( +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 $acceptableContentTypes; +protected $pathInfo; +protected $requestUri; +protected $baseUrl; +protected $basePath; +protected $method; +protected $format; +protected $session; +protected $locale; +protected $defaultLocale ='en'; +protected static $formats; +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->acceptableContentTypes = null; +$this->pathInfo = null; +$this->requestUri = null; +$this->baseUrl = null; +$this->basePath = null; +$this->method = null; +$this->format = null; +} +public static function createFromGlobals() +{ +$request = new static($_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; +} +if (isset($components['query'])) { +parse_str(html_entity_decode($components['query']), $qs); +$query = array_replace($qs, $query); +} +$queryString = http_build_query($query,'','&'); +$server['REQUEST_URI'] = $components['path'].(''!== $queryString ?'?'.$queryString :''); +$server['QUERY_STRING'] = $queryString; +return new static($query, $request, array(), $cookies, $files, $server, $content); +} +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->acceptableContentTypes = null; +$dup->pathInfo = null; +$dup->requestUri = null; +$dup->baseUrl = null; +$dup->basePath = null; +$dup->method = null; +$dup->format = 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() +{ +return +sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n". +$this->headers."\r\n". +$this->getContent(); +} +public function overrideGlobals() +{ +$_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('variable_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 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, $deep = false) +{ +return $this->query->get($key, $this->attributes->get($key, $this->request->get($key, $default, $deep), $deep), $deep); +} +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() +{ +$ip = $this->server->get('REMOTE_ADDR'); +if (!self::$trustedProxies) { +return array($ip); +} +if (!self::$trustedHeaders[self::HEADER_CLIENT_IP] || !$this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { +return array($ip); +} +$clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); +$clientIps[] = $ip; +$trustedProxies = !self::$trustedProxies ? array($ip) : self::$trustedProxies; +$ip = $clientIps[0]; +foreach ($clientIps as $key => $clientIp) { +if (IpUtils::checkIp($clientIp, $trustedProxies)) { +unset($clientIps[$key]); +continue; +} +} +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 (self::$trustedProxies) { +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; +} +} +return $this->server->get('SERVER_PORT'); +} +public function getUser() +{ +return $this->server->get('PHP_AUTH_USER'); +} +public function getPassword() +{ +return $this->server->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 getQueryString() +{ +$qs = static::normalizeQueryString($this->server->get('QUERY_STRING')); +return''=== $qs ? null : $qs; +} +public function isSecure() +{ +if (self::$trustedProxies && self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && $proto = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO])) { +return in_array(strtolower($proto), array('https','on','1')); +} +return'on'== strtolower($this->server->get('HTTPS')) || 1 == $this->server->get('HTTPS'); +} +public function getHost() +{ +if (self::$trustedProxies && 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_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host)) { +throw new \UnexpectedValueException('Invalid 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; +} +} +return null; +} +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->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 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) +{ +if (false === $this->content || (true === $asResource && null !== $this->content)) { +throw new \LogicException('getContent() can only be called once when using the resource return type.'); +} +if (true === $asResource) { +$this->content = false; +return fopen('php://input','rb'); +} +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 (array_keys($languages) as $lang) { +if (strstr($lang,'-')) { +$codes = explode('-', $lang); +if ($codes[0] =='i') { +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 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, dirname($baseUrl))) { +return rtrim($prefix,'/'); +} +$truncatedRequestUri = $requestUri; +if (($pos = strpos($requestUri,'?')) !== false) { +$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,'/'); +} +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); +} +if ((null !== $baseUrl) && (false === ($pathInfo = substr($requestUri, strlen($baseUrl))))) { +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'), +); +} +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("#^(%[[:xdigit:]]{2}|.){{$len}}#", $string, $match)) { +return $match[0]; +} +return false; +} +} +} +namespace Symfony\Component\HttpFoundation +{ +class Response +{ +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', +306 =>'Reserved', +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 =>'Request Entity Too Large', +414 =>'Request-URI Too Long', +415 =>'Unsupported Media Type', +416 =>'Requested 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'); +if (!$this->headers->has('Date')) { +$this->setDate(new \DateTime(null, new \DateTimeZone('UTC'))); +} +} +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() || in_array($this->statusCode, array(204, 304))) { +$this->setContent(null); +} +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 === strpos($headers->get('Content-Type'),'text/') && false === strpos($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; +} +header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)); +foreach ($this->headers->allPreserveCase() as $name => $values) { +foreach ($values as $value) { +header($name.': '.$value, false); +} +} +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) { +$previous = null; +$obStatus = ob_get_status(1); +while (($level = ob_get_level()) > 0 && $level !== $previous) { +$previous = $level; +if ($obStatus[$level - 1] && isset($obStatus[$level - 1]['del']) && $obStatus[$level - 1]['del']) { +ob_end_flush(); +} +} +flush(); +} +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] :''; +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->has('proxy-revalidate'); +} +public function getDate() +{ +return $this->headers->getDate('Date', new \DateTime()); +} +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'); +} +return null; +} +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(); +} +return null; +} +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')) { +return array(); +} +return is_array($vary) ? $vary : preg_split('/[\s,]+/', $vary); +} +public function setVary($headers, $replace = true) +{ +$this->headers->set('Vary', $headers, $replace); +return $this; +} +public function isNotModified(Request $request) +{ +if (!$request->isMethodSafe()) { +return false; +} +$lastModified = $request->headers->get('If-Modified-Since'); +$notModified = false; +if ($etags = $request->getEtags()) { +$notModified = (in_array($this->getEtag(), $etags) || in_array('*', $etags)) && (!$lastModified || $this->headers->get('Last-Modified') == $lastModified); +} elseif ($lastModified) { +$notModified = $lastModified == $this->headers->get('Last-Modified'); +} +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(201, 204, 304)); +} +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 (intval(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) +{ +$this->setCookie(new Cookie($name, null, 1, $path, $domain)); +} +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; +const SCOPE_CONTAINER ='container'; +const SCOPE_PROTOTYPE ='prototype'; +public function set($id, $service, $scope = self::SCOPE_CONTAINER); +public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE); +public function has($id); +public function getParameter($name); +public function hasParameter($name); +public function setParameter($name, $value); +public function enterScope($name); +public function leaveScope($name); +public function addScope(ScopeInterface $scope); +public function hasScope($name); +public function isScopeActive($name); +} +} +namespace Symfony\Component\DependencyInjection +{ +interface IntrospectableContainerInterface extends ContainerInterface +{ +public function initialized($id); +} +} +namespace Symfony\Component\DependencyInjection +{ +use Symfony\Component\DependencyInjection\Exception\InactiveScopeException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +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 IntrospectableContainerInterface +{ +protected $parameterBag; +protected $services; +protected $methodMap; +protected $aliases; +protected $scopes; +protected $scopeChildren; +protected $scopedServices; +protected $scopeStacks; +protected $loading = array(); +public function __construct(ParameterBagInterface $parameterBag = null) +{ +$this->parameterBag = null === $parameterBag ? new ParameterBag() : $parameterBag; +$this->services = array(); +$this->scopes = array(); +$this->scopeChildren = array(); +$this->scopedServices = array(); +$this->scopeStacks = array(); +$this->set('service_container', $this); +} +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, $scope = self::SCOPE_CONTAINER) +{ +if (self::SCOPE_PROTOTYPE === $scope) { +throw new InvalidArgumentException(sprintf('You cannot set service "%s" of scope "prototype".', $id)); +} +$id = strtolower($id); +if (self::SCOPE_CONTAINER !== $scope) { +if (!isset($this->scopedServices[$scope])) { +throw new RuntimeException(sprintf('You cannot set service "%s" of inactive scope.', $id)); +} +$this->scopedServices[$scope][$id] = $service; +} +$this->services[$id] = $service; +if (method_exists($this, $method ='synchronize'.strtr($id, array('_'=>'','.'=>'_')).'Service')) { +$this->$method(); +} +} +public function has($id) +{ +$id = strtolower($id); +return array_key_exists($id, $this->services) || method_exists($this,'get'.strtr($id, array('_'=>'','.'=>'_')).'Service'); +} +public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) +{ +$id = strtolower($id); +if (isset($this->aliases[$id])) { +$id = $this->aliases[$id]; +} +if (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 (method_exists($this, $method ='get'.strtr($id, array('_'=>'','.'=>'_')).'Service')) { +} else { +if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { +if (!$id) { +throw new ServiceNotFoundException($id); +} +$alternatives = array(); +foreach (array_keys($this->services) as $key) { +$lev = levenshtein($id, $key); +if ($lev <= strlen($id) / 3 || false !== strpos($key, $id)) { +$alternatives[] = $key; +} +} +throw new ServiceNotFoundException($id, null, null, $alternatives); +} +return null; +} +$this->loading[$id] = true; +try { +$service = $this->$method(); +} catch (\Exception $e) { +unset($this->loading[$id]); +if (array_key_exists($id, $this->services)) { +unset($this->services[$id]); +} +if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { +return null; +} +throw $e; +} +unset($this->loading[$id]); +return $service; +} +public function initialized($id) +{ +return array_key_exists(strtolower($id), $this->services); +} +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]); +} +} +return array_unique(array_merge($ids, array_keys($this->services))); +} +public function enterScope($name) +{ +if (!isset($this->scopes[$name])) { +throw new InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name)); +} +if (self::SCOPE_CONTAINER !== $this->scopes[$name] && !isset($this->scopedServices[$this->scopes[$name]])) { +throw new RuntimeException(sprintf('The parent scope "%s" must be active when entering this scope.', $this->scopes[$name])); +} +if (isset($this->scopedServices[$name])) { +$services = array($this->services, $name => $this->scopedServices[$name]); +unset($this->scopedServices[$name]); +foreach ($this->scopeChildren[$name] as $child) { +if (isset($this->scopedServices[$child])) { +$services[$child] = $this->scopedServices[$child]; +unset($this->scopedServices[$child]); +} +} +$this->services = call_user_func_array('array_diff_key', $services); +array_shift($services); +if (!isset($this->scopeStacks[$name])) { +$this->scopeStacks[$name] = new \SplStack(); +} +$this->scopeStacks[$name]->push($services); +} +$this->scopedServices[$name] = array(); +} +public function leaveScope($name) +{ +if (!isset($this->scopedServices[$name])) { +throw new InvalidArgumentException(sprintf('The scope "%s" is not active.', $name)); +} +$services = array($this->services, $this->scopedServices[$name]); +unset($this->scopedServices[$name]); +foreach ($this->scopeChildren[$name] as $child) { +if (!isset($this->scopedServices[$child])) { +continue; +} +$services[] = $this->scopedServices[$child]; +unset($this->scopedServices[$child]); +} +$this->services = call_user_func_array('array_diff_key', $services); +if (isset($this->scopeStacks[$name]) && count($this->scopeStacks[$name]) > 0) { +$services = $this->scopeStacks[$name]->pop(); +$this->scopedServices += $services; +foreach ($services as $array) { +foreach ($array as $id => $service) { +$this->set($id, $service, $name); +} +} +} +} +public function addScope(ScopeInterface $scope) +{ +$name = $scope->getName(); +$parentScope = $scope->getParentName(); +if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) { +throw new InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name)); +} +if (isset($this->scopes[$name])) { +throw new InvalidArgumentException(sprintf('A scope with name "%s" already exists.', $name)); +} +if (self::SCOPE_CONTAINER !== $parentScope && !isset($this->scopes[$parentScope])) { +throw new InvalidArgumentException(sprintf('The parent scope "%s" does not exist, or is invalid.', $parentScope)); +} +$this->scopes[$name] = $parentScope; +$this->scopeChildren[$name] = array(); +while ($parentScope !== self::SCOPE_CONTAINER) { +$this->scopeChildren[$parentScope][] = $name; +$parentScope = $this->scopes[$parentScope]; +} +} +public function hasScope($name) +{ +return isset($this->scopes[$name]); +} +public function isScopeActive($name) +{ +return isset($this->scopedServices[$name]); +} +public static function camelize($id) +{ +return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) { return ('.'=== $match[1] ?'_':'').strtoupper($match[2]); }, $id); +} +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,'_','.'))); +} +} +} +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\HttpKernelInterface; +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 isClassInActiveBundle($class); +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\ClosureLoader; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +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; +protected $bundleMap; +protected $container; +protected $rootDir; +protected $environment; +protected $debug; +protected $booted; +protected $name; +protected $startTime; +protected $classes; +protected $loadClassCache; +const VERSION ='2.3.1'; +const VERSION_ID ='20301'; +const MAJOR_VERSION ='2'; +const MINOR_VERSION ='3'; +const RELEASE_VERSION ='1'; +const EXTRA_VERSION =''; +public function __construct($environment, $debug) +{ +$this->environment = $environment; +$this->debug = (Boolean) $debug; +$this->booted = false; +$this->rootDir = $this->getRootDir(); +$this->name = $this->getName(); +$this->classes = array(); +$this->bundles = array(); +if ($this->debug) { +$this->startTime = microtime(true); +} +$this->init(); +} +public function init() +{ +} +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 isClassInActiveBundle($class) +{ +foreach ($this->getBundles() as $bundle) { +if (0 === strpos($class, $bundle->getNamespace())) { +return true; +} +} +return false; +} +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 = str_replace('\\','/', 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 (count($diff = array_values(array_diff(array_keys($directChildren), array_keys($this->bundles))))) { +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; +$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'=> $this->rootDir,'kernel.environment'=> $this->environment,'kernel.debug'=> $this->debug,'kernel.name'=> $this->name,'kernel.cache_dir'=> $this->getCacheDir(),'kernel.logs_dir'=> $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)) { +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)); +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')) { +$container->setProxyInstantiator(new RuntimeInstantiator()); +} +return $container; +} +protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container, $class, $baseClass) +{ +$dumper = new PhpDumper($container); +if (class_exists('ProxyManager\Configuration')) { +$dumper->setProxyDumper(new ProxyDumper()); +} +$content = $dumper->dump(array('class'=> $class,'base_class'=> $baseClass)); +if (!$this->debug) { +$content = self::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 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); +for (reset($tokens); false !== $token = current($tokens); next($tokens)) { +if (is_string($token)) { +$rawChunk .= $token; +} elseif (T_START_HEREDOC === $token[0]) { +$output .= preg_replace(array('/\s+$/Sm','/\n+/S'),"\n", $rawChunk).$token[1]; +do { +$token = next($tokens); +$output .= $token[1]; +} while ($token[0] !== T_END_HEREDOC); +$rawChunk =''; +} elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) { +$rawChunk .= $token[1]; +} +} +$output .= preg_replace(array('/\s+$/Sm','/\n+/S'),"\n", $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 +{ +abstract class ContainerAware implements ContainerAwareInterface +{ +protected $container; +public function setContainer(ContainerInterface $container = null) +{ +$this->container = $container; +} +} +} +namespace Symfony\Component\HttpKernel\Bundle +{ +use Symfony\Component\DependencyInjection\ContainerAware; +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 extends ContainerAware implements BundleInterface +{ +protected $name; +protected $reflected; +protected $extension; +public function boot() +{ +} +public function shutdown() +{ +} +public function build(ContainerBuilder $container) +{ +} +public function getContainerExtension() +{ +if (null === $this->extension) { +$basename = preg_replace('/Bundle$/','', $this->getName()); +$class = $this->getNamespace().'\\DependencyInjection\\'.$basename.'Extension'; +if (class_exists($class)) { +$extension = new $class(); +$expectedAlias = Container::underscore($basename); +if ($expectedAlias != $extension->getAlias()) { +throw new \LogicException(sprintf('The extension alias for the default extension of a '.'bundle must be the underscored version of the '.'bundle name ("%s" instead of "%s")', +$expectedAlias, $extension->getAlias() +)); +} +$this->extension = $extension; +} else { +$this->extension = false; +} +} +if ($this->extension) { +return $this->extension; +} +} +public function getNamespace() +{ +if (null === $this->reflected) { +$this->reflected = new \ReflectionObject($this); +} +return $this->reflected->getNamespaceName(); +} +public function getPath() +{ +if (null === $this->reflected) { +$this->reflected = new \ReflectionObject($this); +} +return dirname($this->reflected->getFileName()); +} +public function getParent() +{ +return null; +} +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; +} +$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,'/','\\'); +} +$r = new \ReflectionClass($ns.'\\'.$file->getBasename('.php')); +if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract() && !$r->getConstructor()->getNumberOfRequiredParameters()) { +$application->add($r->newInstance()); +} +} +} +} +} +namespace Symfony\Component\Config +{ +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Filesystem\Filesystem; +class ConfigCache +{ +private $debug; +private $file; +public function __construct($file, $debug) +{ +$this->file = $file; +$this->debug = (Boolean) $debug; +} +public function __toString() +{ +return $this->file; +} +public function isFresh() +{ +if (!is_file($this->file)) { +return false; +} +if (!$this->debug) { +return true; +} +$metadata = $this->getMetaFile(); +if (!is_file($metadata)) { +return false; +} +$time = filemtime($this->file); +$meta = unserialize(file_get_contents($metadata)); +foreach ($meta as $resource) { +if (!$resource->isFresh($time)) { +return false; +} +} +return true; +} +public function write($content, array $metadata = null) +{ +$mode = 0666 & ~umask(); +$filesystem = new Filesystem(); +$filesystem->dumpFile($this->file, $content, $mode); +if (null !== $metadata && true === $this->debug) { +$filesystem->dumpFile($this->getMetaFile(), serialize($metadata), $mode); +} +} +private function getMetaFile() +{ +return $this->file.'.meta'; +} +} +} +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\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\Response; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +class HttpKernel implements HttpKernelInterface, TerminableInterface +{ +protected $dispatcher; +protected $resolver; +public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver) +{ +$this->dispatcher = $dispatcher; +$this->resolver = $resolver; +} +public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) +{ +try { +return $this->handleRaw($request, $type); +} catch (\Exception $e) { +if (false === $catch) { +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)); +} +private function handleRaw(Request $request, $type = self::MASTER_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". Maybe you forgot to add the matching route in your routing configuration?', $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); +return $event->getResponse(); +} +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()) { +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; +} +} +} +namespace Symfony\Component\HttpKernel\DependencyInjection +{ +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Scope; +class ContainerAwareHttpKernel extends HttpKernel +{ +protected $container; +public function __construct(EventDispatcherInterface $dispatcher, ContainerInterface $container, ControllerResolverInterface $controllerResolver) +{ +parent::__construct($dispatcher, $controllerResolver); +$this->container = $container; +if (!$container->hasScope('request')) { +$container->addScope(new Scope('request')); +} +} +public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) +{ +$request->headers->set('X-Php-Ob-Level', ob_get_level()); +$this->container->enterScope('request'); +$this->container->set('request', $request,'request'); +try { +$response = parent::handle($request, $type, $catch); +} catch (\Exception $e) { +$this->container->set('request', null,'request'); +$this->container->leaveScope('request'); +throw $e; +} +$this->container->set('request', null,'request'); +$this->container->leaveScope('request'); +return $response; +} +} +} + +namespace { return $loader; } + \ No newline at end of file diff --git a/app/check.php b/app/check.php new file mode 100644 index 0000000..91b826b --- /dev/null +++ b/app/check.php @@ -0,0 +1,62 @@ +getPhpIniConfigPath(); + +echo "********************************\n"; +echo "* *\n"; +echo "* Symfony requirements check *\n"; +echo "* *\n"; +echo "********************************\n\n"; + +echo $iniPath ? sprintf("* Configuration file used by PHP: %s\n\n", $iniPath) : "* WARNING: No configuration file (php.ini) used by PHP!\n\n"; + +echo "** ATTENTION **\n"; +echo "* The PHP CLI can use a different php.ini file\n"; +echo "* than the one used with your web server.\n"; +if ('\\' == DIRECTORY_SEPARATOR) { + echo "* (especially on the Windows platform)\n"; +} +echo "* To be on the safe side, please also launch the requirements check\n"; +echo "* from your web server using the web/config.php script.\n"; + +echo_title('Mandatory requirements'); + +$checkPassed = true; +foreach ($symfonyRequirements->getRequirements() as $req) { + /** @var $req Requirement */ + echo_requirement($req); + if (!$req->isFulfilled()) { + $checkPassed = false; + } +} + +echo_title('Optional recommendations'); + +foreach ($symfonyRequirements->getRecommendations() as $req) { + echo_requirement($req); +} + +exit($checkPassed ? 0 : 1); + +/** + * Prints a Requirement instance + */ +function echo_requirement(Requirement $requirement) +{ + $result = $requirement->isFulfilled() ? 'OK' : ($requirement->isOptional() ? 'WARNING' : 'ERROR'); + echo ' ' . str_pad($result, 9); + echo $requirement->getTestMessage() . "\n"; + + if (!$requirement->isFulfilled()) { + echo sprintf(" %s\n\n", $requirement->getHelpText()); + } +} + +function echo_title($title) +{ + echo "\n** $title **\n\n"; +} diff --git a/app/config/config.yml b/app/config/config.yml new file mode 100644 index 0000000..0e522e7 --- /dev/null +++ b/app/config/config.yml @@ -0,0 +1,75 @@ +imports: + - { resource: parameters.yml } + - { resource: security.yml } + +framework: + #esi: ~ + #translator: { fallback: %locale% } + secret: %secret% + router: + resource: "%kernel.root_dir%/config/routing.yml" + strict_requirements: ~ + form: ~ + csrf_protection: ~ + validation: { enable_annotations: true } + templating: + engines: ['twig'] + #assets_version: SomeVersionScheme + default_locale: "%locale%" + trusted_proxies: ~ + session: ~ + fragments: ~ + +# Twig Configuration +twig: + debug: %kernel.debug% + strict_variables: %kernel.debug% +services: + custom.twig.extension.debug: + class: Twig_Extension_Debug + tags: + - { name: 'twig.extension' } +# Assetic Configuration +assetic: + debug: %kernel.debug% + use_controller: false + bundles: [ ] + #java: /usr/bin/java + filters: + cssrewrite: ~ + uglifyjs2: + bin: /usr/bin/uglifyjs + apply_to: "\.js$" + uglifycss: + bin: /usr/bin/uglifycss + apply_to: "\.css$" + #closure: + # jar: %kernel.root_dir%/Resources/java/compiler.jar + #yui_css: + # jar: %kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar + +# Doctrine Configuration +doctrine: + dbal: + driver: %database_driver% + host: %database_host% + port: %database_port% + dbname: %database_name% + user: %database_user% + password: %database_password% + charset: UTF8 + # if using pdo_sqlite as your database driver, add the path in parameters.yml + # e.g. database_path: %kernel.root_dir%/data/data.db3 + # path: %database_path% + + orm: + auto_generate_proxy_classes: %kernel.debug% + auto_mapping: true + +# Swiftmailer Configuration +swiftmailer: + transport: %mailer_transport% + host: %mailer_host% + username: %mailer_user% + password: %mailer_password% + spool: { type: memory } diff --git a/app/config/config_dev.yml b/app/config/config_dev.yml new file mode 100644 index 0000000..4bb7c8b --- /dev/null +++ b/app/config/config_dev.yml @@ -0,0 +1,31 @@ +imports: + - { resource: config.yml } + +framework: + router: + resource: "%kernel.root_dir%/config/routing_dev.yml" + strict_requirements: true + profiler: { only_exceptions: false } + +web_profiler: + toolbar: true + intercept_redirects: false + +monolog: + handlers: + main: + type: stream + path: %kernel.logs_dir%/%kernel.environment%.log + level: debug + firephp: + type: firephp + level: info + chromephp: + type: chromephp + level: info + +assetic: + use_controller: true + +#swiftmailer: +# delivery_address: me@example.com diff --git a/app/config/config_prod.yml b/app/config/config_prod.yml new file mode 100644 index 0000000..e125e75 --- /dev/null +++ b/app/config/config_prod.yml @@ -0,0 +1,23 @@ +imports: + - { resource: config.yml } + +#framework: +# validation: +# cache: apc + +#doctrine: +# orm: +# metadata_cache_driver: apc +# result_cache_driver: apc +# query_cache_driver: apc + +monolog: + handlers: + main: + type: fingers_crossed + action_level: error + handler: nested + nested: + type: stream + path: %kernel.logs_dir%/%kernel.environment%.log + level: debug diff --git a/app/config/config_test.yml b/app/config/config_test.yml new file mode 100644 index 0000000..a27c240 --- /dev/null +++ b/app/config/config_test.yml @@ -0,0 +1,16 @@ +imports: + - { resource: config_dev.yml } + +framework: + test: ~ + session: + storage_id: session.storage.mock_file + profiler: + enabled: false + +web_profiler: + toolbar: false + intercept_redirects: false + +swiftmailer: + disable_delivery: true diff --git a/app/config/parameters.yml b/app/config/parameters.yml new file mode 100644 index 0000000..b51cc33 --- /dev/null +++ b/app/config/parameters.yml @@ -0,0 +1,28 @@ +# This file is auto-generated during the composer install +#parameters: +# database_driver: pdo_mysql +# database_host: 127.0.0.1 +# database_port: null +# database_name: symfony +# database_user: root +# database_password: null +# mailer_transport: smtp +# mailer_host: 127.0.0.1 +# mailer_user: null +# mailer_password: null +# locale: en +# secret: ThisTokenIsNotSoSecretChangeIt + +parameters: + database_driver: pdo_mysql + database_host: 127.0.0.1 + database_port: null + database_name: symfony + database_user: root + database_password: null + mailer_transport: gmail + mailer_host: ~ + mailer_user: graphicsxp@gmail.com + mailer_password: Samuel44 + locale: fr + secret: ThisTokenIsNotSoSecretChangeIt diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist new file mode 100644 index 0000000..8b317c2 --- /dev/null +++ b/app/config/parameters.yml.dist @@ -0,0 +1,15 @@ +parameters: + database_driver: pdo_mysql + database_host: 127.0.0.1 + database_port: ~ + database_name: symfony + database_user: root + database_password: ~ + + mailer_transport: smtp + mailer_host: 127.0.0.1 + mailer_user: ~ + mailer_password: ~ + + locale: en + secret: ThisTokenIsNotSoSecretChangeIt diff --git a/app/config/routing.yml b/app/config/routing.yml new file mode 100644 index 0000000..b421678 --- /dev/null +++ b/app/config/routing.yml @@ -0,0 +1,4 @@ +photograph_photo: + resource: "@PhotographPhotoBundle/Resources/config/routing.yml" + prefix: / + diff --git a/app/config/routing_dev.yml b/app/config/routing_dev.yml new file mode 100644 index 0000000..5c22cca --- /dev/null +++ b/app/config/routing_dev.yml @@ -0,0 +1,19 @@ +_wdt: + resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" + prefix: /_wdt + +_profiler: + resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" + prefix: /_profiler + +_configurator: + resource: "@SensioDistributionBundle/Resources/config/routing/webconfigurator.xml" + prefix: /_configurator + +_main: + resource: routing.yml + +photograph_photo: + resource: "@PhotographPhotoBundle/Resources/config/routing.yml" + prefix: / + diff --git a/app/config/security.yml b/app/config/security.yml new file mode 100644 index 0000000..243aa71 --- /dev/null +++ b/app/config/security.yml @@ -0,0 +1,39 @@ +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + role_hierarchy: + ROLE_ADMIN: ROLE_USER + ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] + + providers: + in_memory: + memory: + users: + user: { password: userpass, roles: [ 'ROLE_USER' ] } + admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] } + + firewalls: + dev: + pattern: ^/(_(profiler|wdt)|css|images|js)/ + security: false + + login: + pattern: ^/demo/secured/login$ + security: false + + secured_area: + pattern: ^/demo/secured/ + form_login: + check_path: _security_check + login_path: _demo_login + logout: + path: _demo_logout + target: _demo + #anonymous: ~ + #http_basic: + # realm: "Secured Demo Area" + + access_control: + - { path: ^/demo/secured/hello/admin/, roles: ROLE_ADMIN } + #- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https } diff --git a/app/console b/app/console new file mode 100644 index 0000000..fa6a36e --- /dev/null +++ b/app/console @@ -0,0 +1,27 @@ +#!/usr/bin/env php +getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev'); +$debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(array('--no-debug', '')) && $env !== 'prod'; + +if ($debug) { + Debug::enable(); +} + +$kernel = new AppKernel($env, $debug); +$application = new Application($kernel); +$application->run($input); diff --git a/app/phpunit.xml.dist b/app/phpunit.xml.dist new file mode 100644 index 0000000..1e31086 --- /dev/null +++ b/app/phpunit.xml.dist @@ -0,0 +1,41 @@ + + + + + + + + ../src/*/*Bundle/Tests + ../src/*/Bundle/*Bundle/Tests + + + + + + + + ../src + + ../src/*/*Bundle/Resources + ../src/*/*Bundle/Tests + ../src/*/Bundle/*Bundle/Resources + ../src/*/Bundle/*Bundle/Tests + + + + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a2cbbde --- /dev/null +++ b/composer.json @@ -0,0 +1,54 @@ +{ + "name": "symfony/framework-standard-edition", + "license": "MIT", + "type": "project", + "description": "The \"Symfony Standard Edition\" distribution", + "autoload": { + "psr-0": { "": "src/" } + }, + "require": { + "php": ">=5.3.3", + "symfony/symfony": "2.3.*", + "doctrine/orm": ">=2.2.3,<2.4-dev", + "doctrine/doctrine-bundle": "1.2.*", + "twig/extensions": "1.0.*", + "symfony/assetic-bundle": "2.3.*", + "symfony/swiftmailer-bundle": "2.3.*", + "symfony/monolog-bundle": "2.3.*", + "symfony/finder": "2.3.*", + "sensio/distribution-bundle": "2.3.*", + "sensio/framework-extra-bundle": "2.3.*", + "sensio/generator-bundle": "2.3.*", + "incenteev/composer-parameter-handler": "~2.0" + }, + "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" + ], + "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" + ] + }, + "config": { + "bin-dir": "bin" + }, + "minimum-stability": "stable", + "extra": { + "symfony-app-dir": "app", + "symfony-web-dir": "web", + "incenteev-parameters": { + "file": "app/config/parameters.yml" + }, + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..d332a60 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1186 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "e67df720e8a1a2e0e6df41d91d788e48", + "packages": [ + { + "name": "doctrine/common", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common", + "reference": "2.3.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/doctrine/common/zipball/2.3.0", + "reference": "2.3.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ], + "time": "2012-09-19 22:55:18" + }, + { + "name": "doctrine/dbal", + "version": "2.3.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "2.3.4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/2.3.4", + "reference": "2.3.4", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.3.0,<2.5-dev", + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\DBAL": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "persistence", + "queryobject" + ], + "time": "2013-05-11 07:45:37" + }, + { + "name": "doctrine/doctrine-bundle", + "version": "v1.2.0", + "target-dir": "Doctrine/Bundle/DoctrineBundle", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineBundle.git", + "reference": "v1.2.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/v1.2.0", + "reference": "v1.2.0", + "shasum": "" + }, + "require": { + "doctrine/dbal": ">=2.2,<2.5-dev", + "jdorn/sql-formatter": ">=1.1,<2.0", + "php": ">=5.3.2", + "symfony/doctrine-bridge": ">=2.2,<3.0", + "symfony/framework-bundle": ">=2.2,<3.0" + }, + "require-dev": { + "doctrine/orm": ">=2.2,<2.5-dev", + "symfony/validator": ">=2.2,<3.0", + "symfony/yaml": ">=2.2,<3.0" + }, + "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.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Bundle\\DoctrineBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + } + ], + "description": "Symfony DoctrineBundle", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "orm", + "persistence" + ], + "time": "2013-03-25 20:13:59" + }, + { + "name": "doctrine/orm", + "version": "2.3.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/doctrine2.git", + "reference": "2.3.4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/2.3.4", + "reference": "2.3.4", + "shasum": "" + }, + "require": { + "doctrine/dbal": "2.3.*", + "ext-pdo": "*", + "php": ">=5.3.2", + "symfony/console": "2.*" + }, + "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.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\ORM": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "orm" + ], + "time": "2013-05-11 07:51:12" + }, + { + "name": "incenteev/composer-parameter-handler", + "version": "v2.0.0", + "target-dir": "Incenteev/ParameterHandler", + "source": { + "type": "git", + "url": "https://github.com/Incenteev/ParameterHandler.git", + "reference": "v2.0.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/v2.0.0", + "reference": "v2.0.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/yaml": ">=2.0,<3.0" + }, + "require-dev": { + "composer/composer": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "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": "2013-04-06 17:15:44" + }, + { + "name": "jdorn/sql-formatter", + "version": "v1.2.9", + "source": { + "type": "git", + "url": "https://github.com/jdorn/sql-formatter.git", + "reference": "v1.2.9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/v1.2.9", + "reference": "v1.2.9", + "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": "2013-04-26 18:42:52" + }, + { + "name": "kriswallsmith/assetic", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/kriswallsmith/assetic.git", + "reference": "v1.1.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/v1.1.1", + "reference": "v1.1.1", + "shasum": "" + }, + "require": { + "php": ">=5.3.1", + "symfony/process": ">=2.1,<3.0" + }, + "require-dev": { + "cssmin/cssmin": "*", + "joliclic/javascript-packer": "*", + "kamicane/packager": "*", + "leafo/lessphp": "*", + "leafo/scssphp": "*", + "leafo/scssphp-compass": "*", + "mrclay/minify": "*", + "phpunit/phpunit": ">=3.7,<4.0", + "ptachoire/cssembed": "*", + "twig/twig": ">=1.6,<2.0" + }, + "suggest": { + "leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler", + "leafo/scssphp": "Assetic provides the integration with the scssphp SCSS compiler", + "leafo/scssphp-compass": "Assetic provides the integration with the SCSS compass plugin", + "ptachoire/cssembed": "Assetic provides the integration with phpcssembed to embed data uris", + "twig/twig": "Assetic provides the integration with the Twig templating engine" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-0": { + "Assetic": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Asset Management for PHP", + "homepage": "https://github.com/kriswallsmith/assetic", + "keywords": [ + "assets", + "compression", + "minification" + ], + "time": "2013-06-01 22:13:43" + }, + { + "name": "monolog/monolog", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "1.5.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1.5.0", + "reference": "1.5.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": ">=1.0,<2.0" + }, + "require-dev": { + "doctrine/couchdb": "dev-master", + "mlehner/gelf-php": "1.0.*", + "raven/raven": "0.3.*" + }, + "suggest": { + "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", + "mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server", + "raven/raven": "Allow sending log messages to a Sentry server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Monolog": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be", + "role": "Developer" + } + ], + "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": "2013-04-23 10:09:48" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log", + "reference": "1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/php-fig/log/archive/1.0.0.zip", + "reference": "1.0.0", + "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": "v2.3.1", + "target-dir": "Sensio/Bundle/DistributionBundle", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioDistributionBundle.git", + "reference": "v2.3.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/v2.3.1", + "reference": "v2.3.1", + "shasum": "" + }, + "require": { + "symfony/framework-bundle": ">=2.2,<3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Sensio\\Bundle\\DistributionBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "The base bundle for the Symfony Distributions", + "keywords": [ + "configuration", + "distribution" + ], + "time": "2013-05-30 16:15:25" + }, + { + "name": "sensio/framework-extra-bundle", + "version": "v2.3.1", + "target-dir": "Sensio/Bundle/FrameworkExtraBundle", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", + "reference": "v2.3.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/v2.3.1", + "reference": "v2.3.1", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.2,<3.0", + "symfony/framework-bundle": ">=2.2,<3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "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": "2013-06-02 16:13:20" + }, + { + "name": "sensio/generator-bundle", + "version": "v2.3.1", + "target-dir": "Sensio/Bundle/GeneratorBundle", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioGeneratorBundle.git", + "reference": "v2.3.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioGeneratorBundle/zipball/v2.3.1", + "reference": "v2.3.1", + "shasum": "" + }, + "require": { + "symfony/console": ">=2.0,<3.0", + "symfony/framework-bundle": ">=2.2,<3.0" + }, + "require-dev": { + "doctrine/orm": ">=2.2,<3.0,>=2.2.3", + "symfony/doctrine-bridge": ">=2.2,<3.0", + "twig/twig": ">=1.11.0,<2.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Sensio\\Bundle\\GeneratorBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "This bundle generates code for you", + "time": "2013-06-05 17:32:22" + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v5.0.0", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "v5.0.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/v5.0.0", + "reference": "v5.0.0", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Chris Corbyn" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "http://swiftmailer.org", + "keywords": [ + "mail", + "mailer" + ], + "time": "2013-04-30 17:35:30" + }, + { + "name": "symfony/assetic-bundle", + "version": "v2.3.0", + "target-dir": "Symfony/Bundle/AsseticBundle", + "source": { + "type": "git", + "url": "https://github.com/symfony/AsseticBundle.git", + "reference": "v2.3.0-beta1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/AsseticBundle/zipball/v2.3.0-beta1", + "reference": "v2.3.0-beta1", + "shasum": "" + }, + "require": { + "kriswallsmith/assetic": ">=1.1-beta1,<1.2", + "php": ">=5.3.0", + "symfony/framework-bundle": ">=2.1,<3.0" + }, + "require-dev": { + "symfony/class-loader": ">=2.1,<3.0", + "symfony/console": ">=2.1,<3.0", + "symfony/css-selector": ">=2.1,<3.0", + "symfony/dom-crawler": ">=2.1,<3.0", + "symfony/form": ">=2.1,<3.0", + "symfony/twig-bundle": ">=2.1,<3.0", + "symfony/yaml": ">=2.1,<3.0" + }, + "suggest": { + "symfony/twig-bundle": "~2.1" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Bundle\\AsseticBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Integrates Assetic into Symfony2", + "homepage": "https://github.com/symfony/AsseticBundle", + "keywords": [ + "assets", + "compression", + "minification" + ], + "time": "2013-05-13 15:39:47" + }, + { + "name": "symfony/icu", + "version": "v1.2.0", + "target-dir": "Symfony/Component/Icu", + "source": { + "type": "git", + "url": "https://github.com/symfony/Icu.git", + "reference": "v1.2.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Icu/zipball/v1.2.0", + "reference": "v1.2.0", + "shasum": "" + }, + "require": { + "lib-icu": ">=4.4", + "php": ">=5.3.3", + "symfony/intl": ">=2.3,<3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Symfony\\Component\\Icu\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Contains an excerpt of the ICU data and classes to load it.", + "homepage": "http://symfony.com", + "keywords": [ + "icu", + "intl" + ], + "time": "2013-06-03 18:32:58" + }, + { + "name": "symfony/monolog-bundle", + "version": "v2.3.0", + "target-dir": "Symfony/Bundle/MonologBundle", + "source": { + "type": "git", + "url": "https://github.com/symfony/MonologBundle.git", + "reference": "v2.3.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/MonologBundle/zipball/v2.3.0", + "reference": "v2.3.0", + "shasum": "" + }, + "require": { + "monolog/monolog": ">=1.3,<2.0", + "php": ">=5.3.2", + "symfony/config": ">=2.2-beta2,<3.0", + "symfony/dependency-injection": ">=2.2-beta2,<3.0", + "symfony/monolog-bridge": ">=2.2-beta2,<3.0" + }, + "require-dev": { + "symfony/yaml": ">=2.2-beta2,<3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Bundle\\MonologBundle": "" + } + }, + "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 MonologBundle", + "homepage": "http://symfony.com", + "keywords": [ + "log", + "logging" + ], + "time": "2013-05-27 18:06:55" + }, + { + "name": "symfony/swiftmailer-bundle", + "version": "v2.3.1", + "target-dir": "Symfony/Bundle/SwiftmailerBundle", + "source": { + "type": "git", + "url": "https://github.com/symfony/SwiftmailerBundle.git", + "reference": "v2.3.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/SwiftmailerBundle/zipball/v2.3.1", + "reference": "v2.3.1", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "swiftmailer/swiftmailer": ">=4.2.0,<5.1-dev", + "symfony/swiftmailer-bridge": ">=2.1,<3.0" + }, + "require-dev": { + "symfony/config": ">=2.1,<3.0", + "symfony/dependency-injection": ">=2.1,<3.0", + "symfony/http-kernel": ">=2.1,<3.0", + "symfony/yaml": ">=2.1,<3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Bundle\\SwiftmailerBundle": "" + } + }, + "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 SwiftmailerBundle", + "homepage": "http://symfony.com", + "time": "2013-05-15 08:38:58" + }, + { + "name": "symfony/symfony", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/symfony.git", + "reference": "v2.3.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/symfony/zipball/v2.3.1", + "reference": "v2.3.1", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.2,<3.0", + "php": ">=5.3.3", + "psr/log": ">=1.0,<2.0", + "symfony/icu": ">=1.0,<2.0", + "twig/twig": ">=1.11.0,<2.0" + }, + "replace": { + "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/dependency-injection": "self.version", + "symfony/doctrine-bridge": "self.version", + "symfony/dom-crawler": "self.version", + "symfony/event-dispatcher": "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/locale": "self.version", + "symfony/monolog-bridge": "self.version", + "symfony/options-resolver": "self.version", + "symfony/process": "self.version", + "symfony/propel1-bridge": "self.version", + "symfony/property-access": "self.version", + "symfony/proxy-manager-bridge": "self.version", + "symfony/routing": "self.version", + "symfony/security": "self.version", + "symfony/security-bundle": "self.version", + "symfony/serializer": "self.version", + "symfony/stopwatch": "self.version", + "symfony/swiftmailer-bridge": "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/web-profiler-bundle": "self.version", + "symfony/yaml": "self.version" + }, + "require-dev": { + "doctrine/data-fixtures": "1.0.*", + "doctrine/dbal": ">=2.2,<3.0", + "doctrine/orm": ">=2.2,<3.0,>=2.2.3", + "ircmaxell/password-compat": "1.0.*", + "monolog/monolog": ">=1.3,<2.0", + "ocramius/proxy-manager": ">=0.3.1,<0.4-dev", + "propel/propel1": "1.6.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\": "src/" + }, + "classmap": [ + "src/Symfony/Component/HttpFoundation/Resources/stubs", + "src/Symfony/Component/Intl/Resources/stubs" + ], + "files": [ + "src/Symfony/Component/Intl/Resources/stubs/functions.php" + ] + }, + "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": "The Symfony PHP framework", + "homepage": "http://symfony.com", + "keywords": [ + "framework" + ], + "time": "2013-06-11 11:46:38" + }, + { + "name": "twig/extensions", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Twig-extensions.git", + "reference": "v1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Twig-extensions/zipball/v1.0.0", + "reference": "v1.0.0", + "shasum": "" + }, + "require": { + "twig/twig": "1.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_Extensions_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Common additional features for Twig that do not directly belong in core", + "homepage": "https://github.com/fabpot/Twig-extensions", + "keywords": [ + "debug", + "i18n", + "text" + ], + "time": "2013-02-28 14:21:30" + }, + { + "name": "twig/twig", + "version": "v1.13.1", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Twig.git", + "reference": "v1.13.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.13.1", + "reference": "v1.13.1", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.13-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ], + "time": "2013-06-06 06:06:01" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "stable", + "stability-flags": [ + + ], + "platform": { + "php": ">=5.3.3" + }, + "platform-dev": [ + + ] +} diff --git a/nbproject/configs/remote.properties b/nbproject/configs/remote.properties new file mode 100644 index 0000000..e69de29 diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..3d59f54 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,15 @@ +ignore.path= +include.path=${php.global.include.path} +php.version=PHP_54 +phpunit.bootstrap= +phpunit.bootstrap.create.tests=false +phpunit.configuration= +phpunit.run.test.files=false +phpunit.script= +phpunit.suite= +phpunit.test.groups.ask=false +source.encoding=UTF-8 +src.dir=. +tags.asp=false +tags.short=true +web.root=web diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..8e28810 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,9 @@ + + + org.netbeans.modules.php.project + + + ePhotograph + + + diff --git a/src/.htaccess b/src/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/src/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/src/Blogger/BlogBundle/Resources/config/routing.yml b/src/Blogger/BlogBundle/Resources/config/routing.yml deleted file mode 100644 index bda195c..0000000 --- a/src/Blogger/BlogBundle/Resources/config/routing.yml +++ /dev/null @@ -1,6 +0,0 @@ -# src/Blogger/BlogBundle/Resources/config/routing.yml -BloggerBlogBundle_homepage: - pattern: / - defaults: { _controller: BloggerBlogBundle:Page:index } - requirements: - _method: GET \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Controller/ContactController.php b/src/Photograph/PhotoBundle/Controller/ContactController.php new file mode 100644 index 0000000..f12fbd4 --- /dev/null +++ b/src/Photograph/PhotoBundle/Controller/ContactController.php @@ -0,0 +1,30 @@ + 'Type your message here'); + $form = $this->createFormBuilder($defaultData) + ->add('nom', 'text', array('required' => true)) + ->add('email', 'email') + ->add('sujet', 'text') + ->add('message', 'textarea') + ->getForm(); + + $form->handleRequest($request); + + if ($form->isValid()) { + // data is an array with "name", "email", and "message" keys + $data = $form->getData(); + } + + return $this->render('PhotographPhotoBundle:Contact:index.html.twig', array('form' => $form->createView())); + } +} + +?> \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Controller/DefaultController.php b/src/Photograph/PhotoBundle/Controller/DefaultController.php new file mode 100644 index 0000000..8fabe61 --- /dev/null +++ b/src/Photograph/PhotoBundle/Controller/DefaultController.php @@ -0,0 +1,13 @@ +render('PhotographPhotoBundle:Default:index.html.twig'); + } +} diff --git a/src/Photograph/PhotoBundle/Controller/PageController.php b/src/Photograph/PhotoBundle/Controller/PageController.php new file mode 100644 index 0000000..6626ef6 --- /dev/null +++ b/src/Photograph/PhotoBundle/Controller/PageController.php @@ -0,0 +1,67 @@ +render('PhotographPhotoBundle:Page:contact.html.twig'); + } + + public function portfolioAction($name) { + switch ($name) { + case 'angelique-et-pierre': + return $this->render('PhotographPhotoBundle:Page:portfolio-post-angelique-et-pierre.html.twig'); + break; + case 'maud-et-fabrice': + return $this->render('PhotographPhotoBundle:Page:portfolio-post-maud-et-fabrice.html.twig'); + break; + case 'paysages': + return $this->render('PhotographPhotoBundle:Page:portfolio-post-paysages.html.twig'); + break; + case 'portraits': + return $this->render('PhotographPhotoBundle:Page:portfolio-post-portraits.html.twig'); + break; + case 'architecture': + return $this->render('PhotographPhotoBundle:Page:portfolio-post-architecture.html.twig'); + break; + default : + return $this->render('PhotographPhotoBundle:Page:portfolio.html.twig'); + break; + } + return $this->render('PhotographPhotoBundle:Page:portfolio.html.twig'); + } + + public function aboutAction() { + return $this->render('PhotographPhotoBundle:Page:about.html.twig'); + } + + public function faqAction() { + return $this->render('PhotographPhotoBundle:Page:faq.html.twig'); + } + + public function sitemapAction() { + return $this->render('PhotographPhotoBundle:Page:sitemap.html.twig'); + } + + public function prestationsAction() { + return $this->render('PhotographPhotoBundle:Page:prestations.html.twig'); + } + + public function galleryAction($name) { + $finder = new Finder(); + $path = 'img/gallery/' . str_replace('-', '_', $name); + $array = array(); + + $finder->files()->in('/var/www/ePhotograph/web/' . $path)->sortByName()->depth('== 0'); + foreach ($finder as $file){ + array_push($array, array('src' => $path . '/thumbs/' . $file->getFilename(), 'href' => $path . '/' . $file->getFilename())); + } + + return $this->render('PhotographPhotoBundle:Page:gallery.html.twig', array('divs' => $array)); + } +} diff --git a/src/Photograph/PhotoBundle/DependencyInjection/Configuration.php b/src/Photograph/PhotoBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..5578100 --- /dev/null +++ b/src/Photograph/PhotoBundle/DependencyInjection/Configuration.php @@ -0,0 +1,29 @@ +root('photograph_photo'); + + // 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; + } +} diff --git a/src/Photograph/PhotoBundle/DependencyInjection/PhotographPhotoExtension.php b/src/Photograph/PhotoBundle/DependencyInjection/PhotographPhotoExtension.php new file mode 100644 index 0000000..bdf5a51 --- /dev/null +++ b/src/Photograph/PhotoBundle/DependencyInjection/PhotographPhotoExtension.php @@ -0,0 +1,28 @@ +processConfiguration($configuration, $configs); + + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.yml'); + } +} diff --git a/src/Photograph/PhotoBundle/PhotographPhotoBundle.php b/src/Photograph/PhotoBundle/PhotographPhotoBundle.php new file mode 100644 index 0000000..1145a78 --- /dev/null +++ b/src/Photograph/PhotoBundle/PhotographPhotoBundle.php @@ -0,0 +1,9 @@ + +
+
+
+

Contact

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

N'hésitez pas à prendre contact avec moi directement par téléphone ou en remplissant le formulaire ci-dessous. Je répondrais à vos questions dans les 24H.

+
+
+ +
+
+

Téléphone fixe: +352 27996506

+

Téléphone portable: +352 691542795

+

Email: samuelberthelot@gmail.com

+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+
+
+ + +
+{% endblock %} \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Default/index.html.twig b/src/Photograph/PhotoBundle/Resources/views/Default/index.html.twig new file mode 100644 index 0000000..2d41a20 --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Default/index.html.twig @@ -0,0 +1,54 @@ +{% extends '::base.html.twig' %} + +{% block body %} +
+
+

+

Samuel Berthelot

+ Photographe
+ Luxembourg - Metz
Strasbourg - Arlon
+

+

+ +352 69 1542795 +

+

+ +352 2799 6506 +

+

+ graphicsxp@gmail.com +

+
+
+
+{% endblock %} + +{% block javascripts %} + + +{% endblock %} \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Form/fields.html.twig b/src/Photograph/PhotoBundle/Resources/views/Form/fields.html.twig new file mode 100644 index 0000000..5fd8dda --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Form/fields.html.twig @@ -0,0 +1,23 @@ +{# src/Photograph/PhotoBundle/Resources/views/Form/fields.html.twig #} + +{% block field_row %} +{% spaceless %} + {{ form_errors(form) }} + {{ form_widget(form) }} +{% endspaceless %} +{% endblock field_row %} + +{% block email_widget %} + +{% endblock %} + +{% block text_widget %} + {{dump(block('attributes'))}} + +{% endblock %} + +{% block textarea_widget %} + +{% endblock %} + + \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Page/about.html.twig b/src/Photograph/PhotoBundle/Resources/views/Page/about.html.twig new file mode 100644 index 0000000..ad95847 --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Page/about.html.twig @@ -0,0 +1 @@ +{% extends '::base.html.twig' %} diff --git a/src/Photograph/PhotoBundle/Resources/views/Page/faq.html.twig b/src/Photograph/PhotoBundle/Resources/views/Page/faq.html.twig new file mode 100644 index 0000000..bcf957e --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Page/faq.html.twig @@ -0,0 +1,324 @@ +{% extends '::base.html.twig' %} + +{% block body %} +
+ +
+
+
+

FAQ

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

Category 1

+
+
+ Consequat viverra ipsum, sed placerat ligula aliquam eu? + +
+
+
+ Fusce consequat, nunc sit amet lobortis tempor, est lorem suscipit urna, egestas volutpat diam dolor tincidunt odio. + Integer pellentesque dictum lorem, id porttitor odio malesuada eu. Donec vel placerat diam. Ut lorem nisi, condimentum sit amet aliquam ac, + rutrum non eros. In ultrices, magna sed ndum pellentesque, neque libero porttitor justo, vitae consequat dolor nibh vel ipsum. + Praesent gravida posuere velit, et ornare velit rhoncus at curabitur eget magna libero, condimentum id dapibus sit amet, tincidunt id sem. + Phasellus nec libero sed lectus ullamcorper porttitor ut tempus leo. Nulla dignissim vestibulum felis sed lacinia. + In pretium felis eu nulla commodo eleifend. Cum sociis natoque penatibus et magnis dis parturient montes. +
+
+
+ Curabitur suscipit hendrerit egets porttitor. Aliquam nusl icien ullamcorper erat eget tempor dolorem pellentesque? + +
+
+
+ Fusce consequat, nunc sit amet lobortis tempor, est lorem suscipit urna, egestas volutpat diam dolor tincidunt odio. + Integer pellentesque dictum lorem, id porttitor odio malesuada eu. Donec vel placerat diam. Ut lorem nisi, condimentum sit amet aliquam ac, + rutrum non eros. In ultrices, magna sed ndum pellentesque, neque libero porttitor justo, vitae consequat dolor nibh vel ipsum. + Praesent gravida posuere velit, et ornare velit rhoncus at curabitur eget magna libero, condimentum id dapibus sit amet, tincidunt id sem. + Phasellus nec libero sed lectus ullamcorper porttitor ut tempus leo. Nulla dignissim vestibulum felis sed lacinia. + In pretium felis eu nulla commodo eleifend. Cum sociis natoque penatibus et magnis dis parturient montes. +
+
+
+ posuere cubilia curae nulla vestibulum eget ? + +
+
+
+ Fusce consequat, nunc sit amet lobortis tempor, est lorem suscipit urna, egestas volutpat diam dolor tincidunt odio. + Integer pellentesque dictum lorem, id porttitor odio malesuada eu. Donec vel placerat diam. Ut lorem nisi, condimentum sit amet aliquam ac, + rutrum non eros. In ultrices, magna sed ndum pellentesque, neque libero porttitor justo, vitae consequat dolor nibh vel ipsum. + Praesent gravida posuere velit, et ornare velit rhoncus at curabitur eget magna libero, condimentum id dapibus sit amet, tincidunt id sem. + Phasellus nec libero sed lectus ullamcorper porttitor ut tempus leo. Nulla dignissim vestibulum felis sed lacinia. + In pretium felis eu nulla commodo eleifend. Cum sociis natoque penatibus et magnis dis parturient montes. +
+
+
+ egets sed facilisis magna eros, id dictum arcu. Curabitur suscipit hendrerit? + +
+
+
+ Fusce consequat, nunc sit amet lobortis tempor, est lorem suscipit urna, egestas volutpat diam dolor tincidunt odio. + Integer pellentesque dictum lorem, id porttitor odio malesuada eu. Donec vel placerat diam. Ut lorem nisi, condimentum sit amet aliquam ac, + rutrum non eros. In ultrices, magna sed ndum pellentesque, neque libero porttitor justo, vitae consequat dolor nibh vel ipsum. + Praesent gravida posuere velit, et ornare velit rhoncus at curabitur eget magna libero, condimentum id dapibus sit amet, tincidunt id sem. + Phasellus nec libero sed lectus ullamcorper porttitor ut tempus leo. Nulla dignissim vestibulum felis sed lacinia. + In pretium felis eu nulla commodo eleifend. Cum sociis natoque penatibus et magnis dis parturient montes. +
+
+
+
+
+ +
+
+

Category 2

+
+
+ Consequat viverra ipsum, sed placerat ligula aliquam eu? + +
+
+
+ Fusce consequat, nunc sit amet lobortis tempor, est lorem suscipit urna, egestas volutpat diam dolor tincidunt odio. + Integer pellentesque dictum lorem, id porttitor odio malesuada eu. Donec vel placerat diam. Ut lorem nisi, condimentum sit amet aliquam ac, + rutrum non eros. In ultrices, magna sed ndum pellentesque, neque libero porttitor justo, vitae consequat dolor nibh vel ipsum. + Praesent gravida posuere velit, et ornare velit rhoncus at curabitur eget magna libero, condimentum id dapibus sit amet, tincidunt id sem. + Phasellus nec libero sed lectus ullamcorper porttitor ut tempus leo. Nulla dignissim vestibulum felis sed lacinia. + In pretium felis eu nulla commodo eleifend. Cum sociis natoque penatibus et magnis dis parturient montes. +
+
+
+ Curabitur suscipit hendrerit egets porttitor. Aliquam nusl icien ullamcorper erat eget tempor dolorem pellentesque? + +
+
+
+ Fusce consequat, nunc sit amet lobortis tempor, est lorem suscipit urna, egestas volutpat diam dolor tincidunt odio. + Integer pellentesque dictum lorem, id porttitor odio malesuada eu. Donec vel placerat diam. Ut lorem nisi, condimentum sit amet aliquam ac, + rutrum non eros. In ultrices, magna sed ndum pellentesque, neque libero porttitor justo, vitae consequat dolor nibh vel ipsum. + Praesent gravida posuere velit, et ornare velit rhoncus at curabitur eget magna libero, condimentum id dapibus sit amet, tincidunt id sem. + Phasellus nec libero sed lectus ullamcorper porttitor ut tempus leo. Nulla dignissim vestibulum felis sed lacinia. + In pretium felis eu nulla commodo eleifend. Cum sociis natoque penatibus et magnis dis parturient montes. +
+
+
+ posuere cubilia curae nulla vestibulum eget ? + +
+
+
+ Fusce consequat, nunc sit amet lobortis tempor, est lorem suscipit urna, egestas volutpat diam dolor tincidunt odio. + Integer pellentesque dictum lorem, id porttitor odio malesuada eu. Donec vel placerat diam. Ut lorem nisi, condimentum sit amet aliquam ac, + rutrum non eros. In ultrices, magna sed ndum pellentesque, neque libero porttitor justo, vitae consequat dolor nibh vel ipsum. + Praesent gravida posuere velit, et ornare velit rhoncus at curabitur eget magna libero, condimentum id dapibus sit amet, tincidunt id sem. + Phasellus nec libero sed lectus ullamcorper porttitor ut tempus leo. Nulla dignissim vestibulum felis sed lacinia. + In pretium felis eu nulla commodo eleifend. Cum sociis natoque penatibus et magnis dis parturient montes. +
+
+
+ egets sed facilisis magna eros, id dictum arcu. Curabitur suscipit hendrerit? + +
+
+
+ Fusce consequat, nunc sit amet lobortis tempor, est lorem suscipit urna, egestas volutpat diam dolor tincidunt odio. + Integer pellentesque dictum lorem, id porttitor odio malesuada eu. Donec vel placerat diam. Ut lorem nisi, condimentum sit amet aliquam ac, + rutrum non eros. In ultrices, magna sed ndum pellentesque, neque libero porttitor justo, vitae consequat dolor nibh vel ipsum. + Praesent gravida posuere velit, et ornare velit rhoncus at curabitur eget magna libero, condimentum id dapibus sit amet, tincidunt id sem. + Phasellus nec libero sed lectus ullamcorper porttitor ut tempus leo. Nulla dignissim vestibulum felis sed lacinia. + In pretium felis eu nulla commodo eleifend. Cum sociis natoque penatibus et magnis dis parturient montes. +
+
+
+
+
+ +
+
+

Featured Works

+ +
+
+ +
+
+

Justo lacus, molestie ut porta id pharetra vestibulum proin sit amet aliquet massa phasellus idtiquet mi mauris eget ipsum ut nisi rhoncus sollicitudin + fusce accumsan libero a tellus feugiat fermentum mauris eu neque eu mi imperdiet. In hac habitasse platea dictumst. Ut in mauris libero. In vulputate rutrum + bibendum. Vivamus vitae mi id odio aliquet porta semper faucibus mi. In fermentum augue.

+ diam euismod aliquet tincidunt quam consequat pellentesque vel risus mauris praesent lorem. tellus, cursus eget rhoncus quis, eleifend nec eros mauris gravida + lectus eget dolor venenatis. Lorem ipsums dolor sit amet, consectetur adipiscing elit. Nam nec lectus mauris. Suspendisse et elit ut lacus convallis dapibus. + Nunc consequat, turpis ac iaculis porttitor, sem dui molestie mauris, eu ultrices urna dolor imperdiet sagittis in quis enim cras varius sed malesuada donem + pretium, nulla eu aliquet scelerisque, metus lectus varius diam, sit amet cursus turpis risus at purus. Quisque sed dictum. +
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+ +
+{% endblock %} \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Page/gallery.html.twig b/src/Photograph/PhotoBundle/Resources/views/Page/gallery.html.twig new file mode 100644 index 0000000..81397c7 --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Page/gallery.html.twig @@ -0,0 +1,45 @@ +{% extends '::base.html.twig' %} + +{% block body %} + + +
+
+ + {% for div in divs %} + + {% endfor %} + +
+
+
+
+ +{% endblock %} + +{% block javascripts %} + + +{% endblock %} \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-angelique-et-pierre.html.twig b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-angelique-et-pierre.html.twig new file mode 100644 index 0000000..f5719e9 --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-angelique-et-pierre.html.twig @@ -0,0 +1,46 @@ +{% extends '::base.html.twig' %} + +{% block body %} + +
+
+

Angélique et Pierre

+ + +
+ +
+ +
+ +
+ CacherPlus d'info +
+
+
+

Lieu:Strasbourg

+

Prestation: Cérémonie, Vin d'honneur, Soirée, "Day After"

+
+
+ +
+
+

Angélique et Pierre forment un couple plein d'humour et de tendresse avec qui nous avons passé un merveilleux moment.

+ voir la galerie complète +
+
+ +
+
+ + +
+ {% endblock %} \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-architecture.html.twig b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-architecture.html.twig new file mode 100644 index 0000000..3ffce48 --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-architecture.html.twig @@ -0,0 +1,47 @@ +{% extends '::base.html.twig' %} + +{% block body %} + +
+
+

Architecture

+ + +
+ +
+ +
+ +
+ CacherPlus d'info +
+
+
+

Lieu: Divers

+ +
+
+ +
+
+

Passioné de photographie, j'aprécie la photo de paysages, qui permet de m'isoler et d'être en harmonie avec la nature.

+

J'ai une préférence pour les paysages de bords de mer, notamment la Bretagne que j'affectionne particulièrement puisque ce sont mes origines.

+ voir la galerie complète +
+
+ +
+
+ + +
+ {% endblock %} \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-maud-et-fabrice.html.twig b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-maud-et-fabrice.html.twig new file mode 100644 index 0000000..67cf218 --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-maud-et-fabrice.html.twig @@ -0,0 +1,46 @@ +{% extends '::base.html.twig' %} + +{% block body %} + +
+
+

Maud et Fabrice

+ + +
+ +
+ +
+ +
+ CacherPlus d'info +
+
+
+

Lieu: Grenoble

+

Prestation: Préparatifs, Cérémonie, Vin d'honneur, Soirée +

+
+ +
+
+

Un mariage dans un cadre magnifique perdu au milieux des Alpes.

+ voir la galerie complète +
+
+ +
+
+ + +
+ {% endblock %} \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-paysages.html.twig b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-paysages.html.twig new file mode 100644 index 0000000..f61ec58 --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-paysages.html.twig @@ -0,0 +1,47 @@ +{% extends '::base.html.twig' %} + +{% block body %} + +
+
+

Paysages

+ + +
+ +
+ +
+ +
+ CacherPlus d'info +
+
+
+

Lieu: Divers

+ +
+
+ +
+
+

Passioné de photographie, j'aprécie la photo de paysages, qui permet de m'isoler et d'être en harmonie avec la nature.

+

J'ai une préférence pour les paysages de bords de mer, notamment la Bretagne que j'affectionne particulièrement puisque ce sont mes origines.

+ voir la galerie complète +
+
+ +
+
+ + +
+ {% endblock %} \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-portraits.html.twig b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-portraits.html.twig new file mode 100644 index 0000000..55a393b --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio-post-portraits.html.twig @@ -0,0 +1,47 @@ +{% extends '::base.html.twig' %} + +{% block body %} + +
+
+

Portraits

+ + +
+ +
+ +
+ +
+ CacherPlus d'info +
+
+
+

Lieu: Divers

+ +
+
+ + + +
+
+ + +
+ {% endblock %} \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Page/portfolio.html.twig b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio.html.twig new file mode 100644 index 0000000..ff25a31 --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Page/portfolio.html.twig @@ -0,0 +1,142 @@ +{% extends '::base.html.twig' %} + +{% block body %} +
+ +
+
+
+

Portfolio

+ +
+
+
+
+
+
+
+ +
+ +
+
+
+ +
+
+
+
+ +
+ +
+ {% endblock %} + + {% block javascripts %} + + {% endblock %} \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Page/prestations.html.twig b/src/Photograph/PhotoBundle/Resources/views/Page/prestations.html.twig new file mode 100644 index 0000000..162a237 --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Page/prestations.html.twig @@ -0,0 +1,155 @@ +{% extends '::base.html.twig' %} + +{% block body %} +
+ +
+
+
+

Prestations

+
+
+
+ +
+
+ +
+ + + {% endblock %} \ No newline at end of file diff --git a/src/Photograph/PhotoBundle/Resources/views/Page/sitemap.html.twig b/src/Photograph/PhotoBundle/Resources/views/Page/sitemap.html.twig new file mode 100644 index 0000000..ad95847 --- /dev/null +++ b/src/Photograph/PhotoBundle/Resources/views/Page/sitemap.html.twig @@ -0,0 +1 @@ +{% extends '::base.html.twig' %} diff --git a/src/Photograph/PhotoBundle/Tests/Controller/DefaultControllerTest.php b/src/Photograph/PhotoBundle/Tests/Controller/DefaultControllerTest.php new file mode 100644 index 0000000..42c0d03 --- /dev/null +++ b/src/Photograph/PhotoBundle/Tests/Controller/DefaultControllerTest.php @@ -0,0 +1,17 @@ +request('GET', '/hello/Fabien'); + + $this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0); + } +} diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000..aa27b80 --- /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 +{ + private $prefixes = array(); + private $fallbackDirs = array(); + private $useIncludePath = false; + private $classMap = array(); + + public function getPrefixes() + { + return call_user_func_array('array_merge', $this->prefixes); + } + + public function getFallbackDirs() + { + return $this->fallbackDirs; + } + + 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 classes, merging with any others previously set. + * + * @param string $prefix The classes prefix + * @param array|string $paths The location(s) of the classes + * @param bool $prepend Prepend the location(s) + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirs = array_merge( + (array) $paths, + $this->fallbackDirs + ); + } else { + $this->fallbackDirs = array_merge( + $this->fallbackDirs, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixes[$first][$prefix])) { + $this->prefixes[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixes[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixes[$first][$prefix] + ); + } else { + $this->prefixes[$first][$prefix] = array_merge( + $this->prefixes[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of classes, replacing any others previously set. + * + * @param string $prefix The classes prefix + * @param array|string $paths The location(s) of the classes + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirs = (array) $paths; + + return; + } + $this->prefixes[substr($prefix, 0, 1)][$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; + } + + /** + * 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)) { + include $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); + } + + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $classPath = strtr(substr($class, 0, $pos), '\\', DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + $className = substr($class, $pos + 1); + } else { + // PEAR-like class name + $classPath = null; + $className = $class; + } + + $classPath .= strtr($className, '_', DIRECTORY_SEPARATOR) . '.php'; + + $first = $class[0]; + if (isset($this->prefixes[$first])) { + foreach ($this->prefixes[$first] as $prefix => $dirs) { + if (0 === strpos($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; + } + + return $this->classMap[$class] = false; + } +} diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..487a70f --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,15 @@ + $vendorDir . '/symfony/symfony/src/Symfony/Component/Intl/Resources/stubs/Collator.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', + 'SessionHandlerInterface' => $vendorDir . '/symfony/symfony/src/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php', + 'SqlFormatter' => $vendorDir . '/jdorn/sql-formatter/lib/SqlFormatter.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..728e64e --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,28 @@ + array($vendorDir . '/twig/extensions/lib'), + 'Twig_' => array($vendorDir . '/twig/twig/lib'), + 'Symfony\\Component\\Icu\\' => array($vendorDir . '/symfony/icu'), + 'Symfony\\Bundle\\SwiftmailerBundle' => array($vendorDir . '/symfony/swiftmailer-bundle'), + 'Symfony\\Bundle\\MonologBundle' => array($vendorDir . '/symfony/monolog-bundle'), + 'Symfony\\Bundle\\AsseticBundle' => array($vendorDir . '/symfony/assetic-bundle'), + 'Symfony\\' => array($vendorDir . '/symfony/symfony/src'), + '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'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log'), + 'Monolog' => array($vendorDir . '/monolog/monolog/src'), + 'Incenteev\\ParameterHandler' => array($vendorDir . '/incenteev/composer-parameter-handler'), + 'Doctrine\\ORM' => array($vendorDir . '/doctrine/orm/lib'), + 'Doctrine\\DBAL' => array($vendorDir . '/doctrine/dbal/lib'), + 'Doctrine\\Common' => array($vendorDir . '/doctrine/common/lib'), + 'Doctrine\\Bundle\\DoctrineBundle' => array($vendorDir . '/doctrine/doctrine-bundle'), + 'Assetic' => array($vendorDir . '/kriswallsmith/assetic/src'), + '' => array($baseDir . '/src'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 0000000..bab3b31 --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,47 @@ + $path) { + $loader->set($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + + $loader->register(true); + + require $vendorDir . '/symfony/symfony/src/Symfony/Component/Intl/Resources/stubs/functions.php'; + require $vendorDir . '/kriswallsmith/assetic/src/functions.php'; + require $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php'; + + return $loader; + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000..6dac178 --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,1203 @@ +[ + { + "name": "jdorn/sql-formatter", + "version": "v1.2.9", + "version_normalized": "1.2.9.0", + "source": { + "type": "git", + "url": "https://github.com/jdorn/sql-formatter.git", + "reference": "v1.2.9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/v1.2.9", + "reference": "v1.2.9", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "time": "2013-04-26 18:42:52", + "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/common", + "version": "2.3.0", + "version_normalized": "2.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common", + "reference": "2.3.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/doctrine/common/zipball/2.3.0", + "reference": "2.3.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "time": "2012-09-19 22:55:18", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ] + }, + { + "name": "doctrine/dbal", + "version": "2.3.4", + "version_normalized": "2.3.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "2.3.4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/2.3.4", + "reference": "2.3.4", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.3.0,<2.5-dev", + "php": ">=5.3.2" + }, + "time": "2013-05-11 07:45:37", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\DBAL": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "persistence", + "queryobject" + ] + }, + { + "name": "psr/log", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log", + "reference": "1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/php-fig/log/archive/1.0.0.zip", + "reference": "1.0.0", + "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": "twig/twig", + "version": "v1.13.1", + "version_normalized": "1.13.1.0", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Twig.git", + "reference": "v1.13.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.13.1", + "reference": "v1.13.1", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "time": "2013-06-06 06:06:01", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.13-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ] + }, + { + "name": "symfony/symfony", + "version": "v2.3.1", + "version_normalized": "2.3.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/symfony.git", + "reference": "v2.3.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/symfony/zipball/v2.3.1", + "reference": "v2.3.1", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.2,<3.0", + "php": ">=5.3.3", + "psr/log": ">=1.0,<2.0", + "symfony/icu": ">=1.0,<2.0", + "twig/twig": ">=1.11.0,<2.0" + }, + "replace": { + "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/dependency-injection": "self.version", + "symfony/doctrine-bridge": "self.version", + "symfony/dom-crawler": "self.version", + "symfony/event-dispatcher": "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/locale": "self.version", + "symfony/monolog-bridge": "self.version", + "symfony/options-resolver": "self.version", + "symfony/process": "self.version", + "symfony/propel1-bridge": "self.version", + "symfony/property-access": "self.version", + "symfony/proxy-manager-bridge": "self.version", + "symfony/routing": "self.version", + "symfony/security": "self.version", + "symfony/security-bundle": "self.version", + "symfony/serializer": "self.version", + "symfony/stopwatch": "self.version", + "symfony/swiftmailer-bridge": "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/web-profiler-bundle": "self.version", + "symfony/yaml": "self.version" + }, + "require-dev": { + "doctrine/data-fixtures": "1.0.*", + "doctrine/dbal": ">=2.2,<3.0", + "doctrine/orm": ">=2.2,<3.0,>=2.2.3", + "ircmaxell/password-compat": "1.0.*", + "monolog/monolog": ">=1.3,<2.0", + "ocramius/proxy-manager": ">=0.3.1,<0.4-dev", + "propel/propel1": "1.6.*" + }, + "time": "2013-06-11 11:46:38", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\": "src/" + }, + "classmap": [ + "src/Symfony/Component/HttpFoundation/Resources/stubs", + "src/Symfony/Component/Intl/Resources/stubs" + ], + "files": [ + "src/Symfony/Component/Intl/Resources/stubs/functions.php" + ] + }, + "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": "The Symfony PHP framework", + "homepage": "http://symfony.com", + "keywords": [ + "framework" + ] + }, + { + "name": "symfony/icu", + "version": "v1.2.0", + "version_normalized": "1.2.0.0", + "target-dir": "Symfony/Component/Icu", + "source": { + "type": "git", + "url": "https://github.com/symfony/Icu.git", + "reference": "v1.2.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Icu/zipball/v1.2.0", + "reference": "v1.2.0", + "shasum": "" + }, + "require": { + "lib-icu": ">=4.4", + "php": ">=5.3.3", + "symfony/intl": ">=2.3,<3.0" + }, + "time": "2013-06-03 18:32:58", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\Icu\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Contains an excerpt of the ICU data and classes to load it.", + "homepage": "http://symfony.com", + "keywords": [ + "icu", + "intl" + ] + }, + { + "name": "doctrine/doctrine-bundle", + "version": "v1.2.0", + "version_normalized": "1.2.0.0", + "target-dir": "Doctrine/Bundle/DoctrineBundle", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineBundle.git", + "reference": "v1.2.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/v1.2.0", + "reference": "v1.2.0", + "shasum": "" + }, + "require": { + "doctrine/dbal": ">=2.2,<2.5-dev", + "jdorn/sql-formatter": ">=1.1,<2.0", + "php": ">=5.3.2", + "symfony/doctrine-bridge": ">=2.2,<3.0", + "symfony/framework-bundle": ">=2.2,<3.0" + }, + "require-dev": { + "doctrine/orm": ">=2.2,<2.5-dev", + "symfony/validator": ">=2.2,<3.0", + "symfony/yaml": ">=2.2,<3.0" + }, + "suggest": { + "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", + "symfony/web-profiler-bundle": "to use the data collector" + }, + "time": "2013-03-25 20:13:59", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Bundle\\DoctrineBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + } + ], + "description": "Symfony DoctrineBundle", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "orm", + "persistence" + ] + }, + { + "name": "twig/extensions", + "version": "v1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Twig-extensions.git", + "reference": "v1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Twig-extensions/zipball/v1.0.0", + "reference": "v1.0.0", + "shasum": "" + }, + "require": { + "twig/twig": "1.*" + }, + "time": "2013-02-28 14:21:30", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Twig_Extensions_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Common additional features for Twig that do not directly belong in core", + "homepage": "https://github.com/fabpot/Twig-extensions", + "keywords": [ + "debug", + "i18n", + "text" + ] + }, + { + "name": "kriswallsmith/assetic", + "version": "v1.1.1", + "version_normalized": "1.1.1.0", + "source": { + "type": "git", + "url": "https://github.com/kriswallsmith/assetic.git", + "reference": "v1.1.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/v1.1.1", + "reference": "v1.1.1", + "shasum": "" + }, + "require": { + "php": ">=5.3.1", + "symfony/process": ">=2.1,<3.0" + }, + "require-dev": { + "cssmin/cssmin": "*", + "joliclic/javascript-packer": "*", + "kamicane/packager": "*", + "leafo/lessphp": "*", + "leafo/scssphp": "*", + "leafo/scssphp-compass": "*", + "mrclay/minify": "*", + "phpunit/phpunit": ">=3.7,<4.0", + "ptachoire/cssembed": "*", + "twig/twig": ">=1.6,<2.0" + }, + "suggest": { + "leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler", + "leafo/scssphp": "Assetic provides the integration with the scssphp SCSS compiler", + "leafo/scssphp-compass": "Assetic provides the integration with the SCSS compass plugin", + "ptachoire/cssembed": "Assetic provides the integration with phpcssembed to embed data uris", + "twig/twig": "Assetic provides the integration with the Twig templating engine" + }, + "time": "2013-06-01 22:13:43", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Assetic": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Asset Management for PHP", + "homepage": "https://github.com/kriswallsmith/assetic", + "keywords": [ + "assets", + "compression", + "minification" + ] + }, + { + "name": "symfony/assetic-bundle", + "version": "v2.3.0", + "version_normalized": "2.3.0.0", + "target-dir": "Symfony/Bundle/AsseticBundle", + "source": { + "type": "git", + "url": "https://github.com/symfony/AsseticBundle.git", + "reference": "v2.3.0-beta1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/AsseticBundle/zipball/v2.3.0-beta1", + "reference": "v2.3.0-beta1", + "shasum": "" + }, + "require": { + "kriswallsmith/assetic": ">=1.1-beta1,<1.2", + "php": ">=5.3.0", + "symfony/framework-bundle": ">=2.1,<3.0" + }, + "require-dev": { + "symfony/class-loader": ">=2.1,<3.0", + "symfony/console": ">=2.1,<3.0", + "symfony/css-selector": ">=2.1,<3.0", + "symfony/dom-crawler": ">=2.1,<3.0", + "symfony/form": ">=2.1,<3.0", + "symfony/twig-bundle": ">=2.1,<3.0", + "symfony/yaml": ">=2.1,<3.0" + }, + "suggest": { + "symfony/twig-bundle": "~2.1" + }, + "time": "2013-05-13 15:39:47", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Bundle\\AsseticBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Integrates Assetic into Symfony2", + "homepage": "https://github.com/symfony/AsseticBundle", + "keywords": [ + "assets", + "compression", + "minification" + ] + }, + { + "name": "incenteev/composer-parameter-handler", + "version": "v2.0.0", + "version_normalized": "2.0.0.0", + "target-dir": "Incenteev/ParameterHandler", + "source": { + "type": "git", + "url": "https://github.com/Incenteev/ParameterHandler.git", + "reference": "v2.0.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/v2.0.0", + "reference": "v2.0.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/yaml": ">=2.0,<3.0" + }, + "require-dev": { + "composer/composer": "*" + }, + "time": "2013-04-06 17:15:44", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "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": "monolog/monolog", + "version": "1.5.0", + "version_normalized": "1.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "1.5.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1.5.0", + "reference": "1.5.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": ">=1.0,<2.0" + }, + "require-dev": { + "doctrine/couchdb": "dev-master", + "mlehner/gelf-php": "1.0.*", + "raven/raven": "0.3.*" + }, + "suggest": { + "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", + "mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server", + "raven/raven": "Allow sending log messages to a Sentry server" + }, + "time": "2013-04-23 10:09:48", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Monolog": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be", + "role": "Developer" + } + ], + "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.3.0", + "version_normalized": "2.3.0.0", + "target-dir": "Symfony/Bundle/MonologBundle", + "source": { + "type": "git", + "url": "https://github.com/symfony/MonologBundle.git", + "reference": "v2.3.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/MonologBundle/zipball/v2.3.0", + "reference": "v2.3.0", + "shasum": "" + }, + "require": { + "monolog/monolog": ">=1.3,<2.0", + "php": ">=5.3.2", + "symfony/config": ">=2.2-beta2,<3.0", + "symfony/dependency-injection": ">=2.2-beta2,<3.0", + "symfony/monolog-bridge": ">=2.2-beta2,<3.0" + }, + "require-dev": { + "symfony/yaml": ">=2.2-beta2,<3.0" + }, + "time": "2013-05-27 18:06:55", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Bundle\\MonologBundle": "" + } + }, + "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 MonologBundle", + "homepage": "http://symfony.com", + "keywords": [ + "log", + "logging" + ] + }, + { + "name": "doctrine/orm", + "version": "2.3.4", + "version_normalized": "2.3.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/doctrine2.git", + "reference": "2.3.4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/2.3.4", + "reference": "2.3.4", + "shasum": "" + }, + "require": { + "doctrine/dbal": "2.3.*", + "ext-pdo": "*", + "php": ">=5.3.2", + "symfony/console": "2.*" + }, + "suggest": { + "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + }, + "time": "2013-05-11 07:51:12", + "bin": [ + "bin/doctrine", + "bin/doctrine.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\ORM": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "orm" + ] + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v5.0.0", + "version_normalized": "5.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "v5.0.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/v5.0.0", + "reference": "v5.0.0", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "time": "2013-04-30 17:35:30", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Chris Corbyn" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "http://swiftmailer.org", + "keywords": [ + "mail", + "mailer" + ] + }, + { + "name": "symfony/swiftmailer-bundle", + "version": "v2.3.1", + "version_normalized": "2.3.1.0", + "target-dir": "Symfony/Bundle/SwiftmailerBundle", + "source": { + "type": "git", + "url": "https://github.com/symfony/SwiftmailerBundle.git", + "reference": "v2.3.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/SwiftmailerBundle/zipball/v2.3.1", + "reference": "v2.3.1", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "swiftmailer/swiftmailer": ">=4.2.0,<5.1-dev", + "symfony/swiftmailer-bridge": ">=2.1,<3.0" + }, + "require-dev": { + "symfony/config": ">=2.1,<3.0", + "symfony/dependency-injection": ">=2.1,<3.0", + "symfony/http-kernel": ">=2.1,<3.0", + "symfony/yaml": ">=2.1,<3.0" + }, + "time": "2013-05-15 08:38:58", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Bundle\\SwiftmailerBundle": "" + } + }, + "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 SwiftmailerBundle", + "homepage": "http://symfony.com" + }, + { + "name": "sensio/distribution-bundle", + "version": "v2.3.1", + "version_normalized": "2.3.1.0", + "target-dir": "Sensio/Bundle/DistributionBundle", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioDistributionBundle.git", + "reference": "v2.3.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/v2.3.1", + "reference": "v2.3.1", + "shasum": "" + }, + "require": { + "symfony/framework-bundle": ">=2.2,<3.0" + }, + "time": "2013-05-30 16:15:25", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Sensio\\Bundle\\DistributionBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "The base bundle for the Symfony Distributions", + "keywords": [ + "configuration", + "distribution" + ] + }, + { + "name": "sensio/framework-extra-bundle", + "version": "v2.3.1", + "version_normalized": "2.3.1.0", + "target-dir": "Sensio/Bundle/FrameworkExtraBundle", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", + "reference": "v2.3.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/v2.3.1", + "reference": "v2.3.1", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.2,<3.0", + "symfony/framework-bundle": ">=2.2,<3.0" + }, + "time": "2013-06-02 16:13:20", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "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": "sensio/generator-bundle", + "version": "v2.3.1", + "version_normalized": "2.3.1.0", + "target-dir": "Sensio/Bundle/GeneratorBundle", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioGeneratorBundle.git", + "reference": "v2.3.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioGeneratorBundle/zipball/v2.3.1", + "reference": "v2.3.1", + "shasum": "" + }, + "require": { + "symfony/console": ">=2.0,<3.0", + "symfony/framework-bundle": ">=2.2,<3.0" + }, + "require-dev": { + "doctrine/orm": ">=2.2,<3.0,>=2.2.3", + "symfony/doctrine-bridge": ">=2.2,<3.0", + "twig/twig": ">=1.11.0,<2.0" + }, + "time": "2013-06-05 17:32:22", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Sensio\\Bundle\\GeneratorBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "This bundle generates code for you" + } +] diff --git a/vendor/doctrine/common/LICENSE b/vendor/doctrine/common/LICENSE new file mode 100644 index 0000000..4a91f0b --- /dev/null +++ b/vendor/doctrine/common/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/common/README.md b/vendor/doctrine/common/README.md new file mode 100644 index 0000000..c63f762 --- /dev/null +++ b/vendor/doctrine/common/README.md @@ -0,0 +1,12 @@ +# Doctrine Common + +[![Build Status](https://secure.travis-ci.org/doctrine/common.png)](http://travis-ci.org/doctrine/common) + +The Doctrine Common project is a library that provides extensions to core PHP functionality. + +## More resources: + +* [Website](http://www.doctrine-project.org) +* [Documentation](http://www.doctrine-project.org/projects/common/current/docs/en) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DCOM) +* [Downloads](http://github.com/doctrine/common/downloads) diff --git a/vendor/doctrine/common/UPGRADE_TO_2_1 b/vendor/doctrine/common/UPGRADE_TO_2_1 new file mode 100644 index 0000000..891a2e5 --- /dev/null +++ b/vendor/doctrine/common/UPGRADE_TO_2_1 @@ -0,0 +1,39 @@ +This document details all the possible changes that you should investigate when updating +your project from Doctrine Common 2.0.x to 2.1 + +## AnnotationReader changes + +The annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory the operation of the new reader should be backwards compatible, but it has to be setup differently to work that way: + + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $reader->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/bin/travis-setup.php b/vendor/doctrine/common/bin/travis-setup.php new file mode 100644 index 0000000..e9c355a --- /dev/null +++ b/vendor/doctrine/common/bin/travis-setup.php @@ -0,0 +1,141 @@ +. + */ + +/** + * Install PHP extensions required for testing by Travis CI. + * + * @author Victor Berchet + * @since 2.2 + */ +$installer = new PhpExtensions(); + +if (isset($argv[1]) && 'APC' === strtoupper($argv[1])) { + $installer->install('apc'); +} else { + $installer->install('xcache'); +} + +$installer->install('memcache'); +$installer->install('memcached'); + +class PhpExtensions +{ + protected $extensions; + protected $phpVersion; + protected $iniPath; + + public function __construct() + { + $this->phpVersion = phpversion(); + $this->iniPath = php_ini_loaded_file(); + $this->extensions = array( + 'memcache' => array( + 'url' => 'http://pecl.php.net/get/memcache-2.2.6.tgz', + 'php_version' => array(), + 'cfg' => array('--enable-memcache'), + 'ini' => array('extension=memcache.so'), + ), + 'memcached' => array( + 'url' => 'http://pecl.php.net/get/memcached-1.0.2.tgz', + 'php_version' => array( + // memcached 1.0.2 does not build on PHP 5.4 + array('<', '5.4'), + ), + 'cfg' => array(), + 'ini' => array('extension=memcached.so'), + ), + 'apc' => array( + 'url' => 'http://pecl.php.net/get/APC-3.1.9.tgz', + 'php_version' => array( + // apc 3.1.9 causes a segfault on PHP 5.4 + array('<', '5.4'), + ), + 'cfg' => array(), + 'ini' => array( + 'extension=apc.so', + 'apc.enabled=1', + 'apc.enable_cli=1' + ), + ), + 'xcache' => array( + 'url' => 'http://xcache.lighttpd.net/pub/Releases/1.2.2/xcache-1.2.2.tar.gz', + 'php_version' => array( + // xcache does not build with Travis CI (as of 2012-01-09) + array('<', '5'), + ), + 'cfg' => array('--enable-xcache'), + 'ini' => array( + 'extension=xcache.so', + 'xcache.cacher=false', + 'xcache.admin.enable_auth=0', + 'xcache.var_size=1M', + ), + ), + ); + } + + public function install($name) + { + if (array_key_exists($name, $this->extensions)) { + $extension = $this->extensions[$name]; + + + echo "== extension: $name ==\n"; + + foreach ($extension['php_version'] as $version) { + if (!version_compare($this->phpVersion, $version[1], $version[0])) { + printf( + "=> not installed, requires a PHP version %s %s (%s installed)\n", + $version[0], + $version[1], + $this->phpVersion + ); + + return; + } + } + + $this->system(sprintf("wget %s > /dev/null 2>&1", $extension['url'])); + $file = basename($extension['url']); + $this->system(sprintf("tar -xzf %s > /dev/null 2>&1", $file)); + $folder = basename($file, ".tgz"); + $folder = basename($folder, ".tar.gz"); + $this->system(sprintf( + 'sh -c "cd %s && phpize && ./configure %s && make && sudo make install" > /dev/null 2>&1', + $folder, + implode(' ', $extension['cfg']) + )); + foreach ($extension['ini'] as $ini) { + $this->system(sprintf("echo %s >> %s", $ini, $this->iniPath)); + } + printf("=> installed (%s)\n", $folder); + } + } + + private function system($cmd) + { + $ret = 0; + system($cmd, $ret); + if (0 !== $ret) { + printf("=> Command '%s' failed !", $cmd); + + exit($ret); + } + } +} diff --git a/vendor/doctrine/common/composer.json b/vendor/doctrine/common/composer.json new file mode 100644 index 0000000..c87258d --- /dev/null +++ b/vendor/doctrine/common/composer.json @@ -0,0 +1,26 @@ +{ + "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.3.2" + }, + "autoload": { + "psr-0": { "Doctrine\\Common": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation.php new file mode 100644 index 0000000..6a1390a --- /dev/null +++ b/vendor/doctrine/common/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 Unkown 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/common/lib/Doctrine/Common/Annotations/Annotation/Attribute.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Attribute.php new file mode 100644 index 0000000..dbef6df --- /dev/null +++ b/vendor/doctrine/common/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/common/lib/Doctrine/Common/Annotations/Annotation/Attributes.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Attributes.php new file mode 100644 index 0000000..53134e3 --- /dev/null +++ b/vendor/doctrine/common/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/common/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php new file mode 100644 index 0000000..a84a4f5 --- /dev/null +++ b/vendor/doctrine/common/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/common/lib/Doctrine/Common/Annotations/Annotation/Required.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Required.php new file mode 100644 index 0000000..d67f960 --- /dev/null +++ b/vendor/doctrine/common/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/common/lib/Doctrine/Common/Annotations/Annotation/Target.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Target.php new file mode 100644 index 0000000..64655ef --- /dev/null +++ b/vendor/doctrine/common/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 construct + * + * @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/common/lib/Doctrine/Common/Annotations/AnnotationException.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationException.php new file mode 100644 index 0000000..109beeb --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationException.php @@ -0,0 +1,127 @@ +. + */ + +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 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 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 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 typeError($attributeName, $annotationName, $context, $expected, $actual) + { + return new self(sprintf( + '[Type Error] 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 new self(sprintf( + '[Type Error] Attribute "%s" of @%s declared on %s expects %s. This value should not be null.', + $attributeName, + $annotationName, + $context, + $expected + )); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationReader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationReader.php new file mode 100644 index 0000000..286e7d0 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationReader.php @@ -0,0 +1,310 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation; +use Doctrine\Common\Annotations\Annotation\Target; +use Closure; +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( + 'access'=> true, 'author'=> true, 'copyright'=> true, 'deprecated'=> true, + 'example'=> true, 'ignore'=> true, 'internal'=> true, 'link'=> true, 'see'=> true, + 'since'=> true, 'tutorial'=> true, 'version'=> true, 'package'=> true, + 'subpackage'=> true, 'name'=> true, 'global'=> true, 'param'=> true, + 'return'=> true, 'staticvar'=> true, 'category'=> true, 'staticVar'=> true, + 'static'=> true, 'var'=> true, 'throws'=> true, 'inheritdoc'=> true, + 'inheritDoc'=> true, 'license'=> true, 'todo'=> true, + 'deprec'=> true, 'property' => true, 'method' => true, + 'abstract'=> true, 'exception'=> true, 'magic' => true, 'api' => true, + 'final'=> true, 'filesource'=> true, 'throw' => true, 'uses' => true, + 'usedby'=> true, 'private' => true, 'Annotation' => true, 'override' => true, + 'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true, + 'Required' => true, 'Attribute' => true, 'Attributes' => true, + 'Target' => true, 'SuppressWarnings' => true, + 'ingroup' => true, 'code' => true, 'endcode' => true, + 'package_version' => 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() + { + 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; + } + + /** + * 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. + */ + public function getClassAnnotations(ReflectionClass $class) + { + $this->parser->setTarget(Target::TARGET_CLASS); + $this->parser->setImports($this->getImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); + } + + /** + * 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 mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getClassAnnotation(ReflectionClass $class, $annotationName) + { + $annotations = $this->getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * 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. + */ + public function getPropertyAnnotations(ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $context = 'property ' . $class->getName() . "::\$" . $property->getName(); + $this->parser->setTarget(Target::TARGET_PROPERTY); + $this->parser->setImports($this->getImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($property->getDocComment(), $context); + } + + /** + * Gets a property annotation. + * + * @param ReflectionProperty $property + * @param string $annotationName The name of the annotation. + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * 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. + */ + public function getMethodAnnotations(ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; + $this->parser->setTarget(Target::TARGET_METHOD); + $this->parser->setImports($this->getImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($method->getDocComment(), $context); + } + + /** + * Gets a method annotation. + * + * @param ReflectionMethod $method + * @param string $annotationName The name of the annotation. + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + 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]; + } + + /** + * Retrieve imports + * + * @param \ReflectionClass $class + * @return array + */ + private function getImports(ReflectionClass $class) + { + if (isset($this->imports[$name = $class->getName()])) { + return $this->imports[$name]; + } + $this->collectParsingMetadata($class); + + return $this->imports[$name]; + } + + /** + * 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/common/lib/Doctrine/Common/Annotations/AnnotationRegistry.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationRegistry.php new file mode 100644 index 0000000..dfa846a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationRegistry.php @@ -0,0 +1,139 @@ +. + */ + +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(); + + static public function reset() + { + self::$autoloadNamespaces = array(); + self::$loaders = array(); + } + + /** + * Register file + * + * @param string $file + */ + static public function registerFile($file) + { + require_once $file; + } + + /** + * Add 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 + */ + static public function registerAutoloadNamespace($namespace, $dirs = null) + { + self::$autoloadNamespaces[$namespace] = $dirs; + } + + /** + * Register multiple namespaces + * + * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. + * + * @param array $namespaces + */ + static public function registerAutoloadNamespaces(array $namespaces) + { + self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces); + } + + /** + * Register 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 + * + * @throws \InvalidArgumentException + */ + static public function registerLoader($callable) + { + if (!is_callable($callable)) { + throw new \InvalidArgumentException("A callable is expected in AnnotationRegistry::registerLoader()."); + } + self::$loaders[] = $callable; + } + + /** + * Autoload 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 (file_exists($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/common/lib/Doctrine/Common/Annotations/CachedReader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/CachedReader.php new file mode 100644 index 0000000..e377e3b --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/CachedReader.php @@ -0,0 +1,250 @@ +. + */ + +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; + + /** + * 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; + } + + /** + * Get annotations for class + * + * @param \ReflectionClass $class + * @return array + */ + 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; + } + + /** + * Get selected annotation for class + * + * @param \ReflectionClass $class + * @param string $annotationName + * @return null + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Get annotations for property + * + * @param \ReflectionProperty $property + * @return array + */ + 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; + } + + /** + * Get selected annotation for property + * + * @param \ReflectionProperty $property + * @param string $annotationName + * @return null + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + foreach ($this->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Get method annotations + * + * @param \ReflectionMethod $method + * @return array + */ + 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; + } + + /** + * Get selected method annotation + * + * @param \ReflectionMethod $method + * @param string $annotationName + * @return null + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Clear loaded annotations + */ + 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|boolean 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. + */ + private function saveToCache($rawCacheKey, $value) + { + $cacheKey = $rawCacheKey . self::$CACHE_SALT; + $this->cache->save($cacheKey, $value); + if ($this->debug) { + $this->cache->save('[C]'.$cacheKey, time()); + } + } + + /** + * Check if cache is fresh + * + * @param string $cacheKey + * @param \ReflectionClass $class + * @return bool + */ + 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/common/lib/Doctrine/Common/Annotations/DocLexer.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/DocLexer.php new file mode 100644 index 0000000..c9a6f7a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/DocLexer.php @@ -0,0 +1,132 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Lexer; + +/** + * 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 Lexer +{ + 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; + + 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 + ); + + 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]{1}', + '(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?', + '"(?:[^"]|"")*"', + ); + } + + /** + * {@inheritdoc} + */ + protected function getNonCatchablePatterns() + { + return array('\s+', '\*+', '(.)'); + } + + /** + * {@inheritdoc} + * + * @param string $value + * + * @return int + */ + 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/common/lib/Doctrine/Common/Annotations/DocParser.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/DocParser.php new file mode 100644 index 0000000..de31e0b --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/DocParser.php @@ -0,0 +1,988 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Closure; +use ReflectionClass; +use Doctrine\Common\Annotations\Annotation\Target; +use Doctrine\Common\Annotations\Annotation\Attribute; +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' + ) + ), + ), + ); + + /** + * 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 + */ + public function setIgnoredAnnotationNames(array $names) + { + $this->ignoredAnnotationNames = $names; + } + + /** + * Sets ignore on not-imported annotations + * + * @param $bool + */ + public function setIgnoreNotImportedAnnotations($bool) + { + $this->ignoreNotImportedAnnotations = (Boolean) $bool; + } + + /** + * Sets the default namespaces. + * + * @param array $namespace + * + * @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 + * @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 + */ + 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 = '') + { + if (false === $pos = strpos($input, '@')) { + return array(); + } + + // also parse whatever character is before the @ + if ($pos > 0) { + $pos -= 1; + } + + $this->context = $context; + $this->lexer->setInput(trim(substr($input, $pos), '* /')); + $this->lexer->moveNext(); + + return $this->Annotations(); + } + + /** + * 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 type of Token. + * @return bool 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 bool + */ + 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 $token Optional token. + * + * @throws AnnotationException + */ + private function syntaxError($expected, $token = null) + { + if ($token === null) { + $token = $this->lexer->lookahead; + } + + $message = "Expected {$expected}, got "; + + if ($this->lexer->lookahead === null) { + $message .= 'end of string'; + } else { + $message .= "'{$token['value']}' at position {$token['position']}"; + } + + if (strlen($this->context)) { + $message .= ' in ' . $this->context; + } + + $message .= '.'; + + throw AnnotationException::syntaxError($message); + } + + /** + * Attempt 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 + */ + private function collectAnnotationMetadata($name) + { + if (self::$metadataParser == null){ + self::$metadataParser = new self(); + self::$metadataParser->setTarget(Target::TARGET_CLASS); + self::$metadataParser->setIgnoreNotImportedAnnotations(true); + self::$metadataParser->setImports(array( + 'target' => 'Doctrine\Common\Annotations\Annotation\Target', + 'attribute' => 'Doctrine\Common\Annotations\Annotation\Attribute', + 'attributes' => 'Doctrine\Common\Annotations\Annotation\Attributes' + )); + 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']) { + foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) { + if ($annotation instanceof Target) { + $metadata['targets'] = $annotation->targets; + $metadata['targets_literal'] = $annotation->literal; + + } elseif ($annotation instanceof Attributes) { + foreach ($annotation->value as $attrib) { + // handle internal type declaration + $type = isset(self::$typeMap[$attrib->type]) ? self::$typeMap[$attrib->type] : $attrib->type; + + // handle the case if the property type is mixed + if ('mixed' !== $type) { + // Checks if the property has array + if (false !== $pos = strpos($type, '<')) { + $arrayType = substr($type, $pos+1, -1); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$attrib->name]['array_type'] = $arrayType; + } + + $metadata['attribute_types'][$attrib->name]['type'] = $type; + $metadata['attribute_types'][$attrib->name]['value'] = $attrib->type; + $metadata['attribute_types'][$attrib->name]['required'] = $attrib->required; + } + } + } + } + + // 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; + + // checks if the property has @var annotation + if ((false !== $propertyComment = $property->getDocComment()) + && false !== strpos($propertyComment, '@var') + && preg_match('/@var\s+([^\s]+)/',$propertyComment, $matches)) { + // literal type declaration + $value = $matches[1]; + + // handle internal type declaration + $type = isset(self::$typeMap[$value]) ? self::$typeMap[$value] : $value; + + // handle the case if the property type is mixed + if ('mixed' !== $type) { + // Checks if the property has @var array annotation + if (false !== $pos = strpos($type, '<')) { + $arrayType = substr($type, $pos+1, -1); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$property->name]['array_type'] = $arrayType; + } + + $metadata['attribute_types'][$property->name]['type'] = $type; + $metadata['attribute_types'][$property->name]['value'] = $value; + $metadata['attribute_types'][$property->name]['required'] = false !== strpos($propertyComment, '@Required'); + } + } + } + + // choose the first property as default property + $metadata['default_property'] = reset($metadata['properties']); + } + } + + self::$annotationMetadata[$name] = $metadata; + } + + /** + * 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 ["(" [Values] ")"] + * AnnotationName ::= QualifiedName | SimpleName + * QualifiedName ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName + * NameSpacePart ::= identifier | null | false | true + * SimpleName ::= identifier | null | false | true + * + * @throws AnnotationException + * @return mixed False if it is not a valid annotation. + */ + 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)])) { + if (false !== $pos) { + $name = $this->imports[$loweredAlias].substr($name, $pos); + } else { + $name = $this->imports[$loweredAlias]; + } + $found = true; + } elseif (isset($this->imports['__NAMESPACE__']) && $this->classExists($this->imports['__NAMESPACE__'].'\\'.$name)) { + $name = $this->imports['__NAMESPACE__'].'\\'.$name; + $found = true; + } elseif ($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 = array(); + if ($this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) { + $this->match(DocLexer::T_OPEN_PARENTHESIS); + + if ( ! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { + $values = $this->Values(); + } + + $this->match(DocLexer::T_CLOSE_PARENTHESIS); + } + + // 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::typeError($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::typeError($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; + } + + /** + * Values ::= Array | Value {"," Value}* + * + * @return array + */ + private function Values() + { + $values = array(); + + // Handle the case of a single array as value, i.e. @Foo({....}) + if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) { + $values['value'] = $this->Value(); + return $values; + } + + $values[] = $this->Value(); + + while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { + $this->match(DocLexer::T_COMMA); + $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 + * + * @throws AnnotationException + * @return mixed + */ + 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; + if (false !== $pos) { + $className = $this->imports[$loweredAlias].substr($className, $pos); + } else { + $className = $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; + } + } + + 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->lexer->moveNext(); + $className = $this->lexer->token['value']; + } else { + $this->syntaxError('namespace separator or identifier'); + } + + 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); + $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/common/lib/Doctrine/Common/Annotations/FileCacheReader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/FileCacheReader.php new file mode 100644 index 0000000..3934861 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/FileCacheReader.php @@ -0,0 +1,258 @@ +. + */ + +namespace Doctrine\Common\Annotations; + + +/** + * File cache reader for annotations. + * + * @author Johannes M. Schmitt + * @author Benjamin Eberlei + */ +class FileCacheReader implements Reader +{ + /** + * @var Reader + */ + private $reader; + + /** + * @var string + */ + private $dir; + + /** + * @var bool + */ + private $debug; + + /** + * @var array + */ + private $loadedAnnotations = array(); + + /** + * Constructor + * + * @param Reader $reader + * @param string $cacheDir + * @param bool $debug + * + * @throws \InvalidArgumentException + */ + public function __construct(Reader $reader, $cacheDir, $debug = false) + { + $this->reader = $reader; + if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir)); + } + if (!is_writable($cacheDir)) { + 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.', $cacheDir)); + } + + $this->dir = rtrim($cacheDir, '\\/'); + $this->debug = $debug; + } + + /** + * Retrieve annotations for class + * + * @param \ReflectionClass $class + * @return array + */ + public function getClassAnnotations(\ReflectionClass $class) + { + $key = $class->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!file_exists($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; + } + + /** + * Get annotations for property + * + * @param \ReflectionProperty $property + * @return array + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $key = $class->getName().'$'.$property->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!file_exists($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; + } + + /** + * Retrieve annotations for method + * + * @param \ReflectionMethod $method + * @return array + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $key = $class->getName().'#'.$method->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!file_exists($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; + } + + /** + * Save cache file + * + * @param string $path + * @param mixed $data + */ + private function saveCacheFile($path, $data) + { + file_put_contents($path, 'getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Gets a method annotation. + * + * @param \ReflectionMethod $method + * @param string $annotationName The name of the annotation. + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + $annotations = $this->getMethodAnnotations($method); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Gets a property annotation. + * + * @param \ReflectionProperty $property + * @param string $annotationName The name of the annotation. + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Clear stores annotations + */ + public function clearLoadedAnnotations() + { + $this->loadedAnnotations = array(); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/IndexedReader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/IndexedReader.php new file mode 100644 index 0000000..2dfdd4d --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/IndexedReader.php @@ -0,0 +1,141 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Reader; + +/** + * 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; + } + + /** + * Get Annotations for class + * + * @param \ReflectionClass $class + * @return array + */ + public function getClassAnnotations(\ReflectionClass $class) + { + $annotations = array(); + foreach ($this->delegate->getClassAnnotations($class) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * Get selected annotation for class + * + * @param \ReflectionClass $class + * @param string $annotation + * @return mixed + */ + public function getClassAnnotation(\ReflectionClass $class, $annotation) + { + return $this->delegate->getClassAnnotation($class, $annotation); + } + + /** + * Get Annotations for method + * + * @param \ReflectionMethod $method + * @return array + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $annotations = array(); + foreach ($this->delegate->getMethodAnnotations($method) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * Get selected annotation for method + * + * @param \ReflectionMethod $method + * @param string $annotation + * @return mixed + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotation) + { + return $this->delegate->getMethodAnnotation($method, $annotation); + } + + /** + * Get annotations for property + * + * @param \ReflectionProperty $property + * @return array + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $annotations = array(); + foreach ($this->delegate->getPropertyAnnotations($property) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * Get selected annotation for property + * + * @param \ReflectionProperty $property + * @param string $annotation + * @return mixed + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotation) + { + return $this->delegate->getPropertyAnnotation($property, $annotation); + } + + /** + * Proxy 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/common/lib/Doctrine/Common/Annotations/PhpParser.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/PhpParser.php new file mode 100644 index 0000000..c09dd51 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/PhpParser.php @@ -0,0 +1,80 @@ +. + */ + +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()); + $namespace = str_replace('\\', '\\\\', $class->getNamespaceName()); + $content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content); + $tokenizer = new TokenParser('parseUseStatements($class->getNamespaceName()); + + return $statements; + } + + /** + * Get the content of the file right up to the given line number. + * + * @param string $filename The name of the file to load. + * @param int $lineNumber The number of lines to read from file. + * @return string The content of the file. + */ + private function getFileContent($filename, $lineNumber) + { + $content = ''; + $lineCnt = 0; + $file = new SplFileObject($filename); + while (!$file->eof()) { + if ($lineCnt++ == $lineNumber) { + break; + } + + $content .= $file->fgets(); + } + + return $content; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Reader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Reader.php new file mode 100644 index 0000000..6a01cb4 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Reader.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Interface for annotation readers. + * + * @author Johannes M. Schmitt + */ +interface Reader +{ + /** + * @param \ReflectionClass $class + * @return mixed + */ + function getClassAnnotations(\ReflectionClass $class); + + /** + * @param \ReflectionClass $class + * @param string $annotationName + * @return mixed + */ + function getClassAnnotation(\ReflectionClass $class, $annotationName); + + /** + * @param \ReflectionMethod $method + * @return mixed + */ + function getMethodAnnotations(\ReflectionMethod $method); + + /** + * @param \ReflectionMethod $method + * @param string $annotationName + * @return mixed + */ + function getMethodAnnotation(\ReflectionMethod $method, $annotationName); + + /** + * @param \ReflectionProperty $property + * @return mixed + */ + function getPropertyAnnotations(\ReflectionProperty $property); + + /** + * @param \ReflectionProperty $property + * @param string $annotationName + * @return mixed + */ + function getPropertyAnnotation(\ReflectionProperty $property, $annotationName); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php new file mode 100644 index 0000000..4210d90 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php @@ -0,0 +1,157 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\Target; + +/** + * 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 + */ + public function addNamespace($namespace) + { + $this->parser->addNamespace($namespace); + } + + /** + * 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. + */ + public function getClassAnnotations(\ReflectionClass $class) + { + return $this->parser->parse($class->getDocComment(), 'class '.$class->getName()); + } + + /** + * 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. + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + return $this->parser->parse($method->getDocComment(), 'method '.$method->getDeclaringClass()->name.'::'.$method->getName().'()'); + } + + /** + * 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. + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + return $this->parser->parse($property->getDocComment(), 'property '.$property->getDeclaringClass()->name.'::$'.$property->getName()); + } + + /** + * 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 mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Gets a method annotation. + * + * @param \ReflectionMethod $method + * @param string $annotationName The name of the annotation. + * + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Gets a property annotation. + * + * @param \ReflectionProperty $property + * @param string $annotationName The name of the annotation. + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + 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/common/lib/Doctrine/Common/Annotations/TokenParser.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/TokenParser.php new file mode 100644 index 0000000..a1ef115 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/TokenParser.php @@ -0,0 +1,175 @@ +. + */ + +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 = 0; + + /** + * The current array pointer. + * + * @var int + */ + private $pointer = 0; + + public function __construct($contents) + { + $this->tokens = token_get_all($contents); + $this->numTokens = count($this->tokens); + $this->pointer = 0; + } + + /** + * Gets the next non whitespace and non comment token. + * + * @param $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 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; + } + + /** + * Parse 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; + } + + /** + * Get 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; + } + + /** + * Get 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; + } + + /** + * Get the class name. + * + * @return string The foundclass 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/common/lib/Doctrine/Common/Cache/ApcCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ApcCache.php new file mode 100644 index 0000000..2d0cd23 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ApcCache.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * APC cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 (bool) apc_store($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return apc_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return apc_clear_cache() && apc_clear_cache('user'); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = apc_cache_info(); + $sma = apc_sma_info(); + + 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_AVAILIABLE => $sma['avail_mem'], + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/ArrayCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ArrayCache.php new file mode 100644 index 0000000..a7a70aa --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ArrayCache.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Array cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 (isset($this->data[$id])) ? $this->data[$id] : false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return isset($this->data[$id]); + } + + /** + * {@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/common/lib/Doctrine/Common/Cache/Cache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/Cache.php new file mode 100644 index 0000000..5493562 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/Cache.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache drivers. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Fabio B. Silva + */ +interface Cache +{ + const STATS_HITS = 'hits'; + const STATS_MISSES = 'misses'; + const STATS_UPTIME = 'uptime'; + const STATS_MEMORY_USAGE = 'memory_usage'; + const STATS_MEMORY_AVAILIABLE = 'memory_available'; + + /** + * Fetches an entry from the cache. + * + * @param string $id cache 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. + */ + function fetch($id); + + /** + * Test if an entry exists in the cache. + * + * @param string $id cache id The cache id of the entry to check for. + * @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + function contains($id); + + /** + * Puts data into the cache. + * + * @param string $id The cache id. + * @param mixed $data The cache entry/data. + * @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime). + * @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + function save($id, $data, $lifeTime = 0); + + /** + * Deletes a cache entry. + * + * @param string $id cache id + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + function delete($id); + + /** + * Retrieves cached information from 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 + * @var array Associative array with server's statistics if available, NULL otherwise. + */ + function getStats(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/CacheProvider.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/CacheProvider.php new file mode 100644 index 0000000..4221a62 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/CacheProvider.php @@ -0,0 +1,231 @@ +. + */ + +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 +{ + const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]'; + + /** + * @var string The namespace to prefix all cache ids with + */ + private $namespace = ''; + + /** + * @var string The namespace version + */ + private $namespaceVersion; + + /** + * Set the namespace to prefix all cache ids with. + * + * @param string $namespace + * @return void + */ + public function setNamespace($namespace) + { + $this->namespace = (string) $namespace; + } + + /** + * Retrieve 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 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(); + } + + /** + * Deletes all cache entries. + * + * @return boolean TRUE if the cache entries were successfully flushed, FALSE otherwise. + */ + public function flushAll() + { + return $this->doFlush(); + } + + /** + * Delete all cache entries. + * + * @return boolean TRUE if the cache entries were successfully deleted, FALSE otherwise. + */ + public function deleteAll() + { + $namespaceCacheKey = $this->getNamespaceCacheKey(); + $namespaceVersion = $this->getNamespaceVersion() + 1; + + $this->namespaceVersion = $namespaceVersion; + + return $this->doSave($namespaceCacheKey, $namespaceVersion); + } + + /** + * Prefix the passed id with the configured namespace value + * + * @param string $id The id to namespace + * @return string $id The namespaced id + */ + private function getNamespacedId($id) + { + $namespaceVersion = $this->getNamespaceVersion(); + + return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion); + } + + /** + * Namespace cache key + * + * @return string $namespaceCacheKey + */ + private function getNamespaceCacheKey() + { + return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); + } + + /** + * Namespace version + * + * @return string $namespaceVersion + */ + private function getNamespaceVersion() + { + if (null !== $this->namespaceVersion) { + return $this->namespaceVersion; + } + + $namespaceCacheKey = $this->getNamespaceCacheKey(); + $namespaceVersion = $this->doFetch($namespaceCacheKey); + + if (false === $namespaceVersion) { + $namespaceVersion = 1; + + $this->doSave($namespaceCacheKey, $namespaceVersion); + } + + $this->namespaceVersion = $namespaceVersion; + + return $this->namespaceVersion; + } + + /** + * Fetches an entry from the cache. + * + * @param string $id cache id The id of the cache entry to fetch. + * @return string The cached data or FALSE, if no cache entry exists for the given id. + */ + abstract protected function doFetch($id); + + /** + * Test if an entry exists in the cache. + * + * @param string $id cache id The cache id of the entry to check for. + * @return boolean 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 bool|int $lifeTime The lifetime. If != false, sets a specific lifetime for this + * cache entry (null => infinite lifeTime). + * + * @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + abstract protected function doSave($id, $data, $lifeTime = false); + + /** + * Deletes a cache entry. + * + * @param string $id cache id + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + abstract protected function doDelete($id); + + /** + * Deletes all cache entries. + * + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + abstract protected function doFlush(); + + /** + * Retrieves cached information from data store + * + * @since 2.2 + * @return array An associative array with server's statistics if available, NULL otherwise. + */ + abstract protected function doGetStats(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/FileCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/FileCache.php new file mode 100644 index 0000000..da650b4 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/FileCache.php @@ -0,0 +1,132 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Base file cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + */ +abstract class FileCache extends CacheProvider +{ + /** + * @var string Cache directory. + */ + protected $directory; + + /** + * @var string Cache file extension. + */ + protected $extension; + + /** + * Constructor + * + * @param string $directory Cache directory. + * @param string $directory Cache file extension. + * + * @throws \InvalidArgumentException + */ + public function __construct($directory, $extension = null) + { + if ( ! is_dir($directory) && ! @mkdir($directory, 0777, 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->directory = realpath($directory); + $this->extension = $extension ?: $this->extension; + } + + /** + * 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; + } + + /** + * @return string + */ + protected function getFilename($id) + { + $path = implode(str_split(md5($id), 12), DIRECTORY_SEPARATOR); + $path = $this->directory . DIRECTORY_SEPARATOR . $path; + + return $path . DIRECTORY_SEPARATOR . $id . $this->extension; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return @unlink($this->getFilename($id)); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $pattern = '/^.+\\' . $this->extension . '$/i'; + $iterator = new \RecursiveDirectoryIterator($this->directory); + $iterator = new \RecursiveIteratorIterator($iterator); + $iterator = new \RegexIterator($iterator, $pattern); + + foreach ($iterator as $name => $file) { + @unlink($name); + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return null; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/FilesystemCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/FilesystemCache.php new file mode 100644 index 0000000..a27a717 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/FilesystemCache.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Filesystem cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + */ +class FilesystemCache extends FileCache +{ + const EXTENSION = '.doctrinecache.data'; + + /** + * {@inheritdoc} + */ + protected $extension = self::EXTENSION; + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $data = ''; + $lifetime = -1; + $filename = $this->getFilename($id); + + if ( ! file_exists($filename)) { + return false; + } + + $resource = fopen($filename, "r"); + + if (false !== ($line = fgets($resource))) { + $lifetime = (integer) $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 ( ! file_exists($filename)) { + return false; + } + + $resource = fopen($filename, "r"); + + if (false !== ($line = fgets($resource))) { + $lifetime = (integer) $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); + $filepath = pathinfo($filename, PATHINFO_DIRNAME); + + if ( ! is_dir($filepath)) { + mkdir($filepath, 0777, true); + } + + return file_put_contents($filename, $lifeTime . PHP_EOL . $data); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcacheCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcacheCache.php new file mode 100644 index 0000000..5687b96 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcacheCache.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcache; + +/** + * Memcache cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 + */ + private $memcache; + + /** + * Sets the memcache instance to use. + * + * @param Memcache $memcache + */ + public function setMemcache(Memcache $memcache) + { + $this->memcache = $memcache; + } + + /** + * Gets the memcache instance used by the cache. + * + * @return Memcache + */ + public function getMemcache() + { + return $this->memcache; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcache->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (bool) $this->memcache->get($id); + } + + /** + * {@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) + { + return $this->memcache->delete($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_AVAILIABLE => $stats['limit_maxbytes'], + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcachedCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcachedCache.php new file mode 100644 index 0000000..75f1345 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcachedCache.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcached; + +/** + * Memcached cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 + */ + private $memcached; + + /** + * Sets the memcache instance to use. + * + * @param Memcached $memcached + */ + public function setMemcached(Memcached $memcached) + { + $this->memcached = $memcached; + } + + /** + * Gets the memcached instance used by the cache. + * + * @return Memcached + */ + public function getMemcached() + { + return $this->memcached; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcached->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (false !== $this->memcached->get($id)); + } + + /** + * {@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); + } + + /** + * {@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_AVAILIABLE => $stats['limit_maxbytes'], + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/PhpFileCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/PhpFileCache.php new file mode 100644 index 0000000..0971cd9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/PhpFileCache.php @@ -0,0 +1,108 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Php file cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + */ +class PhpFileCache extends FileCache +{ + const EXTENSION = '.doctrinecache.php'; + + /** + * {@inheritdoc} + */ + protected $extension = self::EXTENSION; + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $filename = $this->getFilename($id); + + if ( ! file_exists($filename)) { + return false; + } + + $value = include $filename; + + if ($value['lifetime'] !== 0 && $value['lifetime'] < time()) { + return false; + } + + return $value['data']; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $filename = $this->getFilename($id); + + if ( ! file_exists($filename)) { + return false; + } + + $value = include $filename; + + 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); + $filepath = pathinfo($filename, PATHINFO_DIRNAME); + + if ( ! is_dir($filepath)) { + mkdir($filepath, 0777, true); + } + + $value = array( + 'lifetime' => $lifeTime, + 'data' => $data + ); + + $value = var_export($value, true); + $code = sprintf('. + */ + +namespace Doctrine\Common\Cache; + +use Redis; + +/** + * Redis cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Osman Ungur + */ +class RedisCache extends CacheProvider +{ + /** + * @var Redis + */ + private $redis; + + /** + * Sets the redis instance to use. + * + * @param Redis $redis + */ + public function setRedis(Redis $redis) + { + $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); + $this->redis = $redis; + } + + /** + * Gets the redis instance used by the cache. + * + * @return Redis + */ + public function getRedis() + { + return $this->redis; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->redis->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return $this->redis->exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $result = $this->redis->set($id, $data); + if ($lifeTime > 0) { + $this->redis->expire($id, $lifeTime); + } + return $result; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->redis->delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->redis->flushDB(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = $this->redis->info(); + return array( + Cache::STATS_HITS => false, + Cache::STATS_MISSES => false, + Cache::STATS_UPTIME => $info['uptime_in_seconds'], + Cache::STATS_MEMORY_USAGE => $info['used_memory'], + Cache::STATS_MEMORY_AVAILIABLE => false + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/WinCacheCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/WinCacheCache.php new file mode 100644 index 0000000..777d0fd --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/WinCacheCache.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * WinCache cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 (bool) wincache_ucache_set($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return wincache_ucache_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return wincache_ucache_clear(); + } + + /** + * {@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_AVAILIABLE => $meminfo['memory_free'], + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/XcacheCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/XcacheCache.php new file mode 100644 index 0000000..8733e26 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/XcacheCache.php @@ -0,0 +1,110 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Xcache cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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, 0); + + return true; + } + + /** + * Checks that xcache.admin.enable_auth is Off + * + * @throws \BadMethodCallException When xcache.admin.enable_auth is On + * @return void + */ + 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_AVAILIABLE => $info['avail'], + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/ZendDataCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ZendDataCache.php new file mode 100644 index 0000000..fc90bc6 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ZendDataCache.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Zend Data Cache cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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/common/lib/Doctrine/Common/ClassLoader.php b/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php new file mode 100644 index 0000000..45024e1 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php @@ -0,0 +1,263 @@ +. + */ + +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 + */ +class ClassLoader +{ + /** + * @var string PHP file extension + */ + protected $fileExtension = '.php'; + + /** + * @var string Current namespace + */ + protected $namespace; + + /** + * @var string Current include path + */ + protected $includePath; + + /** + * @var string PHP namespace separator + */ + 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 $ns The namespace of the classes to load. + * @param string $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. + */ + 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 $includePath + */ + public function setIncludePath($includePath) + { + $this->includePath = $includePath; + } + + /** + * Gets the base include path for all class files in the namespace of this ClassLoader. + * + * @return string + */ + public function getIncludePath() + { + return $this->includePath; + } + + /** + * Sets the file extension of class files in the namespace of this ClassLoader. + * + * @param string $fileExtension + */ + 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. + */ + public function register() + { + spl_autoload_register(array($this, 'loadClass')); + } + + /** + * Removes this ClassLoader from the SPL autoload stack. + */ + public function unregister() + { + spl_autoload_unregister(array($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 ($this->namespace !== null && strpos($className, $this->namespace.$this->namespaceSeparator) !== 0) { + return false; + } + + require ($this->includePath !== null ? $this->includePath . DIRECTORY_SEPARATOR : '') + . str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) + . $this->fileExtension; + + return true; + } + + /** + * 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 file_exists($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) + { + if (class_exists($className, false) || interface_exists($className, false)) { + return true; + } + + foreach (spl_autoload_functions() as $loader) { + if (is_array($loader)) { // array(???, ???) + if (is_object($loader[0])) { + if ($loader[0] instanceof ClassLoader) { // array($obj, 'methodName') + if ($loader[0]->canLoadClass($className)) { + return true; + } + } else if ($loader[0]->{$loader[1]}($className)) { + return true; + } + } else if ($loader[0]::$loader[1]($className)) { // array('ClassName', 'methodName') + return true; + } + } else if ($loader instanceof \Closure) { // function($className) {..} + if ($loader($className)) { + return true; + } + } else if (is_string($loader) && $loader($className)) { // "MyClass::loadClass" + return true; + } + } + + return class_exists($className, false) || interface_exists($className, false); + } + + /** + * 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) + && $loader[0] instanceof ClassLoader + && $loader[0]->canLoadClass($className) + ) { + return $loader[0]; + } + } + + return null; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Collections/ArrayCollection.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/ArrayCollection.php new file mode 100644 index 0000000..7c2b13e --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/ArrayCollection.php @@ -0,0 +1,500 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure, ArrayIterator; +use Doctrine\Common\Collections\Expr\Expression; +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; + } + + /** + * Gets the PHP array representation of this collection. + * + * @return array The PHP array representation of this collection. + */ + public function toArray() + { + return $this->_elements; + } + + /** + * Sets the internal iterator to the first element in the collection and + * returns this element. + * + * @return mixed + */ + public function first() + { + return reset($this->_elements); + } + + /** + * Sets the internal iterator to the last element in the collection and + * returns this element. + * + * @return mixed + */ + public function last() + { + return end($this->_elements); + } + + /** + * Gets the current key/index at the current internal iterator position. + * + * @return mixed + */ + public function key() + { + return key($this->_elements); + } + + /** + * Moves the internal iterator position to the next element. + * + * @return mixed + */ + public function next() + { + return next($this->_elements); + } + + /** + * Gets the element of the collection at the current internal iterator position. + * + * @return mixed + */ + public function current() + { + return current($this->_elements); + } + + /** + * Removes an element with a specific key/index from the collection. + * + * @param mixed $key + * @return mixed The removed element or NULL, if no element exists for the given key. + */ + public function remove($key) + { + if (isset($this->_elements[$key])) { + $removed = $this->_elements[$key]; + unset($this->_elements[$key]); + + return $removed; + } + + return null; + } + + /** + * 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) + { + $key = array_search($element, $this->_elements, true); + + if ($key !== false) { + unset($this->_elements[$key]); + + return true; + } + + return false; + } + + /** + * ArrayAccess implementation of offsetExists() + * + * @see containsKey() + * + * @param mixed $offset + * @return bool + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * ArrayAccess implementation of offsetGet() + * + * @see get() + * + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * ArrayAccess implementation of offsetSet() + * + * @see add() + * @see set() + * + * @param mixed $offset + * @param mixed $value + * @return bool + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + return $this->add($value); + } + return $this->set($offset, $value); + } + + /** + * ArrayAccess implementation of offsetUnset() + * + * @see remove() + * + * @param mixed $offset + * @return mixed + */ + public function offsetUnset($offset) + { + return $this->remove($offset); + } + + /** + * Checks whether the collection contains a specific key/index. + * + * @param mixed $key The key to check for. + * @return boolean TRUE if the given key/index exists, FALSE otherwise. + */ + public function containsKey($key) + { + return isset($this->_elements[$key]); + } + + /** + * Checks whether the given element is contained in the collection. + * Only element values are compared, not keys. 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 + * @return boolean TRUE if the given element is contained in the collection, + * FALSE otherwise. + */ + public function contains($element) + { + foreach ($this->_elements as $collectionElement) { + if ($element === $collectionElement) { + return true; + } + } + + return false; + } + + /** + * 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) + { + foreach ($this->_elements as $key => $element) { + if ($p($key, $element)) { + return true; + } + } + return false; + } + + /** + * Searches for a given element and, if found, returns the corresponding key/index + * of that 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 mixed The key/index of the element or FALSE if the element was not found. + */ + public function indexOf($element) + { + return array_search($element, $this->_elements, true); + } + + /** + * Gets the element with the given key/index. + * + * @param mixed $key The key. + * @return mixed The element or NULL, if no element exists for the given key. + */ + public function get($key) + { + if (isset($this->_elements[$key])) { + return $this->_elements[$key]; + } + return null; + } + + /** + * Gets all keys/indexes of the collection elements. + * + * @return array + */ + public function getKeys() + { + return array_keys($this->_elements); + } + + /** + * Gets all elements. + * + * @return array + */ + public function getValues() + { + return array_values($this->_elements); + } + + /** + * Returns the number of elements in the collection. + * + * Implementation of the Countable interface. + * + * @return integer The number of elements in the collection. + */ + public function count() + { + return count($this->_elements); + } + + /** + * Adds/sets an element in the collection at the index / with the specified key. + * + * When the collection is a Map this is like put(key,value)/add(key,value). + * When the collection is a List this is like add(position,value). + * + * @param mixed $key + * @param mixed $value + */ + public function set($key, $value) + { + $this->_elements[$key] = $value; + } + + /** + * Adds an element to the collection. + * + * @param mixed $value + * @return boolean Always TRUE. + */ + public function add($value) + { + $this->_elements[] = $value; + return true; + } + + /** + * Checks whether the collection is empty. + * + * Note: This is preferable over count() == 0. + * + * @return boolean TRUE if the collection is empty, FALSE otherwise. + */ + public function isEmpty() + { + return ! $this->_elements; + } + + /** + * Gets an iterator for iterating over the elements in the collection. + * + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->_elements); + } + + /** + * 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) + { + return new static(array_map($func, $this->_elements)); + } + + /** + * 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) + { + return new static(array_filter($this->_elements, $p)); + } + + /** + * Applies the given predicate p to all elements of this collection, + * returning true, if the predicate yields true for all elements. + * + * @param Closure $p The predicate. + * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + public function forAll(Closure $p) + { + foreach ($this->_elements as $key => $element) { + if ( ! $p($key, $element)) { + return false; + } + } + + return true; + } + + /** + * 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) + { + $coll1 = $coll2 = array(); + foreach ($this->_elements as $key => $element) { + if ($p($key, $element)) { + $coll1[$key] = $element; + } else { + $coll2[$key] = $element; + } + } + return array(new static($coll1), new static($coll2)); + } + + /** + * Returns a string representation of this object. + * + * @return string + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + + /** + * Clears the collection. + */ + public function clear() + { + $this->_elements = array(); + } + + /** + * Extract 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 $length + * @return array + */ + public function slice($offset, $length = null) + { + return array_slice($this->_elements, $offset, $length, true); + } + + /** + * Select all elements from a selectable that match the criteria and + * return a new collection containing these elements. + * + * @param Criteria $criteria + * @return Collection + */ + 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()) { + $next = null; + foreach (array_reverse($orderings) as $field => $ordering) { + $next = ClosureExpressionVisitor::sortByField($field, $ordering == 'DESC' ? -1 : 1, $next); + } + + usort($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/common/lib/Doctrine/Common/Collections/Collection.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Collection.php new file mode 100644 index 0000000..51eb9e7 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Collection.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure, Countable, IteratorAggregate, ArrayAccess; + +/** + * 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 preferrable. + * 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. + */ + function add($element); + + /** + * Clears the collection, removing all elements. + */ + 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. + */ + function contains($element); + + /** + * Checks whether the collection is empty (contains no elements). + * + * @return boolean TRUE if the collection is empty, FALSE otherwise. + */ + 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. + */ + 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. + */ + 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. + */ + 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 + */ + 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. + */ + 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. + */ + 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. + */ + function set($key, $value); + + /** + * Gets a native PHP array representation of the collection. + * + * @return array + */ + function toArray(); + + /** + * Sets the internal iterator to the first element in the collection and + * returns this element. + * + * @return mixed + */ + function first(); + + /** + * Sets the internal iterator to the last element in the collection and + * returns this element. + * + * @return mixed + */ + function last(); + + /** + * Gets the key/index of the element at the current iterator position. + * + */ + function key(); + + /** + * Gets the element of the collection at the current iterator position. + * + */ + function current(); + + /** + * Moves the internal iterator position to the next element. + * + */ + 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. + */ + 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. + */ + function filter(Closure $p); + + /** + * Applies the given predicate p to all elements of this collection, + * returning true, if the predicate yields true for all elements. + * + * @param Closure $p The predicate. + * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + 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 + */ + 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. + */ + 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 mixed The key/index of the element or FALSE if the element was not found. + */ + function indexOf($element); + + /** + * Extract 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 $length + * @return array + */ + function slice($offset, $length = null); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Collections/Criteria.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Criteria.php new file mode 100644 index 0000000..3b05549 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Criteria.php @@ -0,0 +1,240 @@ +. + */ + +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 + */ + private static $expressionBuilder; + + /** + * @var \Doctrine\Common\Collections\Expr\Expression + */ + private $expression; + + /** + * @var array|null + */ + private $orderings; + + /** + * @var int + */ + private $firstResult; + + /** + * @var int + */ + private $maxResults; + + /** + * Creates an instance of the class. + * + * @return Criteria + */ + public static function create() + { + return new static(); + } + + /** + * Return the expression builder. + * + * @return \Doctrine\Common\Collections\ExpressionBuilder + */ + public static function expr() + { + if (self::$expressionBuilder === null) { + self::$expressionBuilder = new ExpressionBuilder(); + } + return self::$expressionBuilder; + } + + /** + * Construct new criteria + * + * @param Expression $expression + * @param array $orderings + * @param int $firstResult + * @param int $maxResults + */ + public function __construct(Expression $expression = null, array $orderings = null, $firstResult = null, $maxResults = null) + { + $this->expression = $expression; + $this->orderings = $orderings; + $this->firstResult = $firstResult; + $this->maxResults = $maxResults; + } + + /** + * Set the where expression to evaluate when this criteria is searched for. + * + * @param Expression + * @return Criteria + */ + public function where(Expression $expression) + { + $this->expression = $expression; + return $this; + } + + /** + * Append the where expression to evaluate when this criteria is searched for + * using an AND with previous expression. + * + * @param 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; + } + + /** + * Append the where expression to evaluate when this criteria is searched for + * using an OR with previous expression. + * + * @param 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; + } + + /** + * Get the expression attached to this criteria. + * + * @return Expression|null + */ + public function getWhereExpression() + { + return $this->expression; + } + + /** + * Get current orderings of this Criteria + * + * @return array + */ + public function getOrderings() + { + return $this->orderings; + } + + /** + * Set 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 array + * @return Criteria + */ + public function orderBy(array $orderings) + { + $this->orderings = $orderings; + return $this; + } + + /** + * Get current first result option of the critera. + * + * @return firstResult. + */ + public function getFirstResult() + { + return $this->firstResult; + } + + /** + * Set number of first result that this criteria should return. + * + * @param firstResult the value to set. + * @return Criteria + */ + public function setFirstResult($firstResult) + { + $this->firstResult = $firstResult; + return $this; + } + + /** + * Get maxResults. + * + * @return maxResults. + */ + public function getMaxResults() + { + return $this->maxResults; + } + + /** + * Set maxResults. + * + * @param maxResults the value to set. + * @return Criteria + */ + public function setMaxResults($maxResults) + { + $this->maxResults = $maxResults; + return $this; + } +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php new file mode 100644 index 0000000..06ccb04 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php @@ -0,0 +1,195 @@ +. + */ + +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 +{ + /** + * Access the field of a given object. This field has to be public directly + * or indirectly (through an accessor get* or a magic method, __get, __call). + * + * is*() is not supported. + * + * @return mixed + */ + static public function getObjectFieldValue($object, $field) + { + $accessor = "get" . $field; + + if (method_exists($object, $accessor) || 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 + */ + static public 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: + case Comparison::IS: + 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); + }; + + 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()); + } + } + + private function andExpressions($expressions) + { + return function ($object) use ($expressions) { + foreach ($expressions as $expression) { + if ( ! $expression($object)) { + return false; + } + } + return true; + }; + } + + 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/common/lib/Doctrine/Common/Collections/Expr/Comparison.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/Comparison.php new file mode 100644 index 0000000..29cfcff --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/Comparison.php @@ -0,0 +1,75 @@ +. + */ + +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 = 'IS'; + const IN = 'IN'; + const NIN = 'NIN'; + + private $field; + private $op; + private $value; + + public function __construct($field, $operator, $value) + { + if ( ! ($value instanceof Value)) { + $value = new Value($value); + } + + $this->field = $field; + $this->op = $operator; + $this->value = $value; + } + + public function getField() + { + return $this->field; + } + + public function getValue() + { + return $this->value; + } + + public function getOperator() + { + return $this->op; + } + + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkComparison($this); + } +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php new file mode 100644 index 0000000..fe917cf --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php @@ -0,0 +1,72 @@ +. + */ + +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'; + + private $type; + private $expressions = array(); + + 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; + } + } + + /** + * Return the list of expressions nested in this composite. + * + * @return Expression[] + */ + public function getExpressionList() + { + return $this->expressions; + } + + public function getType() + { + return $this->type; + } + + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkCompositeExpression($this); + } +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/Expression.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/Expression.php new file mode 100644 index 0000000..b0762ad --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/Expression.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Expression for the {@link Selectable} interface. + * + * @author Benjamin Eberlei + */ +interface Expression +{ + public function visit(ExpressionVisitor $visitor); +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php new file mode 100644 index 0000000..5e69b98 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php @@ -0,0 +1,81 @@ +. + */ + +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 +{ + /** + * Convert a comparison expression into the target query language output + * + * @param Comparison $comparison + * + * @return mixed + */ + abstract public function walkComparison(Comparison $comparison); + + /** + * Convert a value expression into the target query language part. + * + * @param Value $value + * + * @return mixed + */ + abstract public function walkValue(Value $value); + + /** + * Convert a composite expression into the target query language output + * + * @param CompositeExpression $expr + * + * @return mixed + */ + abstract public function walkCompositeExpression(CompositeExpression $expr); + + /** + * Dispatch walking an expression to the appropriate handler. + * + * @param Expression + * + * @return mixed + */ + 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/common/lib/Doctrine/Common/Collections/Expr/Value.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/Value.php new file mode 100644 index 0000000..f0df11a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Expr/Value.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +class Value implements Expression +{ + private $value; + + public function __construct($value) + { + $this->value = $value; + } + + public function getValue() + { + return $this->value; + } + + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkValue($this); + } +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Collections/ExpressionBuilder.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/ExpressionBuilder.php new file mode 100644 index 0000000..b53f0cd --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/ExpressionBuilder.php @@ -0,0 +1,149 @@ +. + */ + +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. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class ExpressionBuilder +{ + /** + * @return CompositeExpression + */ + public function andX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + /** + * @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 + * @param mixed $value + * + * @return Comparison + */ + public function isNull($field) + { + return new Comparison($field, Comparison::IS, new Value(null)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function in($field, array $values) + { + return new Comparison($field, Comparison::IN, new Value($values)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function notIn($field, array $values) + { + return new Comparison($field, Comparison::NIN, new Value($values)); + } +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Collections/Selectable.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Selectable.php new file mode 100644 index 0000000..675d6ea --- /dev/null +++ b/vendor/doctrine/common/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 +{ + /** + * Select all elements from a selectable that match the expression and + * return a new collection containing these elements. + * + * @param Criteria $criteria + * @return Collection + */ + function matching(Criteria $criteria); +} + 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..6db7675 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php @@ -0,0 +1,28 @@ +. + */ + +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..20d065e --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php @@ -0,0 +1,49 @@ +. + */ + + +namespace Doctrine\Common; + +/** + * Comparable interface that allows to compare two value objects to each other for similarity. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + */ +interface Comparable +{ + /** + * Compare 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..a87eee8 --- /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}. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EventArgs +{ + /** + * @var EventArgs Single instance of 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..25aac44 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php @@ -0,0 +1,147 @@ +. + */ + +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. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EventManager +{ + /** + * Map of registered listeners. + * => + * + * @var array + */ + private $_listeners = array(); + + /** + * 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 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 $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. + */ + 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 + */ + 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 he is + * interested in and added as a listener for these events. + * + * @param \Doctrine\Common\EventSubscriber $subscriber The subscriber. + */ + 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. + */ + 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..1458791 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php @@ -0,0 +1,45 @@ +. + */ + +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. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 + */ + 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..8e2554c --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php @@ -0,0 +1,266 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Base class for writing simple lexers, i.e. for creating small DSLs. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @todo Rename: AbstractLexer + */ +abstract class Lexer +{ + /** + * @var array Array of scanned tokens + */ + private $tokens = array(); + + /** + * @var integer Current lexer position in input string + */ + private $position = 0; + + /** + * @var integer Current peek of current lexer position + */ + private $peek = 0; + + /** + * @var array The next token in the input. + */ + public $lookahead; + + /** + * @var array The last matched/seen token. + */ + 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. + */ + public function setInput($input) + { + $this->tokens = array(); + $this->reset(); + $this->scan($input); + } + + /** + * Resets the lexer. + */ + public function reset() + { + $this->lookahead = null; + $this->token = null; + $this->peek = 0; + $this->position = 0; + } + + /** + * Resets the peek pointer to 0. + */ + 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 + */ + public function resetPosition($position = 0) + { + $this->position = $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. + * + * A 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 + * + * @return array|null the next token; null if there is no more tokens left + */ + 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. + */ + 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 + */ + protected function scan($input) + { + static $regex; + + if ( ! isset($regex)) { + $regex = '/(' . implode(')|(', $this->getCatchablePatterns()) . ')|' + . implode('|', $this->getNonCatchablePatterns()) . '/i'; + } + + $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; + } + + /** + * 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/common/lib/Doctrine/Common/NotifyPropertyChanged.php b/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php new file mode 100644 index 0000000..e32c0b9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Contract for classes that provide the service of notifying listeners of + * changes to their properties. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @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 + */ + 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..94fcd05 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php @@ -0,0 +1,259 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +use Doctrine\Common\Persistence\ManagerRegistry; + +/** + * Abstract implementation of the ManagerRegistry contract. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 name of the service + * @return object 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 name of the service + * @return void + */ + abstract protected function resetService($name); + + /** + * Get 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 = array(); + 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); + $class = $this->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + $proxyClass = new \ReflectionClass($class); + if ($proxyClass->implementsInterface($this->proxyInterfaceName)) { + $class = $proxyClass->getParentClass()->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 = array(); + 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..7d6f0cf --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract covering connection for a Doctrine persistence layer ManagerRegistry class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 + */ + function getDefaultConnectionName(); + + /** + * Gets the named connection. + * + * @param string $name The connection name (null for the default one) + * + * @return object + */ + function getConnection($name = null); + + /** + * Gets an array of all registered connections + * + * @return array An array of Connection instances + */ + function getConnections(); + + /** + * Gets all connection names. + * + * @return array An array of connection names + */ + 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..2fb7c47 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php @@ -0,0 +1,77 @@ +. +*/ + +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 $entity; + + /** + * Constructor + * + * @param object $entity + * @param ObjectManager $objectManager + */ + public function __construct($entity, ObjectManager $objectManager) + { + $this->entity = $entity; + $this->objectManager = $objectManager; + } + + /** + * Retrieve associated Entity. + * + * @return object + */ + public function getEntity() + { + return $this->entity; + } + + /** + * Retrieve 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..c014d73 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * 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; + } + + /** + * Retrieve associated ClassMetadata. + * + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->classMetadata; + } + + /** + * Retrieve 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..f139365 --- /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\Persistence\ObjectManager; + +/** + * Provides event arguments for the preFlush event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ManagerEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * Constructor. + * + * @param ObjectManager $objectManager + */ + public function __construct(ObjectManager $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * Retrieve 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..18b6554 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +/** + * Provides event arguments for the onClear event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnClearEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var \Doctrine\Common\Persistence\ObjectManager + */ + private $objectManager; + + /** + * @var string + */ + private $entityClass; + + /** + * Constructor. + * + * @param \Doctrine\Common\Persistence\ObjectManager $objectManager + * @param string $entityClass Optional entity class + */ + public function __construct($objectManager, $entityClass = null) + { + $this->objectManager = $objectManager; + $this->entityClass = $entityClass; + } + + /** + * Retrieve associated ObjectManager. + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } + + /** + * Name of the entity class that is cleared, or empty if all are cleared. + * + * @return string + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * Check if 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..86ac819 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php @@ -0,0 +1,133 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs, + 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; + } + + /** + * Retrieve entity changeset. + * + * @return array + */ + public function getEntityChangeSet() + { + return $this->entityChangeSet; + } + + /** + * Check if field has a changeset. + * + * @param string $field + * + * @return boolean + */ + public function hasChangedField($field) + { + return isset($this->entityChangeSet[$field]); + } + + /** + * Get 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]; + } + + /** + * Get 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]; + } + + /** + * Set the new value of this field. + * + * @param string $field + * @param mixed $value + */ + public function setNewValue($field, $value) + { + $this->assertValidField($field); + + $this->entityChangeSet[$field][1] = $value; + } + + /** + * Assert the field exists in changeset. + * + * @param string $field + * + * @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..bdb23bd --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract covering object managers for a Doctrine persistence layer ManagerRegistry class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 + */ + function getDefaultManagerName(); + + /** + * Gets a named object manager. + * + * @param string $name The object manager name (null for the default one) + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + function getManager($name = null); + + /** + * Gets an array of all registered object managers + * + * @return \Doctrine\Common\Persistence\ObjectManager[] An array of ObjectManager instances + */ + 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 $name The object manager name (null for the default one) + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + 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 + */ + function getAliasNamespace($alias); + + /** + * Gets all connection names. + * + * @return array An array of connection names + */ + 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 + */ + 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 + */ + 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..1ace1cc --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -0,0 +1,383 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use Doctrine\Common\Cache\Cache, + Doctrine\Common\Util\ClassUtils; + +/** + * 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 + */ + private $cacheDriver; + + /** + * @var array + */ + private $loadedMetadata = array(); + + /** + * @var bool + */ + protected $initialized = false; + + /** + * @var ReflectionService + */ + private $reflectionService; + + /** + * Sets the cache driver used by the factory to cache ClassMetadata instances. + * + * @param Doctrine\Common\Cache\Cache $cacheDriver + */ + 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 + */ + public function getCacheDriver() + { + return $this->cacheDriver; + } + + /** + * Return an array of all the loaded metadata currently in memory. + * + * @return array + */ + 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 = array(); + 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(); + + /** + * Get the fully qualified class-name from the namespace alias. + * + * @param string $namespaceAlias + * @param string $simpleClassName + * @return string + */ + abstract protected function getFqcnFromAlias($namespaceAlias, $simpleClassName); + + /** + * Return the mapping driver implementation. + * + * @return \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver + */ + abstract protected function getDriver(); + + /** + * Wakeup reflection after ClassMetadata gets unserialized from cache. + * + * @param ClassMetadata $class + * @param ReflectionService $reflService + * @return void + */ + abstract protected function wakeupReflection(ClassMetadata $class, ReflectionService $reflService); + + /** + * Initialize 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 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 \Doctrine\Common\Persistence\Mapping\ClassMetadata + */ + public function getMetadataFor($className) + { + if (isset($this->loadedMetadata[$className])) { + return $this->loadedMetadata[$className]; + } + + $realClassName = $className; + + // Check for namespace alias + if (strpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className); + $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } else { + $realClassName = ClassUtils::getRealClass($realClassName); + } + + if (isset($this->loadedMetadata[$realClassName])) { + // We do not have the alias name in the map, include it + $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + + return $this->loadedMetadata[$realClassName]; + } + + 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); + } + + 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 + */ + public function setMetadataFor($className, $class) + { + $this->loadedMetadata[$className] = $class; + } + + /** + * Get array of parent classes for the given entity class + * + * @param string $name + * @return array $parentClasses + */ + protected function getParentClasses($name) + { + // Collect parent classes, ignoring transient (not-mapped) classes. + $parentClasses = array(); + 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. + * + * @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 = array(); + + $parentClasses = $this->getParentClasses($name); + $parentClasses[] = $name; + + // Move down the hierarchy of parent classes, starting from the topmost class + $parent = null; + $rootEntityFound = false; + $visited = array(); + $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; + } + + /** + * Actually load the metadata from the underlying metadata + * + * @param ClassMetadata $class + * @param ClassMetadata|null $parent + * @param bool $rootEntityFound + * @param array $nonSuperclassParents classnames all parent classes 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); + + /** + * Check if this class is mapped by this Object Manager + ClassMetadata configuration + * + * @param $class + * @return bool + */ + public function isTransient($class) + { + if ( ! $this->initialized) { + $this->initialize(); + } + + // Check for namespace alias + if (strpos($class, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $class); + $class = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } + + return $this->getDriver()->isTransient($class); + } + + /** + * Set reflectionService. + * + * @param ReflectionService $reflectionService + */ + public function setReflectionService(ReflectionService $reflectionService) + { + $this->reflectionService = $reflectionService; + } + + /** + * Get 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..4836bf8 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php @@ -0,0 +1,165 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Contract for a Doctrine persistence layer ClassMetadata class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ClassMetadata +{ + /** + * Get fully-qualified class name of this persistent class. + * + * @return string + */ + function getName(); + + /** + * Gets the mapped identifier field name. + * + * The returned structure is an array of the identifier field names. + * + * @return array + */ + function getIdentifier(); + + /** + * Gets the ReflectionClass instance for this mapped class. + * + * @return \ReflectionClass + */ + function getReflectionClass(); + + /** + * Checks if the given field name is a mapped identifier for this class. + * + * @param string $fieldName + * @return boolean + */ + function isIdentifier($fieldName); + + /** + * Checks if the given field is a mapped property for this class. + * + * @param string $fieldName + * @return boolean + */ + function hasField($fieldName); + + /** + * Checks if the given field is a mapped association for this class. + * + * @param string $fieldName + * @return boolean + */ + function hasAssociation($fieldName); + + /** + * Checks if the given field is a mapped single valued association for this class. + * + * @param string $fieldName + * @return boolean + */ + function isSingleValuedAssociation($fieldName); + + /** + * Checks if the given field is a mapped collection valued association for this class. + * + * @param string $fieldName + * @return boolean + */ + 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 + */ + function getFieldNames(); + + /** + * Returns an array of identifier field names numerically indexed. + * + * @return array + */ + function getIdentifierFieldNames(); + + /** + * A numerically indexed list of association names of this persistent class. + * + * This array includes identifier associations if present on this class. + * + * @return array + */ + 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 + */ + function getTypeOfField($fieldName); + + /** + * Returns the target class name of the given association. + * + * @param string $assocName + * @return string + */ + function getAssociationTargetClass($assocName); + + /** + * Checks if the association is the inverse side of a bidirectional association + * + * @param string $assocName + * @return boolean + */ + function isAssociationInverseSide($assocName); + + /** + * Returns the target field of the owning side of the association + * + * @param string $assocName + * @return string + */ + function getAssociationMappedByTargetField($assocName); + + /** + * Return 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 + */ + 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..3fa39bc --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php @@ -0,0 +1,74 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Contract for a Doctrine persistence layer ClassMetadata class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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. + */ + function getAllMetadata(); + + /** + * Gets the class metadata descriptor for a class. + * + * @param string $className The name of the class. + * @return ClassMetadata + */ + 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. + */ + function hasMetadataFor($className); + + /** + * Sets the metadata descriptor for a specific class. + * + * @param string $className + * @param ClassMetadata $class + */ + function setMetadataFor($className, $class); + + /** + * 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 + */ + 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..1131add --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php @@ -0,0 +1,214 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Cache\ArrayCache, + Doctrine\Common\Annotations\AnnotationReader, + Doctrine\Common\Annotations\AnnotationRegistry, + 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 = array(); + + /** + * The file extension of mapping documents. + * + * @var string + */ + protected $fileExtension = '.php'; + + /** + * Cache for AnnotationDriver#getAllClassNames() + * + * @var array + */ + protected $classNames; + + /** + * Name of the entity annotations as keys + * + * @var array + */ + protected $entityAnnotationClasses = array(); + + /** + * 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 $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); + } + } + + /** + * Append lookup paths to metadata driver. + * + * @param array $paths + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieve the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Retrieve the current annotation reader + * + * @return AnnotationReader + */ + public function getReader() + { + return $this->reader; + } + + /** + * Get the file extension used to look for mapping files under + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Set 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; + } + + /** + * 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 = array(); + $includedFiles = array(); + + 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 + ), + '/^.+' . str_replace('.', '\.', $this->fileExtension) . '$/i', + \RecursiveRegexIterator::GET_MATCH + ); + + foreach ($iterator as $file) { + $sourceFile = realpath($file[0]); + + 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..0d61174 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php @@ -0,0 +1,170 @@ +. +*/ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * Locate the file that contains the metadata information for a given class name. + * + * This behavior is inpependent 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 = array(); + + /** + * The file extension of mapping documents. + * + * @var string + */ + 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 + */ + public function __construct($paths, $fileExtension = null) + { + $this->addPaths((array) $paths); + $this->fileExtension = $fileExtension; + } + + /** + * Append lookup paths to metadata driver. + * + * @param array $paths + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieve the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Get the file extension used to look for mapping files under + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Set 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 findMappingFile($className) + { + $fileName = str_replace('\\', '.', $className) . $this->fileExtension; + + // Check whether file exists + foreach ($this->paths as $path) { + if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) { + return $path . DIRECTORY_SEPARATOR . $fileName; + } + } + + throw MappingException::mappingFileNotFound($className, $fileName); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename) + { + $classes = array(); + + 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 (file_exists($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..b0a7685 --- /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 '.'. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @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 + */ + protected $classCache; + + /** + * @var string + */ + 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 $fileExtension + */ + public function __construct($locator, $fileExtension = null) + { + if ($locator instanceof FileLocator) { + $this->locator = $locator; + } else { + $this->locator = new DefaultFileLocator((array)$locator, $fileExtension); + } + } + + /** + * Set global basename + * + * @param string $file + */ + public function setGlobalBasename($file) + { + $this->globalBasename = $file; + } + + /** + * Retrieve global basename + * + * @return string + */ + public function getGlobalBasename() + { + return $this->globalBasename; + } + + /** + * Get 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 + * + * @throws MappingException + * @return array The element of schema meta data + */ + 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]; + } + + /** + * 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) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return false; + } + + return !$this->locator->fileExists($className); + } + + /** + * 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() + { + if ($this->classCache === null) { + $this->initialize(); + } + + $classNames = (array)$this->locator->getAllClassNames($this->globalBasename); + if ($this->classCache) { + $classNames = array_merge(array_keys($this->classCache), $classNames); + } + return $classNames; + } + + /** + * 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); + + /** + * Initialize 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 = array(); + 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) + ); + } + } + } + } + + /** + * Retrieve the locator used to discover mapping files by className + * + * @return FileLocator + */ + public function getLocator() + { + return $this->locator; + } + + /** + * Set 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..ec2b606 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php @@ -0,0 +1,71 @@ +. +*/ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +/** + * Locate 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 +{ + /** + * Locate mapping file for the given class name. + * + * @param string $className + * @return string + */ + function findMappingFile($className); + + /** + * Get all class names that are found with this file locator. + * + * @param string $globalBasename Passed to allow excluding the basename + * @return array + */ + function getAllClassNames($globalBasename); + + /** + * Check if a file can be found for this class name. + * + * @param string $className + * + * @return bool + */ + function fileExists($className); + + /** + * Get all the paths that this file locator looks for mapping files. + * + * @return array + */ + function getPaths(); + + /** + * Get the file extension that mapping files are suffixed with. + * + * @return string + */ + 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..955d831 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php @@ -0,0 +1,56 @@ +. + */ + +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 + */ + 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. + */ + function getAllClassNames(); + + /** + * 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 + */ + 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..3b1049d --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php @@ -0,0 +1,168 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver, + Doctrine\Common\Persistence\Mapping\ClassMetadata, + 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 + */ + private $defaultDriver; + + /** + * @var array + */ + private $drivers = array(); + + /** + * Get the default driver. + * + * @return MappingDriver|null + */ + public function getDefaultDriver() + { + return $this->defaultDriver; + } + + /** + * Set the default driver. + * + * @param MappingDriver $driver + */ + public function setDefaultDriver(MappingDriver $driver) + { + $this->defaultDriver = $driver; + } + + /** + * Add a nested driver. + * + * @param MappingDriver $nestedDriver + * @param string $namespace + */ + public function addDriver(MappingDriver $nestedDriver, $namespace) + { + $this->drivers[$namespace] = $nestedDriver; + } + + /** + * Get the array of nested drivers. + * + * @return array $drivers + */ + public function getDrivers() + { + return $this->drivers; + } + + /** + * Loads the metadata for the specified class into the provided container. + * + * @param string $className + * @param ClassMetadata $metadata + * + * @throws MappingException + */ + 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)); + } + + /** + * 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() + { + $classNames = array(); + $driverClasses = array(); + + /* @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; + } + } + } + + return array_keys($classNames); + } + + /** + * Whether the class with the specified name should have its metadata loaded. + * + * This is only the case for non-transient classes either mapped as an Entity or MappedSuperclass. + * + * @param string $className + * @return boolean + */ + 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..e0c8611 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php @@ -0,0 +1,72 @@ +. + */ + +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 + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class PHPDriver extends FileDriver +{ + /** + * {@inheritdoc} + */ + protected $metadata; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = null) + { + $fileExtension = ".php"; + parent::__construct($locator, $fileExtension); + } + + /** + * {@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 array($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..e3cea73 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php @@ -0,0 +1,141 @@ +. + */ + +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. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 = array(); + + /** + * Map of all class names. + * + * @var array + */ + private $classNames; + + /** + * Constructor + * + * @param array|string $paths + */ + public function __construct($paths) + { + $this->addPaths((array) $paths); + } + + /** + * Add paths + * + * @param array $paths + */ + 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 = array(); + $includedFiles = array(); + + 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..9095187 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php @@ -0,0 +1,214 @@ +. +*/ + +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 = array(); + + /** + * A map of mapping directory path to namespace prefix used to expand class shortnames. + * + * @var array + */ + protected $prefixes = array(); + + /** + * File extension that is searched for. + * + * @var string + */ + protected $fileExtension; + + /** + * Constructor + * + * @param array $prefixes + * @param string|null $fileExtension + */ + public function __construct(array $prefixes, $fileExtension = null) + { + $this->addNamespacePrefixes($prefixes); + $this->fileExtension = $fileExtension; + } + + /** + * Add Namespace Prefixes + * + * @param array $prefixes + */ + public function addNamespacePrefixes(array $prefixes) + { + $this->prefixes = array_merge($this->prefixes, $prefixes); + $this->paths = array_merge($this->paths, array_keys($prefixes)); + } + + /** + * Get Namespace Prefixes + * + * @return array + */ + public function getNamespacePrefixes() + { + return $this->prefixes; + } + + /** + * {@inheritDoc} + */ + public function getPaths() + { + return $this->paths; + } + + /** + * {@inheritDoc} + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Set 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('\\', '.', $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->fileExtension; + return is_file($filename); + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename = null) + { + $classes = array(); + + 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])) { + $classes[] = $this->prefixes[$path].'\\'.str_replace('.', '\\', $fileName); + } else { + $classes[] = str_replace('.', '\\', $fileName); + } + } + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile($className) + { + $defaultFileName = str_replace('\\', '.', $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->fileExtension; + if (is_file($filename)) { + return $filename; + } + + throw MappingException::mappingFileNotFound($className, $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..c1e7ad5 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php @@ -0,0 +1,86 @@ +. + */ + +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 MappingException + */ + public static function classNotFoundInNamespaces($className, $namespaces) + { + return new self("The class '" . $className . "' was not found in the ". + "chain configured namespaces " . implode(", ", $namespaces)); + } + + /** + * @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|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!' + ); + } + + /** + * @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'."); + } + + /** + * @param string $entityName + * @param string $fileName + * @return MappingException + */ + public static function invalidMappingFile($entityName, $fileName) + { + return new self("Invalid mapping file '$fileName' for class '$entityName'."); + } +} 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..3db85d9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php @@ -0,0 +1,80 @@ +. + */ + +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 +{ + /** + * Return an array of the parent classes (not interfaces) for the given class. + * + * @param string $class + * @return array + */ + function getParentClasses($class); + + /** + * Return the shortname of a class. + * + * @param string $class + * @return string + */ + function getClassShortName($class); + + /** + * @param string $class + * @return string + */ + function getClassNamespace($class); + + /** + * Return a reflection class instance or null + * + * @param string $class + * @return \ReflectionClass|null + */ + function getClass($class); + + /** + * Return an accessible property (setAccessible(true)) or null. + * + * @param string $class + * @param string $property + * @return \ReflectionProperty|null + */ + function getAccessibleProperty($class, $property); + + /** + * Check if the class have a public method with the given name. + * + * @param mixed $class + * @param mixed $method + * @return bool + */ + 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..77b9e76 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use ReflectionClass; +use ReflectionProperty; + +/** + * PHP Runtime Reflection Service + * + * @author Benjamin Eberlei + */ +class RuntimeReflectionService implements ReflectionService +{ + /** + * Return an array of the parent classes (not interfaces) for the given class. + * + * @param string $class + * @return array + */ + public function getParentClasses($class) + { + return class_parents($class); + } + + /** + * Return the shortname of a class. + * + * @param string $class + * @return string + */ + public function getClassShortName($class) + { + $r = new ReflectionClass($class); + return $r->getShortName(); + } + + /** + * @param string $class + * @return string + */ + public function getClassNamespace($class) + { + $r = new ReflectionClass($class); + return $r->getNamespaceName(); + } + + /** + * Return a reflection class instance or null + * + * @param string $class + * @return ReflectionClass|null + */ + public function getClass($class) + { + return new ReflectionClass($class); + } + + /** + * Return an accessible property (setAccessible(true)) or null. + * + * @param string $class + * @param string $property + * @return ReflectionProperty|null + */ + public function getAccessibleProperty($class, $property) + { + $property = new ReflectionProperty($class, $property); + $property->setAccessible(true); + return $property; + } + + /** + * Check if the class have a public method with the given name. + * + * @param mixed $class + * @param mixed $method + * @return bool + */ + public function hasPublicMethod($class, $method) + { + return method_exists($class, $method) && is_callable(array($class, $method)); + } +} + 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..4f6d1cf --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use ReflectionClass; +use ReflectionProperty; + +/** + * PHP Runtime Reflection Service + * + * @author Benjamin Eberlei + */ +class StaticReflectionService implements ReflectionService +{ + /** + * Return an array of the parent classes (not interfaces) for the given class. + * + * @param string $class + * @return array + */ + public function getParentClasses($class) + { + return array(); + } + + /** + * Return the shortname of a class. + * + * @param string $className + * @return string + */ + public function getClassShortName($className) + { + if (strpos($className, '\\') !== false) { + $className = substr($className, strrpos($className, "\\")+1); + } + return $className; + } + + /** + * Return the namespace of a class. + * + * @param string $className + * @return string + */ + public function getClassNamespace($className) + { + $namespace = ''; + if (strpos($className, '\\') !== false) { + $namespace = strrev(substr( strrev($className), strpos(strrev($className), '\\')+1 )); + } + return $namespace; + } + + /** + * Return a reflection class instance or null + * + * @param string $class + * @return ReflectionClass|null + */ + public function getClass($class) + { + return null; + } + + /** + * Return an accessible property (setAccessible(true)) or null. + * + * @param string $class + * @param string $property + * @return ReflectionProperty|null + */ + public function getAccessibleProperty($class, $property) + { + return null; + } + + /** + * Check if the class have a public method with the given name. + * + * @param mixed $class + * @param mixed $method + * @return bool + */ + public function hasPublicMethod($class, $method) + { + return method_exists($class, $method) && is_callable(array($class, $method)); + } +} + 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..2bb8722 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php @@ -0,0 +1,152 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract for a Doctrine persistence layer ObjectManager class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ObjectManager +{ + /** + * Finds a object by its identifier. + * + * This is just a convenient shortcut for getRepository($className)->find($id). + * + * @param string + * @param mixed + * @return object + */ + 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. + */ + 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. + */ + 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 + */ + function merge($object); + + /** + * Clears the ObjectManager. All objects that are currently managed + * by this ObjectManager become detached. + * + * @param string $objectName if given, only objects of this type will get detached + */ + 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. + */ + 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. + */ + 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. + */ + function flush(); + + /** + * Gets the repository for a class. + * + * @param string $className + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + 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 + */ + function getClassMetadata($className); + + /** + * Gets the metadata factory used to gather the metadata of classes. + * + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory + */ + 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 + */ + function initializeObject($obj); + + /** + * Check if the object is part of the current UnitOfWork and therefore + * managed. + * + * @param object $object + * @return bool + */ + 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..69fba78 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php @@ -0,0 +1,49 @@ +. + */ + +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 persistance-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 + */ + public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata); +} 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..9a3e5b6 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract for a Doctrine persistence layer ObjectRepository class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 int $id The identifier. + * @return object The object. + */ + function find($id); + + /** + * Finds all objects in the repository. + * + * @return mixed The objects. + */ + 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. + * + * @throws \UnexpectedValueException + * @param array $criteria + * @param array|null $orderBy + * @param int|null $limit + * @param int|null $offset + * @return mixed The objects. + */ + function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null); + + /** + * Finds a single object by a set of criteria. + * + * @param array $criteria + * @return object The object. + */ + function findOneBy(array $criteria); + + /** + * Returns the class name of the object managed by the repository + * + * @return string + */ + 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..9fcc4cb --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php @@ -0,0 +1,244 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; + +/** + * 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 aswell. 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 + */ + private static $objectManager; + + /** + * @var ClassMetadata + */ + private $cm; + + /** + * Set the object manager responsible for all persistent object base classes. + * + * @param ObjectManager $objectManager + */ + static public function setObjectManager(ObjectManager $objectManager = null) + { + self::$objectManager = $objectManager; + } + + /** + * @return ObjectManager + */ + static public function getObjectManager() + { + return self::$objectManager; + } + + /** + * Inject Doctrine Object Manager + * + * @param ObjectManager $objectManager + * @param ClassMetadata $classMetadata + * + * @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 + * + * @throws \BadMethodCallException - When no persistent field exists by that name. + * @throws \InvalidArgumentException - When the wrong target object type is passed to an association + * @return void + */ + 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()."'"); + } + } + + /** + * Get persistent field value. + * + * + * @param string $field + * + * @throws \BadMethodCallException - When no persistent field exists by that name. + * @return mixed + */ + 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 complete the owning side. + * + * @param string $field + * @param ClassMetadata $targetClass + * @param object $targetObject + */ + private function completeOwningSide($field, $targetClass, $targetObject) + { + // add this object on the owning side aswell, 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); + } + } + + /** + * Add an object to a collection + * + * @param string $field + * @param array $args + * + * @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 ?: array()); + } + $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()); + } + } + + /** + * Initialize Doctrine Metadata for this class. + * + * @throws \RuntimeException + * @return void + */ + 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 method that implements + * + * @param string $method + * @param array $args + * + * @throws \BadMethodCallException + * @return mixed + */ + 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..e25598c --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php @@ -0,0 +1,60 @@ +. + */ + +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 int + */ + const MARKER_LENGTH = 6; + + /** + * Initialize this proxy if its not yet initialized. + * + * Acts as a no-op if already initialized. + * + * @return void + */ + public function __load(); + + /** + * Is this proxy 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..1171874 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Contract for classes that are potential listeners of a NotifyPropertyChanged + * implementor. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @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. + */ + function propertyChanged($sender, $propertyName, $oldValue, $newValue); +} + 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..ae69607 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ClassFinderInterface.php @@ -0,0 +1,38 @@ +. + */ + +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 + * 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..b6a5fd1 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/Psr0FindFile.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +/** + * Finds a class in a PSR-0 structure. + * + * @author Karoly Negyesi + */ +class Psr0FindFile implements ClassFinderInterface +{ + /** + * The PSR-0 prefixes. + * + * @var string + */ + protected $prefixes; + + /** + * @param string $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; + } + + /** + * Finds a class. + * + * @param string $class The name of the class. + * + * @return + * The name of the class or NULL if not found. + */ + 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 (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + } + } + } +} 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..a436a2d --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +interface ReflectionProviderInterface +{ + /** + * Get the ReflectionClass equivalent for this class. + * + * @return ReflectionClass + */ + public function getReflectionClass(); + + /** + * Get the ReflectionClass equivalent for this class. + * + * @return ReflectionMethod + */ + public function getReflectionMethod($name); + + /** + * Get the ReflectionClass equivalent for this class. + * + * @return ReflectionMethod + */ + public function getReflectionProperty($name); +} 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..12e45d5 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionClass.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionClass; +use ReflectionException; + +class StaticReflectionClass extends ReflectionClass +{ + /** + * The static reflection parser object. + * + * @var StaticReflectionParser + */ + private $staticReflectionParser; + + public function __construct(StaticReflectionParser $staticReflectionParser) + { + $this->staticReflectionParser = $staticReflectionParser; + } + + public function getName() + { + return $this->staticReflectionParser->getClassName(); + } + + public function getDocComment() + { + return $this->staticReflectionParser->getDocComment(); + } + + public function getNamespaceName() + { + return $this->staticReflectionParser->getNamespaceName(); + } + + public function getUseStatements() + { + return $this->staticReflectionParser->getUseStatements(); + } + + public function getMethod($name) + { + return $this->staticReflectionParser->getReflectionMethod($name); + } + + public function getProperty($name) + { + return $this->staticReflectionParser->getReflectionProperty($name); + } + + public static function export($argument, $return = false) { throw new ReflectionException('Method not implemented'); } + public function getConstant($name) { throw new ReflectionException('Method not implemented'); } + public function getConstants() { throw new ReflectionException('Method not implemented'); } + public function getConstructor() { throw new ReflectionException('Method not implemented'); } + public function getDefaultProperties() { throw new ReflectionException('Method not implemented'); } + public function getEndLine() { throw new ReflectionException('Method not implemented'); } + public function getExtension() { throw new ReflectionException('Method not implemented'); } + public function getExtensionName() { throw new ReflectionException('Method not implemented'); } + public function getFileName() { throw new ReflectionException('Method not implemented'); } + public function getInterfaceNames() { throw new ReflectionException('Method not implemented'); } + public function getInterfaces() { throw new ReflectionException('Method not implemented'); } + public function getMethods($filter = NULL) { throw new ReflectionException('Method not implemented'); } + public function getModifiers() { throw new ReflectionException('Method not implemented'); } + public function getParentClass() { throw new ReflectionException('Method not implemented'); } + public function getProperties($filter = NULL) { throw new ReflectionException('Method not implemented'); } + public function getShortName() { throw new ReflectionException('Method not implemented'); } + public function getStartLine() { throw new ReflectionException('Method not implemented'); } + public function getStaticProperties() { throw new ReflectionException('Method not implemented'); } + public function getStaticPropertyValue($name, $default = '') { throw new ReflectionException('Method not implemented'); } + public function getTraitAliases() { throw new ReflectionException('Method not implemented'); } + public function getTraitNames() { throw new ReflectionException('Method not implemented'); } + public function getTraits() { throw new ReflectionException('Method not implemented'); } + public function hasConstant($name) { throw new ReflectionException('Method not implemented'); } + public function hasMethod($name) { throw new ReflectionException('Method not implemented'); } + public function hasProperty($name) { throw new ReflectionException('Method not implemented'); } + public function implementsInterface($interface) { throw new ReflectionException('Method not implemented'); } + public function inNamespace() { throw new ReflectionException('Method not implemented'); } + public function isAbstract() { throw new ReflectionException('Method not implemented'); } + public function isCloneable() { throw new ReflectionException('Method not implemented'); } + public function isFinal() { throw new ReflectionException('Method not implemented'); } + public function isInstance($object) { throw new ReflectionException('Method not implemented'); } + public function isInstantiable() { throw new ReflectionException('Method not implemented'); } + public function isInterface() { throw new ReflectionException('Method not implemented'); } + public function isInternal() { throw new ReflectionException('Method not implemented'); } + public function isIterateable() { throw new ReflectionException('Method not implemented'); } + public function isSubclassOf($class) { throw new ReflectionException('Method not implemented'); } + public function isTrait() { throw new ReflectionException('Method not implemented'); } + public function isUserDefined() { throw new ReflectionException('Method not implemented'); } + public function newInstance($args) { throw new ReflectionException('Method not implemented'); } + public function newInstanceArgs(array $args = array()) { throw new ReflectionException('Method not implemented'); } + public function newInstanceWithoutConstructor() { throw new ReflectionException('Method not implemented'); } + public function setStaticPropertyValue($name, $value) { throw new ReflectionException('Method not implemented'); } + 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..6482036 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionMethod; +use ReflectionException; + +class StaticReflectionMethod extends ReflectionMethod +{ + /** + * The PSR-0 parser object. + * + * @var StaticReflectionParser + */ + protected $staticReflectionParser; + + /** + * The name of the method. + * + * @var string + */ + protected $methodName; + + public function __construct(StaticReflectionParser $staticReflectionParser, $methodName) + { + $this->staticReflectionParser = $staticReflectionParser; + $this->methodName = $methodName; + } + public function getName() + { + return $this->methodName; + } + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('method', $this->methodName); + } + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + public function getNamespaceName() + { + return $this->getStaticReflectionParser()->getNamespaceName(); + } + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('method', $this->methodName); + } + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + public static function export($class, $name, $return = false) { throw new ReflectionException('Method not implemented'); } + public function getClosure($object) { throw new ReflectionException('Method not implemented'); } + public function getModifiers() { throw new ReflectionException('Method not implemented'); } + public function getPrototype() { throw new ReflectionException('Method not implemented'); } + public function invoke($object, $parameter = NULL) { throw new ReflectionException('Method not implemented'); } + public function invokeArgs($object, array $args) { throw new ReflectionException('Method not implemented'); } + public function isAbstract() { throw new ReflectionException('Method not implemented'); } + public function isConstructor() { throw new ReflectionException('Method not implemented'); } + public function isDestructor() { throw new ReflectionException('Method not implemented'); } + public function isFinal() { throw new ReflectionException('Method not implemented'); } + public function isPrivate() { throw new ReflectionException('Method not implemented'); } + public function isProtected() { throw new ReflectionException('Method not implemented'); } + public function isPublic() { throw new ReflectionException('Method not implemented'); } + public function isStatic() { throw new ReflectionException('Method not implemented'); } + public function setAccessible($accessible) { throw new ReflectionException('Method not implemented'); } + public function __toString() { throw new ReflectionException('Method not implemented'); } + public function getClosureThis() { throw new ReflectionException('Method not implemented'); } + public function getEndLine() { throw new ReflectionException('Method not implemented'); } + public function getExtension() { throw new ReflectionException('Method not implemented'); } + public function getExtensionName() { throw new ReflectionException('Method not implemented'); } + public function getFileName() { throw new ReflectionException('Method not implemented'); } + public function getNumberOfParameters() { throw new ReflectionException('Method not implemented'); } + public function getNumberOfRequiredParameters() { throw new ReflectionException('Method not implemented'); } + public function getParameters() { throw new ReflectionException('Method not implemented'); } + public function getShortName() { throw new ReflectionException('Method not implemented'); } + public function getStartLine() { throw new ReflectionException('Method not implemented'); } + public function getStaticVariables() { throw new ReflectionException('Method not implemented'); } + public function inNamespace() { throw new ReflectionException('Method not implemented'); } + public function isClosure() { throw new ReflectionException('Method not implemented'); } + public function isDeprecated() { throw new ReflectionException('Method not implemented'); } + public function isInternal() { throw new ReflectionException('Method not implemented'); } + public function isUserDefined() { throw new ReflectionException('Method not implemented'); } + 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..7f3e41f --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php @@ -0,0 +1,282 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionException; +use Doctrine\Common\Annotations\TokenParser; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Karoly Negyesi + */ +class StaticReflectionParser implements ReflectionProviderInterface +{ + + /** + * The name of the class. + * + * @var string + */ + protected $className; + + /** + * TRUE if the caller only wants class annotations. + * + * @var boolean. + */ + protected $classAnnotationOptimize; + + /** + * TRUE when the parser has ran. + * + * @var boolean + */ + protected $parsed = false; + + /** + * The namespace of the class + * + * @var string + */ + protected $namespace = ''; + + /** + * The use statements of this class. + * + * @var array + */ + protected $useStatements = array(); + + /** + * The docComment of the class. + * + * @var string + */ + protected $docComment = array( + 'class' => '', + 'property' => array(), + 'method' => array(), + ); + + /** + * The name of the class this class extends, if any. + * + * @var string + */ + protected $parentClassName = ''; + + /** + * The parent PSR-0 Parser. + * + * @var \Doctrine\Common\Annotations\StaticReflectionParser + */ + protected $parentStaticReflectionParser; + + /** + * Parses a class residing in a PSR-0 hierarchy. + * + * @param string $class + * The full, namespaced class name. + * @param ClassFinder $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, '\\'); + if ($lastNsPos = strrpos($this->className, '\\')) { + $this->namespace = substr($this->className, 0, $lastNsPos); + } + $this->finder = $finder; + $this->classAnnotationOptimize = $classAnnotationOptimize; + } + + 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+$className\s+{/sm", $contents, $matches)) { + $contents = $matches[1]; + } + } + $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; + } + } + } + } + + protected function getParentStaticReflectionParser() + { + if (empty($this->parentStaticReflectionParser)) { + $this->parentStaticReflectionParser = new static($this->parentClassName, $this->finder); + } + + return $this->parentStaticReflectionParser; + } + + public function getClassName() + { + return $this->className; + } + + public function getNamespaceName() + { + return $this->namespace; + } + + /** + * Get the ReflectionClass equivalent for this file / class. + */ + public function getReflectionClass() + { + return new StaticReflectionClass($this); + } + + /** + * Get the ReflectionMethod equivalent for the method of this file / class. + */ + public function getReflectionMethod($methodName) + { + return new StaticReflectionMethod($this, $methodName); + } + + /** + * Get the ReflectionProperty equivalent for the method of this file / class. + */ + public function getReflectionProperty($propertyName) + { + return new StaticReflectionProperty($this, $propertyName); + } + + /** + * Get the use statements from this file. + */ + public function getUseStatements() + { + $this->parse(); + + return $this->useStatements; + } + + /** + * Get docComment. + * + * @param string $type class, property or method. + * @param string $name Name of the property or method, not needed for class. + * + * @return string the doc comment or empty string if none. + */ + public function getDocComment($type = 'class', $name = '') + { + $this->parse(); + + return $name ? $this->docComment[$type][$name] : $this->docComment[$type]; + } + + /** + * Get the PSR-0 parser for the declaring class. + * + * @param string $type property or method. + * @param string $name Name of the property or method. + * + * @return StaticReflectionParser A static reflection parser for the declaring class. + */ + 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..7c6411a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php @@ -0,0 +1,77 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionProperty; +use ReflectionException; + +class StaticReflectionProperty extends ReflectionProperty +{ + /** + * The PSR-0 parser object. + * + * @var StaticReflectionParser + */ + protected $staticReflectionParser; + + /** + * The name of the property. + * + * @var string + */ + protected $propertyName; + + public function __construct(StaticReflectionParser $staticReflectionParser, $propertyName) + { + $this->staticReflectionParser = $staticReflectionParser; + $this->propertyName = $propertyName; + } + public function getName() + { + return $this->propertyName; + } + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('property', $this->propertyName); + } + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('property', $this->propertyName); + } + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + public static function export ($class, $name, $return = false) { throw new ReflectionException('Method not implemented'); } + public function getModifiers() { throw new ReflectionException('Method not implemented'); } + public function getValue($object = NULL) { throw new ReflectionException('Method not implemented'); } + public function isDefault() { throw new ReflectionException('Method not implemented'); } + public function isPrivate() { throw new ReflectionException('Method not implemented'); } + public function isProtected() { throw new ReflectionException('Method not implemented'); } + public function isPublic() { throw new ReflectionException('Method not implemented'); } + public function isStatic() { throw new ReflectionException('Method not implemented'); } + public function setAccessible ($accessible) { throw new ReflectionException('Method not implemented'); } + public function setValue ($object, $value = NULL) { throw new ReflectionException('Method not implemented'); } + 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..078a8db --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php @@ -0,0 +1,103 @@ +. + */ + +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 +{ + /** + * Get the real class name of a class name that could be a proxy. + * + * @param string + * @return string + */ + public static function getRealClass($class) + { + if (false === $pos = strrpos($class, '\\'.Proxy::MARKER.'\\')) { + return $class; + } + + return substr($class, $pos + Proxy::MARKER_LENGTH + 2); + } + + /** + * Get the real class name of an object (even if its a proxy) + * + * @param object + * @return string + */ + public static function getClass($object) + { + return self::getRealClass(get_class($object)); + } + + /** + * Get the real parent class name of a class or object + * + * @param string + * @return string + */ + public static function getParentClass($className) + { + return get_parent_class( self::getRealClass( $className ) ); + } + + /** + * Create a new reflection class + * + * @param string + * @return \ReflectionClass + */ + public static function newReflectionClass($class) + { + return new \ReflectionClass( self::getRealClass( $class ) ); + } + + /** + * Create a new reflection object + * + * @param object + * @return \ReflectionObject + */ + public static function newReflectionObject($object) + { + return self::newReflectionClass( self::getClass( $object ) ); + } + + /** + * Given a class name and a proxy namespace return 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..458e5c8 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php @@ -0,0 +1,135 @@ +. + */ + +namespace Doctrine\Common\Util; + +/** + * Static class containing most used debug methods. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 from instantiation) + * + */ + private function __construct() {} + + /** + * Prints a dump of the public, protected and private properties of $var. + * + * @link http://xdebug.org/ + * @param mixed $var + * @param integer $maxDepth Maximum nesting level for object properties + * @param boolean $stripTags Flag that indicate if output should strip HTML tags + */ + public static function dump($var, $maxDepth = 2, $stripTags = true) + { + ini_set('html_errors', 'On'); + + 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(); + + echo ($stripTags ? strip_tags(html_entity_decode($dump)) : $dump); + + ini_set('html_errors', 'Off'); + } + + /** + * Export + * + * @param mixed $var + * @param int $maxDepth + * @return mixed + */ + public static function export($var, $maxDepth) + { + $return = null; + $isObj = is_object($var); + + if ($isObj && in_array('Doctrine\Common\Collections\Collection', class_implements($var))) { + $var = $var->toArray(); + } + + if ($maxDepth) { + if (is_array($var)) { + $return = array(); + + 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 \Doctrine\Common\Persistence\Proxy) { + $return->__IS_PROXY__ = true; + $return->__PROXY_INITIALIZED__ = $var->__isInitialized(); + } + + 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; + } + + /** + * Convert to string + * + * @param object $obj + * @return string + */ + public static function toString($obj) + { + return method_exists('__toString', $obj) ? (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..214ed57 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\Common\Util; + +/** + * 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 + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 1.0 + * @version $Revision: 3189 $ + * @author Konsta Vesterinen + * @author Jonathan H. Wage + */ +class Inflector +{ + /** + * Convert word in to the format for a Doctrine table name. Converts 'ModelName' to 'model_name' + * + * @param string $word Word to tableize + * @return string $word Tableized word + */ + public static function tableize($word) + { + return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word)); + } + + /** + * Convert a word in to the format for a Doctrine class name. Converts 'table_name' to 'TableName' + * + * @param string $word Word to classify + * @return string $word Classified word + */ + public static function classify($word) + { + return str_replace(" ", "", ucwords(strtr($word, "_-", " "))); + } + + /** + * Camelize a word. This uses the classify() method and turns the first character to lowercase + * + * @param string $word + * @return string $word + */ + public static function camelize($word) + { + return lcfirst(self::classify($word)); + } +} 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..cca4894 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Version.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Class to store and retrieve the version of Doctrine + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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.3.0'; + + /** + * 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/doctrine/common/phpunit.xml.dist b/vendor/doctrine/common/phpunit.xml.dist new file mode 100644 index 0000000..b9d3b34 --- /dev/null +++ b/vendor/doctrine/common/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + + + + performance + + + 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..c88f9e6 --- /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) +* 2.2: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=2.2)](http://travis-ci.org/doctrine/dbal) +* 2.1.x: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=2.1.x)](http://travis-ci.org/doctrine/dbal) + +## More resources: + +* [Website](http://www.doctrine-project.org) +* [Documentation](http://www.doctrine-project.org/projects/dbal/current/docs/en) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DBAL) +* [Downloads](http://github.com/doctrine/dbal/downloads) diff --git a/vendor/doctrine/dbal/UPGRADE b/vendor/doctrine/dbal/UPGRADE new file mode 100644 index 0000000..36ca80e --- /dev/null +++ b/vendor/doctrine/dbal/UPGRADE @@ -0,0 +1,148 @@ +# 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\Connnection#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 seperates 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/composer.json b/vendor/doctrine/dbal/composer.json new file mode 100644 index 0000000..22f9d53 --- /dev/null +++ b/vendor/doctrine/dbal/composer.json @@ -0,0 +1,26 @@ +{ + "name": "doctrine/dbal", + "type": "library","version":"2.3.4", + "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.3.0,<2.5-dev" + }, + "autoload": { + "psr-0": { "Doctrine\\DBAL": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + } +} diff --git a/vendor/doctrine/dbal/docs/design/AZURE_FEDERATIONS.md b/vendor/doctrine/dbal/docs/design/AZURE_FEDERATIONS.md new file mode 100644 index 0000000..99d7e3c --- /dev/null +++ b/vendor/doctrine/dbal/docs/design/AZURE_FEDERATIONS.md @@ -0,0 +1,94 @@ +# Azure Federations + +Implementing Federations inside a new Doctrine Sharding Extension. Some extensions to the DBAL and ORM core have to be done to get this working. + +1. DBAL (Database Abstraction Layer) + +* Add support for Database Schema Operations + * CREATE FEDERATION + * CREATE TABLE ... FEDERATED ON + * Add support to create a multi-tenent schema from any given schema +* Add API to pick a shard based on distribution key and atomic value +* Add API to ask about federations, federation members and so on. +* Add Sharding Abstraction + * If a shard is picked via distribution key and atomic value fire queries against this only + * Or query the global database. + +2. ORM (Object-Relational Mapper) + +* Federation Key has to be part of the clustered index of the table + * Test with a pure Multi-Tenent App with Filtering = ON (TaskList) + * Test with sharded app (Weather) + +## Implementation Details + +SQL Azure requires one and exactly one clustered index. It makes no difference if the primary key +or any other key is the clustered index. Sharding requires an external ID generation (no auto-increment) +such as GUIDs. GUIDs have negative properties with regard to clustered index performance, so that +typically you would add a "created" timestamp for example that holds the clustered index instead +of making the GUID a clustered index. + +## Example API: + + @@@ php + 'tcp:dbname.database.windows.net', + 'sharding' => array( + 'federationName' => 'Orders_Federation', + 'distributionKey' => 'CustID', + 'distributionType' => 'integer', + 'filteringEnabled' => false, + ), + // ... + ); + + $conn = DriverManager::getConnection($dbParams); + $shardManager = $conn->getShardManager(); + + // Example 1: query against root database + $sql = "SELECT * FROM Products"; + $rows = $conn->executeQuery($sql); + + // Example 2: query against the selected shard with CustomerId = 100 + $aCustomerID = 100; + $shardManager->selectShard($aCustomerID); // Using Default federationName and distributionKey + // Query: "USE FEDERATION Orders_Federation (CustID = $aCustomerID) WITH RESET, FILTERING OFF;" + + $sql = "SELECT * FROM Customers"; + $rows = $conn->executeQuery($sql); + + // Example 3: Reset API to root database again + $shardManager->selectGlobal(); + +## ID Generation + +With sharding all the ids have to be generated for global uniqueness. There are three strategies for this. + +1. Use GUIDs as described here http://blogs.msdn.com/b/cbiyikoglu/archive/2011/06/20/id-generation-in-federations-identity-sequences-and-guids-uniqueidentifier.aspx +2. Having a central table that is accessed with a second connection to generate sequential ids +3. Using natural keys from the domain. + +The second approach has the benefit of having numerical primary keys, however also a central failure location. The third strategy can seldom be used, because the domains dont allow this. Identity columns cannot be used at all. + + @@@ php + 'dbname.database.windows.net', + // ... + ); + $conn = DriverManager::getConnection($dbParams); + + $idGenerator = new TableHiLoIdGenerator($conn, 'id_table_name', $multiplicator = 1); + // only once, create this table + $idGenerator->createTable(); + + $nextId = $idGenerator->generateId('for_table_name'); + $nextOtherId = $idGenerator->generateId('for_other_table'); + +The connection for the table generator has to be a different one than the one used for the main app to avoid transaction clashes. diff --git a/vendor/doctrine/dbal/docs/design/SHARDING.md b/vendor/doctrine/dbal/docs/design/SHARDING.md new file mode 100644 index 0000000..24e6cef --- /dev/null +++ b/vendor/doctrine/dbal/docs/design/SHARDING.md @@ -0,0 +1,74 @@ +# Doctrine Shards + +Doctrine Extension to support horizontal sharding in the Doctrine ORM. + +## Idea + +Implement sharding inside Doctrine at a level that is as unobtrusive to the developer as possible. + +Problems to tackle: + +1. Where to send INSERT statements? +2. How to generate primary keys? +3. How to pick shards for update, delete statements? +4. How to pick shards for select operations? +5. How to merge select queries that span multiple shards? +6. How to handle/prevent multi-shard queries that cannot be merged (GROUP BY)? +7. How to handle non-sharded data? (static metadata tables for example) +8. How to handle multiple connections? +9. Implementation on the DBAL or ORM level? + +## Roadmap + +Version 1: DBAL 2.3 (Multi-Tenant Apps) + + 1. ID Generation support (in DBAL + ORM done) + 2. Multi-Tenant Support: Either pick a global metadata database or exactly one shard. + 3. Fan-out queries over all shards (or a subset) by result appending + +Version 2: ORM related (complex): + + 4. ID resolving (Pick shard for a new ID) + 5. Query resolving (Pick shards a query should send to) + 6. Shard resolving (Pick shards an ID could be on) + 7. Transactions + 8. Read Only objects + +## Technical Requirements for Database Schemas + +Sharded tables require the sharding-distribution key as one of their columns. This will affect your code compared to a normalized db-schema. If you have a Blog <-> BlogPost <-> PostComments entity setup sharded by `blog_id` then even the PostComment table needs this column, even if an "unsharded", normalized DB-Schema does not need this information. + +## Implementation Details + +Assumptions: + +* For querying you either want to query ALL or just exactly one shard. +* IDs for ALL sharded tables have to be unique across all shards. +* Non-shareded data is replicated between all shards. They redundantly keep the information available. This is necessary so join queries on shards to reference data work. +* If you retrieve an object A from a shard, then all references and collections of this object reside on the same shard. +* The database schema on all shards is the same (or compatible) + +### SQL Azure Federations + +SQL Azure is a special case, points 1, 2, 3, 4, 7 and 8 are partly handled on the database level. This makes it a perfect test-implementation for just the subset of features in points 5-6. However there need to be a way to configure SchemaTool to generate the correct Schema on SQL Azure. + +* SELECT Operations: The most simple assumption is to always query all shards unless the user specifies otherwise explicitly. +* Queries can be merged in PHP code, this obviously does not work for DISTINCT, GROUP BY and ORDER BY queries. + +### Generic Sharding + +More features are necessary to implement sharding on the PHP level, independent from database support: + +1. Configuration of multiple connections, one connection = one shard. +2. Primary Key Generation mechanisms (UUID, central table, sequence emulation) + +## Primary Use-Cases + +1. Multi-Tenant Applications + +These are easier to support as you have some value to determine the shard id for the whole request very early on. +Here also queries can always be limited to a single shard. + +2. Scale-Out by some attribute (Round-Robin?) + +This strategy requires access to multiple shards in a single request based on the data accessed. diff --git a/vendor/doctrine/dbal/docs/examples/sharding/README.md b/vendor/doctrine/dbal/docs/examples/sharding/README.md new file mode 100644 index 0000000..3680e54 --- /dev/null +++ b/vendor/doctrine/dbal/docs/examples/sharding/README.md @@ -0,0 +1,26 @@ +# Sharding with SQLAzure Example + +This example demonstrates Sharding with SQL Azure Federations. + +## Requirements + +1. Windows Azure Account +2. SQL Azure Database +3. Composer for dependencies + +## Install + + composer install + +Change "examples/sharding/bootstrap.php" to contain Database connection. + +## Order to execute Scripts + +1. create_schema.php +2. view_federation_members.php +3. insert_data.php +4. split_federation.php +5. insert_data_after_split.php +6. query_filtering_off.php +7. query_filtering_on.php + diff --git a/vendor/doctrine/dbal/docs/examples/sharding/bootstrap.php b/vendor/doctrine/dbal/docs/examples/sharding/bootstrap.php new file mode 100644 index 0000000..fe174f1 --- /dev/null +++ b/vendor/doctrine/dbal/docs/examples/sharding/bootstrap.php @@ -0,0 +1,26 @@ + 'SalesDB', + 'host' => 'tcp:dbname.windows.net', + 'user' => 'user@dbname', + 'password' => 'XXX', + 'sharding' => array( + 'federationName' => 'Orders_Federation', + 'distributionKey' => 'CustId', + 'distributionType' => 'integer', + ) +); + +if ($config['host'] == "tcp:dbname.windows.net") { + die("You have to change the configuration to your Azure account.\n"); +} + +$conn = DriverManager::getConnection($config); +$shardManager = new SQLAzureShardManager($conn); + diff --git a/vendor/doctrine/dbal/docs/examples/sharding/composer.json b/vendor/doctrine/dbal/docs/examples/sharding/composer.json new file mode 100644 index 0000000..214f922 --- /dev/null +++ b/vendor/doctrine/dbal/docs/examples/sharding/composer.json @@ -0,0 +1,6 @@ +{ + "require": { + "doctrine/dbal": "*", + "doctrine/shards": "0.3" + } +} diff --git a/vendor/doctrine/dbal/docs/examples/sharding/create_schema.php b/vendor/doctrine/dbal/docs/examples/sharding/create_schema.php new file mode 100644 index 0000000..ac6b66c --- /dev/null +++ b/vendor/doctrine/dbal/docs/examples/sharding/create_schema.php @@ -0,0 +1,51 @@ +createTable('Products'); +$products->addColumn('ProductID', 'integer'); +$products->addColumn('SupplierID', 'integer'); +$products->addColumn('ProductName', 'string'); +$products->addColumn('Price', 'decimal', array('scale' => 2, 'precision' => 12)); +$products->setPrimaryKey(array('ProductID')); +$products->addOption('azure.federated', true); + +$customers = $schema->createTable('Customers'); +$customers->addColumn('CustomerID', 'integer'); +$customers->addColumn('CompanyName', 'string'); +$customers->addColumn('FirstName', 'string'); +$customers->addColumn('LastName', 'string'); +$customers->setPrimaryKey(array('CustomerID')); +$customers->addOption('azure.federated', true); +$customers->addOption('azure.federatedOnColumnName', 'CustomerID'); + +$orders = $schema->createTable('Orders'); +$orders->addColumn('CustomerID', 'integer'); +$orders->addColumn('OrderID', 'integer'); +$orders->addColumn('OrderDate', 'datetime'); +$orders->setPrimaryKey(array('CustomerID', 'OrderID')); +$orders->addOption('azure.federated', true); +$orders->addOption('azure.federatedOnColumnName', 'CustomerID'); + +$orderItems = $schema->createTable('OrderItems'); +$orderItems->addColumn('CustomerID', 'integer'); +$orderItems->addColumn('OrderID', 'integer'); +$orderItems->addColumn('ProductID', 'integer'); +$orderItems->addColumn('Quantity', 'integer'); +$orderItems->setPrimaryKey(array('CustomerID', 'OrderID', 'ProductID')); +$orderItems->addOption('azure.federated', true); +$orderItems->addOption('azure.federatedOnColumnName', 'CustomerID'); + +// Create the Schema + Federation: +$synchronizer = new SQLAzureSchemaSynchronizer($conn, $shardManager); + +// Or jut look at the SQL: +echo implode("\n", $synchronizer->getCreateSchema($schema)); + +$synchronizer->createSchema($schema); + diff --git a/vendor/doctrine/dbal/docs/examples/sharding/insert_data.php b/vendor/doctrine/dbal/docs/examples/sharding/insert_data.php new file mode 100644 index 0000000..57aeda6 --- /dev/null +++ b/vendor/doctrine/dbal/docs/examples/sharding/insert_data.php @@ -0,0 +1,132 @@ +selectShard(0); + +$conn->insert("Products", array( + "ProductID" => 386, + "SupplierID" => 1001, + "ProductName" => 'Titanium Extension Bracket Left Hand', + "Price" => 5.25, +)); +$conn->insert("Products", array( + "ProductID" => 387, + "SupplierID" => 1001, + "ProductName" => 'Titanium Extension Bracket Right Hand', + "Price" => 5.25, +)); +$conn->insert("Products", array( + "ProductID" => 388, + "SupplierID" => 1001, + "ProductName" => 'Fusion Generator Module 5 kV', + "Price" => 10.50, +)); +$conn->insert("Products", array( + "ProductID" => 389, + "SupplierID" => 1001, + "ProductName" => 'Bypass Filter 400 MHz Low Pass', + "Price" => 10.50, +)); + +$conn->insert("Customers", array( + 'CustomerID' => 10, + 'CompanyName' => 'Van Nuys', + 'FirstName' => 'Catherine', + 'LastName' => 'Abel', +)); +$conn->insert("Customers", array( + 'CustomerID' => 20, + 'CompanyName' => 'Abercrombie', + 'FirstName' => 'Kim', + 'LastName' => 'Branch', +)); +$conn->insert("Customers", array( + 'CustomerID' => 30, + 'CompanyName' => 'Contoso', + 'FirstName' => 'Frances', + 'LastName' => 'Adams', +)); +$conn->insert("Customers", array( + 'CustomerID' => 40, + 'CompanyName' => 'A. Datum Corporation', + 'FirstName' => 'Mark', + 'LastName' => 'Harrington', +)); +$conn->insert("Customers", array( + 'CustomerID' => 50, + 'CompanyName' => 'Adventure Works', + 'FirstName' => 'Keith', + 'LastName' => 'Harris', +)); +$conn->insert("Customers", array( + 'CustomerID' => 60, + 'CompanyName' => 'Alpine Ski House', + 'FirstName' => 'Wilson', + 'LastName' => 'Pais', +)); +$conn->insert("Customers", array( + 'CustomerID' => 70, + 'CompanyName' => 'Baldwin Museum of Science', + 'FirstName' => 'Roger', + 'LastName' => 'Harui', +)); +$conn->insert("Customers", array( + 'CustomerID' => 80, + 'CompanyName' => 'Blue Yonder Airlines', + 'FirstName' => 'Pilar', + 'LastName' => 'Pinilla', +)); +$conn->insert("Customers", array( + 'CustomerID' => 90, + 'CompanyName' => 'City Power & Light', + 'FirstName' => 'Kari', + 'LastName' => 'Hensien', +)); +$conn->insert("Customers", array( + 'CustomerID' => 100, + 'CompanyName' => 'Coho Winery', + 'FirstName' => 'Peter', + 'LastName' => 'Brehm', +)); + +$conn->executeUpdate(" + DECLARE @orderId INT + + DECLARE @customerId INT + + SET @orderId = 10 + SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Hensien' and FirstName = 'Kari' + + INSERT INTO Orders (CustomerId, OrderId, OrderDate) + VALUES (@customerId, @orderId, GetDate()) + + INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity) + VALUES (@customerId, @orderId, 388, 4) + + SET @orderId = 20 + SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Harui' and FirstName = 'Roger' + + INSERT INTO Orders (CustomerId, OrderId, OrderDate) + VALUES (@customerId, @orderId, GetDate()) + + INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity) + VALUES (@customerId, @orderId, 389, 2) + + SET @orderId = 30 + SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Brehm' and FirstName = 'Peter' + + INSERT INTO Orders (CustomerId, OrderId, OrderDate) + VALUES (@customerId, @orderId, GetDate()) + + INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity) + VALUES (@customerId, @orderId, 387, 3) + + SET @orderId = 40 + SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Pais' and FirstName = 'Wilson' + + INSERT INTO Orders (CustomerId, OrderId, OrderDate) + VALUES (@customerId, @orderId, GetDate()) + + INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity) + VALUES (@customerId, @orderId, 388, 1)"); diff --git a/vendor/doctrine/dbal/docs/examples/sharding/insert_data_aftersplit.php b/vendor/doctrine/dbal/docs/examples/sharding/insert_data_aftersplit.php new file mode 100644 index 0000000..312e90b --- /dev/null +++ b/vendor/doctrine/dbal/docs/examples/sharding/insert_data_aftersplit.php @@ -0,0 +1,27 @@ +selectShard($newCustomerId); + +$conn->insert("Customers", array( + "CustomerID" => $newCustomerId, + "CompanyName" => "Microsoft", + "FirstName" => "Brian", + "LastName" => "Swan", +)); + +$conn->insert("Orders", array( + "CustomerID" => 55, + "OrderID" => 37, + "OrderDate" => date('Y-m-d H:i:s'), +)); + +$conn->insert("OrderItems", array( + "CustomerID" => 55, + "OrderID" => 37, + "ProductID" => 387, + "Quantity" => 1, +)); diff --git a/vendor/doctrine/dbal/docs/examples/sharding/query_filtering_off.php b/vendor/doctrine/dbal/docs/examples/sharding/query_filtering_off.php new file mode 100644 index 0000000..c0b24fa --- /dev/null +++ b/vendor/doctrine/dbal/docs/examples/sharding/query_filtering_off.php @@ -0,0 +1,8 @@ +selectShard(0); + +$data = $conn->fetchAll('SELECT * FROM Customers'); +print_r($data); diff --git a/vendor/doctrine/dbal/docs/examples/sharding/query_filtering_on.php b/vendor/doctrine/dbal/docs/examples/sharding/query_filtering_on.php new file mode 100644 index 0000000..e7d9e14 --- /dev/null +++ b/vendor/doctrine/dbal/docs/examples/sharding/query_filtering_on.php @@ -0,0 +1,9 @@ +setFilteringEnabled(true); +$shardManager->selectShard(55); + +$data = $conn->fetchAll('SELECT * FROM Customers'); +print_r($data); diff --git a/vendor/doctrine/dbal/docs/examples/sharding/split_federation.php b/vendor/doctrine/dbal/docs/examples/sharding/split_federation.php new file mode 100644 index 0000000..ff681ed --- /dev/null +++ b/vendor/doctrine/dbal/docs/examples/sharding/split_federation.php @@ -0,0 +1,5 @@ +splitFederation(60); diff --git a/vendor/doctrine/dbal/docs/examples/sharding/view_federation_members.php b/vendor/doctrine/dbal/docs/examples/sharding/view_federation_members.php new file mode 100644 index 0000000..497e4df --- /dev/null +++ b/vendor/doctrine/dbal/docs/examples/sharding/view_federation_members.php @@ -0,0 +1,8 @@ +getShards(); +foreach ($shards as $shard) { + print_r($shard); +} 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..8ad167b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\ResultStatement; +use PDO; + +class ArrayStatement implements \IteratorAggregate, ResultStatement +{ + private $data; + private $columnCount = 0; + private $num = 0; + private $defaultFetchMode = PDO::FETCH_BOTH; + + public function __construct(array $data) + { + $this->data = $data; + if (count($data)) { + $this->columnCount = count($data[0]); + } + } + + public function closeCursor() + { + unset ($this->data); + } + + public function columnCount() + { + return $this->columnCount; + } + + 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; + } + + public function getIterator() + { + $data = $this->fetchAll(); + return new \ArrayIterator($data); + } + + 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; + } else if ($fetchMode === PDO::FETCH_NUM) { + return array_values($row); + } else if ($fetchMode === PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } else if ($fetchMode === PDO::FETCH_COLUMN) { + return reset($row); + } else { + throw new \InvalidArgumentException("Invalid fetch-style given for fetching result."); + } + } + return false; + } + + public function fetchAll($fetchMode = null) + { + $rows = array(); + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + return $rows; + } + + 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..dd27477 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +/** + * @author Benjamin Eberlei + * @since 2.2 + */ +class CacheException extends \Doctrine\DBAL\DBALException +{ + static public function noCacheKey() + { + return new self("No cache key was set."); + } + + 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..54c34b9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php @@ -0,0 +1,131 @@ +. + */ + +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 Cache + */ + private $resultCacheDriver; + /** + * @var int + */ + private $lifetime = 0; + /** + * @var string + */ + private $cacheKey; + + /** + * @param int $lifetime + * @param string $cacheKey + * @param Cache $resultCache + */ + public function __construct($lifetime = 0, $cacheKey = null, Cache $resultCache = null) + { + $this->lifetime = $lifetime; + $this->cacheKey = $cacheKey; + $this->resultCacheDriver = $resultCache; + } + + /** + * @return Cache + */ + public function getResultCacheDriver() + { + return $this->resultCacheDriver; + } + + /** + * @return int + */ + public function getLifetime() + { + return $this->lifetime; + } + + /** + * @return string + */ + public function getCacheKey() + { + if ($this->cacheKey === null) { + throw CacheException::noCacheKey(); + } + return $this->cacheKey; + } + + /** + * Generate 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 Cache $cache + * @return QueryCacheProfile + */ + public function setResultCacheDriver(Cache $cache) + { + return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache); + } + + /** + * @param string|null $cacheKey + * @return QueryCacheProfile + */ + public function setCacheKey($cacheKey) + { + return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCacheDriver); + } + + /** + * @param int $lifetime + * @return 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..f118e7c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -0,0 +1,239 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Driver\ResultStatement; +use Doctrine\DBAL\Connection; +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 int + */ + private $lifetime; + + /** + * @var Doctrine\DBAL\Driver\Statement + */ + private $statement; + + /** + * Did we reach the end of the statement? + * + * @var bool + */ + private $emptied = false; + + /** + * @var array + */ + private $data; + + /** + * @var int + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @param Statement $stmt + * @param Cache $resultCache + * @param string $cacheKey + * @param string $realKey + * @param int $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; + } + + /** + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + 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); + } + } + + /** + * columnCount + * Returns the number of columns in the result set + * + * @return integer Returns 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() + { + return $this->statement->columnCount(); + } + + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + } + + public function getIterator() + { + $data = $this->fetchAll(); + return new \ArrayIterator($data); + } + + /** + * fetch + * + * @see Query::HYDRATE_* constants + * @param integer $fetchMode Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @return mixed + */ + 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; + } else if ($fetchMode == PDO::FETCH_NUM) { + return array_values($row); + } else if ($fetchMode == PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } else if ($fetchMode == PDO::FETCH_COLUMN) { + return reset($row); + } else { + throw new \InvalidArgumentException("Invalid fetch-style given for caching result."); + } + } + $this->emptied = true; + return false; + } + + /** + * Returns an array containing all of the result set rows + * + * @param integer $fetchMode Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @return array + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + return $rows; + } + + /** + * fetchColumn + * 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 returns a single column in the next row of a result set. + */ + 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]; + } + + /** + * rowCount + * rowCount() 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 Returns 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..53f7b5e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php @@ -0,0 +1,113 @@ +. + */ + +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 SQLLogger $logger + */ + public function setSQLLogger(SQLLogger $logger = null) + { + $this->_attributes['sqlLogger'] = $logger; + } + + /** + * Gets the SQL logger that is used. + * + * @return SQLLogger + */ + 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 + */ + 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 + */ + public function setResultCacheImpl(Cache $cacheImpl) + { + $this->_attributes['resultCacheImpl'] = $cacheImpl; + } + + /** + * 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 + */ + public function setFilterSchemaAssetsExpression($filterExpression) + { + $this->_attributes['filterSchemaAssetsExpression'] = $filterExpression; + } + + /** + * Return filter schema assets expression. + * + * @return string|null + */ + public function getFilterSchemaAssetsExpression() + { + if (isset($this->_attributes['filterSchemaAssetsExpression'])) { + return $this->_attributes['filterSchemaAssetsExpression']; + } + return null; + } +} 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..80efa94 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php @@ -0,0 +1,1308 @@ +. + */ + +namespace Doctrine\DBAL; + +use PDO, Closure, Exception, + Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Driver\Connection as DriverConnection, + Doctrine\Common\EventManager, + Doctrine\DBAL\DBALException, + Doctrine\DBAL\Cache\ResultCacheStatement, + Doctrine\DBAL\Cache\QueryCacheProfile, + Doctrine\DBAL\Cache\ArrayStatement, + Doctrine\DBAL\Cache\CacheException; + +/** + * 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 int + */ + const PARAM_INT_ARRAY = 101; + + /** + * Represents an array of strings to be expanded by Doctrine SQL parsing. + * + * @var int + */ + const PARAM_STR_ARRAY = 102; + + /** + * Offset by which PARAM_* constants are detected as arrays of the param type. + * + * @var int + */ + 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 transaction nesting level. + * + * @var integer + */ + private $_transactionNestingLevel = 0; + + /** + * The currently active transaction isolation level. + * + * @var integer + */ + private $_transactionIsolationLevel; + + /** + * If nested transations should use savepoints + * + * @var integer + */ + private $_nestTransactionsWithSavepoints; + + /** + * 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 + */ + protected $_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; + + private $_defaultFetchMode = PDO::FETCH_ASSOC; + + /** + * Initializes a new instance of the Connection class. + * + * @param array $params The connection parameters. + * @param Driver $driver + * @param Configuration $config + * @param EventManager $eventManager + */ + 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; + } + + // 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); + + if ( ! isset($params['platform'])) { + $this->_platform = $driver->getDatabasePlatform(); + } else if ($params['platform'] instanceof Platforms\AbstractPlatform) { + $this->_platform = $params['platform']; + } else { + throw DBALException::invalidPlatformSpecified(); + } + + $this->_platform->setEventManager($eventManager); + + $this->_transactionIsolationLevel = $this->_platform->getDefaultTransactionIsolationLevel(); + } + + /** + * Gets the parameters used during instantiation. + * + * @return array $params + */ + public function getParams() + { + return $this->_params; + } + + /** + * Gets the name of the database this Connection is connected to. + * + * @return string $database + */ + public function getDatabase() + { + return $this->_driver->getDatabase($this); + } + + /** + * Gets the hostname of the currently connected database. + * + * @return string + */ + 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 + */ + public function getUsername() + { + return isset($this->_params['user']) ? $this->_params['user'] : null; + } + + /** + * Gets the password used by this connection. + * + * @return string + */ + 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() + { + 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 ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new Event\ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * setFetchMode + * + * @param integer $fetchMode + */ + 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. + * @return array + */ + public function fetchAssoc($statement, array $params = array()) + { + return $this->executeQuery($statement, $params)->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 sql query to be executed + * @param array $params prepared statement params + * @return array + */ + public function fetchArray($statement, array $params = array()) + { + return $this->executeQuery($statement, $params)->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 sql query to be executed + * @param array $params prepared statement params + * @param int $colnum 0-indexed column number to retrieve + * @return mixed + */ + public function fetchColumn($statement, array $params = array(), $colnum = 0) + { + return $this->executeQuery($statement, $params)->fetchColumn($colnum); + } + + /** + * 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. + * + * @param string $tableName The name of the table on which to delete. + * @param array $identifier The deletion criteria. An associative array containing column-value pairs. + * @return integer The number of affected rows. + */ + public function delete($tableName, array $identifier) + { + $this->connect(); + + $criteria = array(); + + foreach (array_keys($identifier) as $columnName) { + $criteria[] = $columnName . ' = ?'; + } + + $query = 'DELETE FROM ' . $tableName . ' WHERE ' . implode(' AND ', $criteria); + + return $this->executeUpdate($query, array_values($identifier)); + } + + /** + * Closes the connection. + * + * @return void + */ + public function close() + { + unset($this->_conn); + + $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->_platform->getSetTransactionIsolationSQL($level)); + } + + /** + * Gets the currently active transaction isolation level. + * + * @return integer The current transaction isolation level. + */ + public function getTransactionIsolation() + { + return $this->_transactionIsolationLevel; + } + + /** + * Executes an SQL UPDATE statement on a table. + * + * @param string $tableName The name of the table to update. + * @param array $data + * @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($tableName, array $data, array $identifier, array $types = array()) + { + $this->connect(); + $set = array(); + foreach ($data as $columnName => $value) { + $set[] = $columnName . ' = ?'; + } + + $params = array_merge(array_values($data), array_values($identifier)); + + $sql = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' = ? AND ', array_keys($identifier)) + . ' = ?'; + + return $this->executeUpdate($sql, $params, $types); + } + + /** + * Inserts a table row with specified data. + * + * @param string $tableName The name of the table to insert data into. + * @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($tableName, array $data, array $types = array()) + { + $this->connect(); + + // column names are specified as array keys + $cols = array(); + $placeholders = array(); + + foreach ($data as $columnName => $value) { + $cols[] = $columnName; + $placeholders[] = '?'; + } + + $query = 'INSERT INTO ' . $tableName + . ' (' . implode(', ', $cols) . ')' + . ' VALUES (' . implode(', ', $placeholders) . ')'; + + return $this->executeUpdate($query, array_values($data), $types); + } + + /** + * Quote 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->_platform->quoteIdentifier($str); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input Parameter to be quoted. + * @param string $type 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. + * @return array + */ + public function fetchAll($sql, array $params = array()) + { + return $this->executeQuery($sql, $params)->fetchAll(); + } + + /** + * Prepares an SQL statement. + * + * @param string $statement The SQL statement to prepare. + * @return \Doctrine\DBAL\Driver\Statement The prepared statement. + */ + public function prepare($statement) + { + $this->connect(); + + try { + $stmt = new Statement($statement, $this); + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($ex, $statement); + } + + $stmt->setFetchMode($this->_defaultFetchMode); + + return $stmt; + } + + /** + * Executes an, optionally parameterized, SQL query. + * + * If the query is parameterized, 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 QueryCacheProfile $qcp + * @return \Doctrine\DBAL\Driver\Statement The executed statement. + * @internal PERF: Directly prepares a driver statement, not a wrapper. + */ + 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($ex, $query, $this->resolveParams($params, $types)); + } + + $stmt->setFetchMode($this->_defaultFetchMode); + + if ($logger) { + $logger->stopQuery(); + } + + return $stmt; + } + + /** + * Execute a caching query and + * + * @param string $query + * @param array $params + * @param array $types + * @param QueryCacheProfile $qcp + * @return \Doctrine\DBAL\Driver\ResultStatement + */ + 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]); + } else if (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 parameterized, 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 $mapper The transformation function that is applied on each row. + * The function receives a single paramater, an array, that + * represents a row of the result set. + * @return mixed The projected result of the query. + */ + public function project($query, array $params, Closure $function) + { + $result = array(); + $stmt = $this->executeQuery($query, $params ?: array()); + + while ($row = $stmt->fetch()) { + $result[] = $function($row); + } + + $stmt->closeCursor(); + + return $result; + } + + /** + * Executes an SQL statement, returning a result set as a Statement object. + * + * @param string $statement + * @param integer $fetchType + * @return \Doctrine\DBAL\Driver\Statement + */ + public function query() + { + $this->connect(); + + $args = func_get_args(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($args[0]); + } + + try { + $statement = call_user_func_array(array($this->_conn, 'query'), $args); + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($ex, func_get_arg(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. + * @internal PERF: Directly prepares a driver statement, not a wrapper. + */ + 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($ex, $query, $this->resolveParams($params, $types)); + } + + if ($logger) { + $logger->stopQuery(); + } + + return $result; + } + + /** + * Execute an SQL statement and return the number of affected rows. + * + * @param string $statement + * @return integer The number of affected rows. + */ + 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($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; + } + + /** + * Fetch the SQLSTATE associated with the last database operation. + * + * @return integer The last error code. + */ + public function errorCode() + { + $this->connect(); + return $this->_conn->errorCode(); + } + + /** + * Fetch 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 $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. + */ + public function transactional(Closure $func) + { + $this->beginTransaction(); + try { + $func($this); + $this->commit(); + } catch (Exception $e) { + $this->rollback(); + throw $e; + } + } + + /** + * Set if nested transactions should use savepoints + * + * @param boolean $nestTransactionsWithSavepoints + * @return void + */ + public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) + { + if ($this->_transactionNestingLevel > 0) { + throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction(); + } + + if ( ! $this->_platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_nestTransactionsWithSavepoints = $nestTransactionsWithSavepoints; + } + + /** + * Get 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(); + } + } else if ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"SAVEPOINT"'); + } + $this->createSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger) { + $logger->stopQuery(); + } + } + } + + /** + * Commits the current transaction. + * + * @return void + * @throws 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(); + } + } else if ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"RELEASE SAVEPOINT"'); + } + $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger) { + $logger->stopQuery(); + } + } + + --$this->_transactionNestingLevel; + } + + /** + * Cancel any database changes done during the current transaction. + * + * this method can be listened with onPreTransactionRollback and onTransactionRollback + * eventlistener methods + * + * @throws 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(); + } + } else if ($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; + } + } + + /** + * createSavepoint + * creates a new savepoint + * + * @param string $savepoint name of a savepoint to set + * @return void + */ + public function createSavepoint($savepoint) + { + if ( ! $this->_platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_conn->exec($this->_platform->createSavePoint($savepoint)); + } + + /** + * releaseSavePoint + * releases given savepoint + * + * @param string $savepoint name of a savepoint to release + * @return void + */ + public function releaseSavepoint($savepoint) + { + if ( ! $this->_platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + if ($this->_platform->supportsReleaseSavepoints()) { + $this->_conn->exec($this->_platform->releaseSavePoint($savepoint)); + } + } + + /** + * rollbackSavePoint + * releases given savepoint + * + * @param string $savepoint name of a savepoint to rollback to + * @return void + */ + public function rollbackSavepoint($savepoint) + { + if ( ! $this->_platform->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. + * + * @throws ConnectionException If no transaction is active. + */ + public function setRollbackOnly() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + $this->_isRollbackOnly = true; + } + + /** + * Check whether the current transaction is marked for rollback only. + * + * @return boolean + * @throws 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->_platform); + } + + /** + * 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->_platform); + } + + /** + * 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 string $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). + * @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->_platform); + $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; + } + + /** + * Create a new instance of a SQL query builder. + * + * @return \Doctrine\DBAL\Query\QueryBuilder + */ + public function createQueryBuilder() + { + return new Query\QueryBuilder($this); + } +} 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..4b41ef2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Doctrine\DBAL\ConnectionException + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 4628 $ + * @author Jonathan H. Wage . + */ + +namespace Doctrine\DBAL\Connections; + + +use Doctrine\DBAL\Connection, + Doctrine\DBAL\Driver, + Doctrine\DBAL\Configuration, + Doctrine\Common\EventManager, + Doctrine\DBAL\Event\ConnectionEventArgs, + 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 bool + */ + protected $keepSlave = false; + + /** + * Create Master Slave Connection + * + * @param array $params + * @param Driver $driver + * @param Configuration $config + * @param EventManager $eventManager + */ + 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); + } + + /** + * Check if the connection is currently towards the master or not. + * + * @return bool + */ + 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]) { + if ($forceMasterAsSlave) { + $this->connections['slave'] = $this->_conn = $this->connections['master']; + } else { + $this->_conn = $this->connections[$connectionName]; + } + 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; + } + + /** + * Connect to a specific connection + * + * @param string $connectionName + * @return 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); + } + + 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'); + return parent::beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() + { + $this->connect('master'); + return parent::commit(); + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + $this->connect('master'); + return parent::rollBack(); + } + + /** + * {@inheritDoc} + */ + public function delete($tableName, array $identifier) + { + $this->connect('master'); + return parent::delete($tableName, $identifier); + } + + /** + * {@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'); + + return parent::createSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function releaseSavepoint($savepoint) + { + $this->connect('master'); + + return parent::releaseSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function rollbackSavepoint($savepoint) + { + $this->connect('master'); + + return parent::rollbackSavepoint($savepoint); + } + + 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; + } + + 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..2b4151e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php @@ -0,0 +1,128 @@ +getMessage(); + + 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)) . ']'; + } + + public static function invalidWrapperClass($wrapperClass) + { + return new self("The given 'wrapperClass' ".$wrapperClass." has to be a ". + "subtype of \Doctrine\DBAL\Connection."); + } + + public static function invalidDriverClass($driverClass) + { + return new self("The given 'driverClass' ".$driverClass." has to implement the ". + "\Doctrine\DBAL\Driver interface."); + } + + /** + * @param string $tableName + * @return DBALException + */ + public static function invalidTableName($tableName) + { + return new self("Invalid table name specified: ".$tableName); + } + + /** + * @param string $tableName + * @return DBALException + */ + public static function noColumnsSpecifiedForTable($tableName) + { + return new self("No columns specified for table ".$tableName); + } + + public static function limitOffsetInvalid() + { + return new self("Invalid Offset in Limit Query, it has to be larger or equal to 0."); + } + + public static function typeExists($name) + { + return new self('Type '.$name.' already exists.'); + } + + 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::getTypeMap(). 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.' + ); + } + + 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..8364990 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php @@ -0,0 +1,72 @@ +. + */ + +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 $username The username to use when connecting. + * @param string $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(); + + /** + * Get the name of the database connected to for this driver. + * + * @param \Doctrine\DBAL\Connection $conn + * @return string $database + */ + public function getDatabase(Connection $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..a618487 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php @@ -0,0 +1,42 @@ +. + */ + +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 +{ + function prepare($prepareString); + function query(); + function quote($input, $type=\PDO::PARAM_STR); + function exec($statement); + function lastInsertId($name = null); + function beginTransaction(); + function commit(); + function rollBack(); + function errorCode(); + function errorInfo(); +} 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..2b46c99 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php @@ -0,0 +1,41 @@ +. + */ + +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..8030bbc --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php @@ -0,0 +1,99 @@ +. + */ + +namespace Doctrine\DBAL\Driver\DrizzlePDOMySql; + +/** + * Drizzle driver using PDO MySql. + * + * @author Kim Hemsø Rasmussen + */ +class Driver implements \Doctrine\DBAL\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; + } + + /** + * Constructs the Drizzle MySql PDO DSN. + * + * @return string The DSN. + */ + private 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'] . ';'; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\DrizzlePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\DrizzleSchemaManager($conn); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'drizzle_pdo_mysql'; + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} 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..c1c2212 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php @@ -0,0 +1,115 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +class DB2Connection implements \Doctrine\DBAL\Driver\Connection +{ + private $_conn = null; + + 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()); + } + } + + public function prepare($sql) + { + $stmt = @db2_prepare($this->_conn, $sql); + if ( ! $stmt) { + throw new DB2Exception(db2_stmt_errormsg()); + } + return new DB2Statement($stmt); + } + + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + return $stmt; + } + + public function quote($input, $type=\PDO::PARAM_STR) + { + $input = db2_escape_string($input); + if ($type == \PDO::PARAM_INT ) { + return $input; + } else { + return "'".$input."'"; + } + } + + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + return $stmt->rowCount(); + } + + public function lastInsertId($name = null) + { + return db2_last_insert_id($this->_conn); + } + + public function beginTransaction() + { + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_OFF); + } + + public function commit() + { + if (!db2_commit($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + public function rollBack() + { + if (!db2_rollback($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + public function errorCode() + { + return db2_conn_error($this->_conn); + } + + 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..82c71e5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php @@ -0,0 +1,111 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver, + Doctrine\DBAL\Connection; + +/** + * IBM DB2 Driver + * + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Driver implements Driver +{ + /** + * Attempts to create a connection with the database. + * + * @param array $params All connection parameters passed by the user. + * @param string $username The username to use when connecting. + * @param string $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()) + { + 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); + } + + /** + * 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() + { + return new \Doctrine\DBAL\Platforms\DB2Platform; + } + + /** + * 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\DB2SchemaManager + */ + public function getSchemaManager(Connection $conn) + { + return new \Doctrine\DBAL\Schema\DB2SchemaManager($conn); + } + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName() + { + return 'ibm_db2'; + } + + /** + * Get the name of the database connected to for this driver. + * + * @param \Doctrine\DBAL\Connection $conn + * @return string $database + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} 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..3d07658 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php @@ -0,0 +1,27 @@ +. +*/ + +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..9a43f9f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php @@ -0,0 +1,214 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use \Doctrine\DBAL\Driver\Statement; + +class DB2Statement implements \IteratorAggregate, Statement +{ + private $_stmt = null; + + private $_bindParam = array(); + + 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, + ); + + 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; + } + + /** + * {@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_NUM: + return db2_fetch_array($this->_stmt); + default: + throw new DB2Exception("Given Fetch-Style " . $fetchMode . " is not supported."); + } + } + + /** + * {@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 ($row && isset($row[$columnIndex])) { + return $row[$columnIndex]; + } + return false; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return (@db2_num_rows($this->_stmt))?:0; + } +} 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..60defba --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver as DriverInterface; + +/** + * @author Kim Hemsø Rasmussen + */ +class Driver implements DriverInterface +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new MysqliConnection($params, $username, $password, $driverOptions); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'mysqli'; + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\MySqlSchemaManager($conn); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\MySqlPlatform(); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} 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..7ffa2ca --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php @@ -0,0 +1,146 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Connection as Connection; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliConnection implements Connection +{ + /** + * @var \mysqli + */ + private $_conn; + + public function __construct(array $params, $username, $password, array $driverOptions = array()) + { + $port = isset($params['port']) ? $params['port'] : ini_get('mysqli.default_port'); + $socket = isset($params['unix_socket']) ? $params['unix_socket'] : ini_get('mysqli.default_socket'); + + $this->_conn = mysqli_init(); + if ( ! $this->_conn->real_connect($params['host'], $username, $password, $params['dbname'], $port, $socket)) { + throw new MysqliException($this->_conn->connect_error, $this->_conn->connect_errno); + } + + if (isset($params['charset'])) { + $this->_conn->set_charset($params['charset']); + } + } + + /** + * Retrieve 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 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) + { + $this->_conn->query($statement); + 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; + } +} 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..139ce8f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php @@ -0,0 +1,26 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\Mysqli; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliException extends \Exception +{} 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..2eaa8fd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php @@ -0,0 +1,342 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Statement; +use PDO; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliStatement implements \IteratorAggregate, Statement +{ + 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. + ); + + protected $_conn; + protected $_stmt; + + /** + * @var null|false|array + */ + protected $_columnNames; + + /** + * @var null|array + */ + protected $_rowBindedValues; + + /** + * @var array + */ + protected $_bindedValues; + + /** + * Contains ref values for bindValue() + * + * @var array + */ + protected $_values = array(); + + protected $_defaultFetchMode = PDO::FETCH_BOTH; + + 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->errno); + } + + $paramCount = $this->_stmt->param_count; + if (0 < $paramCount) { + // Index 0 is types + // Need to init the string else php think we are trying to access it as a array. + $bindedValues = array(0 => str_repeat('s', $paramCount)); + $null = null; + for ($i = 1; $i < $paramCount; $i++) { + $bindedValues[] =& $null; + } + $this->_bindedValues = $bindedValues; + } + } + + /** + * {@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("Unkown type: '{$type}'"); + } + } + + $this->_bindedValues[$column] =& $variable; + $this->_bindedValues[0][$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->_bindedValues[0][$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'), $this->_bindedValues)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + } + } + + if ( ! $this->_stmt->execute()) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + + if (null === $this->_columnNames) { + $meta = $this->_stmt->result_metadata(); + if (false !== $meta) { + $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->errno); + } + } else { + $this->_columnNames = false; + } + } + + // We have a result. + if (false !== $this->_columnNames) { + $this->_stmt->store_result(); + } + return true; + } + + /** + * Bind 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) { + // Mysqli converts them to a scalar type it can fit in. + $values[] = null === $v ? null : (string)$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->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 $row[$columnIndex]; + } + + /** + * {@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; + } + + /** + * {@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..d512610 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php @@ -0,0 +1,99 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\Platforms; + +/** + * A Doctrine DBAL driver for the Oracle OCI8 PHP extensions. + * + * @author Roman Borschel + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + 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 + ); + } + + /** + * Constructs the Oracle DSN. + * + * @return string The DSN. + */ + protected function _constructDsn(array $params) + { + $dsn = ''; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= '(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)' . + '(HOST=' . $params['host'] . ')'; + + if (isset($params['port'])) { + $dsn .= '(PORT=' . $params['port'] . ')'; + } else { + $dsn .= '(PORT=1521)'; + } + + if (isset($params['service']) && $params['service'] == true) { + $dsn .= '))(CONNECT_DATA=(SERVICE_NAME=' . $params['dbname'] . '))'; + } else { + $dsn .= '))(CONNECT_DATA=(SID=' . $params['dbname'] . '))'; + } + if (isset($params['pooled']) && $params['pooled'] == true) { + $dsn .= '(SERVER=POOLED)'; + } + $dsn .= ')'; + } else { + $dsn .= $params['dbname']; + } + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\OraclePlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\OracleSchemaManager($conn); + } + + public function getName() + { + return 'oci8'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['user']; + } +} 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..bc74787 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php @@ -0,0 +1,200 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\Platforms\OraclePlatform; + +/** + * OCI8 implementation of the Connection interface. + * + * @since 2.0 + */ +class OCI8Connection implements \Doctrine\DBAL\Driver\Connection +{ + /** + * @var resource + */ + protected $dbh; + + /** + * @var int + */ + protected $executeMode = OCI_COMMIT_ON_SUCCESS; + + /** + * Create a Connection to an Oracle Database using oci8 extension. + * + * @param string $username + * @param string $password + * @param string $db + */ + 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()); + } + } + + /** + * Create a non-executed prepared statement. + * + * @param string $prepareString + * @return OCI8Statement + */ + public function prepare($prepareString) + { + return new OCI8Statement($this->dbh, $prepareString, $this); + } + + /** + * @param string $sql + * @return OCI8Statement + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + //$fetchMode = $args[1]; + $stmt = $this->prepare($sql); + $stmt->execute(); + return $stmt; + } + + /** + * Quote input value. + * + * @param mixed $input + * @param int $type PDO::PARAM* + * @return mixed + */ + 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") . "'"; + } + + /** + * + * @param string $statement + * @return int + */ + 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']; + } + + /** + * Return the current execution mode. + */ + public function getExecuteMode() + { + return $this->executeMode; + } + + /** + * Start a transactiom + * + * Oracle has to explicitly set the autocommit mode off. That means + * after connection, a commit or rollback there is always automatically + * opened a new transaction. + * + * @return bool + */ + public function beginTransaction() + { + $this->executeMode = OCI_NO_AUTO_COMMIT; + return true; + } + + /** + * @throws OCI8Exception + * @return bool + */ + public function commit() + { + if (!oci_commit($this->dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->executeMode = OCI_COMMIT_ON_SUCCESS; + return true; + } + + /** + * @throws OCI8Exception + * @return bool + */ + public function rollBack() + { + if (!oci_rollback($this->dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->executeMode = OCI_COMMIT_ON_SUCCESS; + return true; + } + + public function errorCode() + { + $error = oci_error($this->dbh); + if ($error !== false) { + $error = $error['code']; + } + return $error; + } + + 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..adeb13f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php @@ -0,0 +1,30 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\OCI8; + +class OCI8Exception extends \Exception +{ + static public function fromErrorInfo($error) + { + return new self($error['message'], $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..8bf3121 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php @@ -0,0 +1,268 @@ +. + */ + +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 +{ + /** Statement handle. */ + protected $_dbh; + protected $_sth; + protected $_conn; + protected static $_PARAM = ':param'; + protected static $fetchModeMap = array( + PDO::FETCH_BOTH => OCI_BOTH, + PDO::FETCH_ASSOC => OCI_ASSOC, + PDO::FETCH_NUM => OCI_NUM, + PDO::PARAM_LOB => OCI_B_BLOB, + PDO::FETCH_COLUMN => OCI_NUM, + ); + protected $_defaultFetchMode = PDO::FETCH_BOTH; + 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. + */ + 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; + } + + /** + * Convert 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; + } else if ($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); + } else { + return oci_bind_by_name($this->_sth, $column, $variable); + } + } + + /** + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + 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; + } + + /** + * {@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); + return isset($row[$columnIndex]) ? $row[$columnIndex] : false; + } + + /** + * {@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..4595e5a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php @@ -0,0 +1,40 @@ +. + */ + +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 +{ + public function __construct($dsn, $user = null, $password = null, array $options = null) + { + 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); + } +} 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..de30757 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php @@ -0,0 +1,126 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\PDOIbm; + +use Doctrine\DBAL\Connection; + +/** + * Driver for the PDO IBM extension + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * Attempts to establish a connection with the underlying driver. + * + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * @return \Doctrine\DBAL\Driver\Connection + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + return $conn; + } + + /** + * Constructs the MySql PDO DSN. + * + * @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; + } + + /** + * 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() + { + return new \Doctrine\DBAL\Platforms\DB2Platform; + } + + /** + * 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\DB2SchemaManager + */ + public function getSchemaManager(Connection $conn) + { + return new \Doctrine\DBAL\Schema\DB2SchemaManager($conn); + } + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName() + { + return 'pdo_ibm'; + } + + /** + * Get the name of the database connected to for this driver. + * + * @param \Doctrine\DBAL\Connection $conn + * @return string $database + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} 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..eeb6727 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOMySql; + +use Doctrine\DBAL\Connection; + +/** + * PDO MySql driver. + * + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * Attempts to establish a connection with the underlying driver. + * + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * @return \Doctrine\DBAL\Driver\Connection + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + return $conn; + } + + /** + * Constructs the MySql PDO DSN. + * + * @return string The DSN. + */ + private 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; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\MySqlPlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\MySqlSchemaManager($conn); + } + + public function getName() + { + return 'pdo_mysql'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + return $conn->query('SELECT DATABASE()')->fetchColumn(); + } +} 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..cb2e6b0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOOracle; + +use Doctrine\DBAL\Platforms; + +/** + * PDO Oracle driver + * + * WARNING: This driver gives us segfauls 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 implements \Doctrine\DBAL\Driver +{ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } + + /** + * Constructs the Oracle PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'oci:'; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= 'dbname=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)' . + '(HOST=' . $params['host'] . ')'; + + if (isset($params['port'])) { + $dsn .= '(PORT=' . $params['port'] . ')'; + } else { + $dsn .= '(PORT=1521)'; + } + + if (isset($params['service']) && $params['service'] == true) { + $dsn .= '))(CONNECT_DATA=(SERVICE_NAME=' . $params['dbname'] . ')))'; + } else { + $dsn .= '))(CONNECT_DATA=(SID=' . $params['dbname'] . ')))'; + } + } else { + $dsn .= 'dbname=' . $params['dbname']; + } + + if (isset($params['charset'])) { + $dsn .= ';charset=' . $params['charset']; + } + + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\OraclePlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\OracleSchemaManager($conn); + } + + public function getName() + { + return 'pdo_oracle'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['user']; + } +} 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..951406d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php @@ -0,0 +1,70 @@ +_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } + + /** + * Constructs the Postgres PDO DSN. + * + * @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'] . ' '; + } + + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\PostgreSqlPlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\PostgreSqlSchemaManager($conn); + } + + public function getName() + { + return 'pdo_pgsql'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} 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..903d999 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php @@ -0,0 +1,116 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlite; + +/** + * The PDO Sqlite driver. + * + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * @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), + ); + + /** + * Tries to establish a database connection to SQLite. + * + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * @return \Doctrine\DBAL\Driver\PDOConnection + */ + 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']); + } + + $pdo = new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + foreach ($this->_userDefinedFunctions as $fn => $data) { + $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); + } + + return $pdo; + } + + /** + * Constructs the Sqlite PDO DSN. + * + * @return string The DSN. + * @override + */ + protected function _constructPdoDsn(array $params) + { + $dsn = 'sqlite:'; + if (isset($params['path'])) { + $dsn .= $params['path']; + } else if (isset($params['memory'])) { + $dsn .= ':memory:'; + } + + return $dsn; + } + + /** + * Gets the database platform that is relevant for this driver. + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\SqlitePlatform(); + } + + /** + * Gets the schema manager that is relevant for this driver. + * + * @param \Doctrine\DBAL\Connection $conn + * @return \Doctrine\DBAL\Schema\SqliteSchemaManager + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\SqliteSchemaManager($conn); + } + + public function getName() + { + return 'pdo_sqlite'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return isset($params['path']) ? $params['path'] : null; + } +} 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..01a5769 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +/** + * Sqlsrv Connection implementation. + * + * @since 2.0 + */ +class Connection extends \Doctrine\DBAL\Driver\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..7072b5f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php @@ -0,0 +1,87 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +/** + * The PDO-based Sqlsrv driver. + * + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + 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. + * + * @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; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\SQLServer2008Platform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\SQLServerSchemaManager($conn); + } + + public function getName() + { + return 'pdo_sqlsrv'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} 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..2673ae9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php @@ -0,0 +1,50 @@ +. + */ + +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 +{ + private function __construct() {} + + 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. + if ($arg2 === null && $arg3 === null) { + return parent::setFetchMode($fetchMode); + } + + if ($arg3 === null) { + return parent::setFetchMode($fetchMode, $arg2); + } + + return parent::setFetchMode($fetchMode, $arg2, $arg3); + } +} 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..561dc35 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use PDO; + +/** + * 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 Returns TRUE on success or FALSE on failure. + */ + function closeCursor(); + + + /** + * columnCount + * Returns the number of columns in the result set + * + * @return integer Returns the number of columns in the result set represented + * by the PDOStatement object. If there is no result set, + * this method should return 0. + */ + function columnCount(); + + /** + * setFetchMode + * Set the fetch mode to use while iterating this statement. + * + * @param integer $fetchMode + */ + function setFetchMode($fetchMode, $arg2 = null, $arg3 = null); + + /** + * fetch + * + * @see Query::HYDRATE_* constants + * @param integer $fetchMode Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @return mixed + */ + function fetch($fetchMode = null); + + /** + * Returns an array containing all of the result set rows + * + * @param integer $fetchMode Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @return array + */ + function fetchAll($fetchMode = null); + + /** + * fetchColumn + * 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 returns a single column in the next row of a result set. + */ + function fetchColumn($columnIndex = 0); +} + 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..c7043cc --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Driver.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +/** + * Driver for ext/sqlsrv + */ +class Driver implements \Doctrine\DBAL\Driver +{ + 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."); + } + if (!isset($params['dbname'])) { + throw new SQLSrvException("Missing 'dbname' in configuration for sqlsrv driver."); + } + + $serverName = $params['host']; + if (isset($params['port'])) { + $serverName .= ', ' . $params['port']; + } + $driverOptions['Database'] = $params['dbname']; + $driverOptions['UID'] = $username; + $driverOptions['PWD'] = $password; + + if (!isset($driverOptions['ReturnDatesAsStrings'])) { + $driverOptions['ReturnDatesAsStrings'] = 1; + } + + return new SQLSrvConnection($serverName, $driverOptions); + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\SQLServer2008Platform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\SQLServerSchemaManager($conn); + } + + public function getName() + { + return 'sqlsrv'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} + 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..421d071 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +/** + * Last Id Data Container + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class LastInsertId +{ + private $id; + + public function setId($id) + { + $this->id = $id; + } + + 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..82544ca --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php @@ -0,0 +1,161 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +/** + * SQL Server implementation for the Connection interface. + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class SQLSrvConnection implements \Doctrine\DBAL\Driver\Connection +{ + /** + * @var resource + */ + protected $conn; + + /** + * @var LastInsertId + */ + protected $lastInsertId; + + + public function __construct($serverName, $connectionOptions) + { + $this->conn = sqlsrv_connect($serverName, $connectionOptions); + if ( ! $this->conn) { + throw SQLSrvException::fromSqlSrvErrors(); + } + $this->lastInsertId = new LastInsertId(); + } + + /** + * {@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; + } else if (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..6777877 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +class SQLSrvException extends \Doctrine\DBAL\DBALException +{ + /** + * Helper method to turn sql server errors into exception. + * + * @return 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 occured 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..76a156b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php @@ -0,0 +1,251 @@ +. + */ + +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 +{ + /** + * SQLSRV Resource + * + * @var resource + */ + private $conn; + + /** + * SQL Statement to execute + * + * @var string + */ + private $sql; + + /** + * 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, + ); + + /** + * Fetch Style + * + * @param int + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @var int|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;'; + + public function __construct($conn, $sql, $lastInsertId = null) + { + $this->conn = $conn; + $this->sql = $sql; + + if (stripos($sql, 'INSERT INTO ') === 0) { + $this->sql .= self::LAST_INSERT_ID_SQL; + $this->lastInsertId = $lastInsertId; + } + } + + 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; + } + } + + public function closeCursor() + { + if ($this->stmt) { + sqlsrv_free_stmt($this->stmt); + } + } + + 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); + } + + 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) ); + } + } + + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + if (isset(self::$fetchMap[$fetchMode])) { + return sqlsrv_fetch_array($this->stmt, self::$fetchMap[$fetchMode]); + } else if ($fetchMode == PDO::FETCH_OBJ || $fetchMode == PDO::FETCH_CLASS) { + $className = null; + $ctorArgs = null; + if (func_num_args() >= 2) { + $args = func_get_args(); + $className = $args[1]; + $ctorArgs = (isset($args[2])) ? $args[2] : array(); + } + return sqlsrv_fetch_object($this->stmt, $className, $ctorArgs); + } + + throw new SQLSrvException("Fetch mode is not supported!"); + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $className = null; + $ctorArgs = null; + if (func_num_args() >= 2) { + $args = func_get_args(); + $className = $args[1]; + $ctorArgs = (isset($args[2])) ? $args[2] : array(); + } + + $rows = array(); + while ($row = $this->fetch($fetchMode, $className, $ctorArgs)) { + $rows[] = $row; + } + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + return $row[$columnIndex]; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return sqlsrv_rows_affected($this->stmt); + } +} + 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..718614d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php @@ -0,0 +1,125 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use \PDO; + +/** + * Statement interface. + * Drivers must implement this interface. + * + * This resembles (a subset of) the PDOStatement interface. + * + * @author Konsta Vesterinen + * @author Roman Borschel + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + */ +interface Statement extends ResultStatement +{ + /** + * Binds a value to a corresponding named or positional + * placeholder in the SQL statement that was used to prepare the statement. + * + * @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 Returns TRUE on success or FALSE on failure. + */ + function bindValue($param, $value, $type = null); + + /** + * Binds a PHP variable to a corresponding named 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. + * + * 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 $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 $length You must specify maxlength when using an OUT bind so that PHP allocates enough memory to hold the returned value. + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function bindParam($column, &$variable, $type = null, $length = null); + + /** + * errorCode + * Fetch the SQLSTATE associated with the last operation on the statement handle + * + * @see Doctrine_Adapter_Interface::errorCode() + * @return string error code string + */ + function errorCode(); + + /** + * errorInfo + * Fetch extended error information associated with the last operation on the statement handle + * + * @see Doctrine_Adapter_Interface::errorInfo() + * @return array 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 $params An array of values with as many elements as there are + * bound parameters in the SQL statement being executed. + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function execute($params = null); + + /** + * rowCount + * rowCount() 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 Returns 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..7cdde95 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php @@ -0,0 +1,176 @@ +. + */ + +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_ibm' => 'Doctrine\DBAL\Driver\PDOIbm\Driver', + 'pdo_sqlsrv' => 'Doctrine\DBAL\Driver\PDOSqlsrv\Driver', + 'mysqli' => 'Doctrine\DBAL\Driver\Mysqli\Driver', + 'drizzle_pdo_mysql' => 'Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver', + 'sqlsrv' => 'Doctrine\DBAL\Driver\SQLSrv\Driver', + ); + + /** 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_ibm (unstable) + * pdo_sqlsrv + * mysqli + * 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 The configuration to use. + * @param \Doctrine\Common\EventManager The event manager to use. + * @return \Doctrine\DBAL\Connection + */ + 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(); + } + + // check for existing pdo object + if (isset($params['pdo']) && ! $params['pdo'] instanceof \PDO) { + throw DBALException::invalidPdoInstance(); + } else if (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); + } + + /** + * Checks the list of parameters. + * + * @param array $params + */ + private static function _checkParams(array $params) + { + // check existance 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']); + } + } +} 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..f4cb1cd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php @@ -0,0 +1,79 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs, + Doctrine\DBAL\Connection; + +/** + * Event Arguments used when a Driver connection is established inside Doctrine\DBAL\Connection. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class ConnectionEventArgs extends EventArgs +{ + /** + * @var Connection + */ + private $_connection = null; + + 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..fc22744 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php @@ -0,0 +1,74 @@ +. +*/ + +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 + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @deprecated Use "charset" option to PDO MySQL Connection instead. + */ +class MysqlSessionInit implements EventSubscriber +{ + /** + * @var string + */ + private $_charset; + + /** + * @var string + */ + private $_collation; + + /** + * Configure Charset and Collation options of MySQL Client for each Connection + * + * @param string $charset + * @param string $collation + */ + public function __construct($charset = 'utf8', $collation = false) + { + $this->_charset = $charset; + $this->_collation = $collation; + } + + /** + * @param ConnectionEventArgs $args + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $collation = ($this->_collation) ? " COLLATE ".$this->_collation : ""; + $args->getConnection()->executeUpdate("SET NAMES ".$this->_charset . $collation); + } + + 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..8355403 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php @@ -0,0 +1,80 @@ +. +*/ + +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 enviroment does not match the Doctrine requirements. + * + * The following enviroment 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" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class OracleSessionInit implements EventSubscriber +{ + 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 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); + } + } + + 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..8dfde62 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php @@ -0,0 +1,63 @@ +. +*/ + +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. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @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 ConnectionEventArgs $args + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $conn = $args->getConnection(); + $conn->exec($this->sql); + } + + 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..0200ce7 --- /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, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for adding table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableAddColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @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..bd59d7d --- /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, + Doctrine\DBAL\Schema\ColumnDiff, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for changing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableChangeColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\ColumnDiff + */ + private $_columnDiff = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @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..9f85338 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php @@ -0,0 +1,99 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @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..4b981f8 --- /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, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for removing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRemoveColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @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..90e6a38 --- /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, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for renaming table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRenameColumnEventArgs extends SchemaEventArgs +{ + /** + * @var string + */ + private $_oldColumnName = null; + + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @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..fecb015 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php @@ -0,0 +1,137 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection, + Doctrine\DBAL\Schema\Column; + +/** + * Event Arguments used when the portable column definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaColumnDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * Raw column data as fetched from the database + * + * @var array + */ + private $_tableColumn = null; + + /** + * @var string + */ + private $_table = null; + + /** + * @var string + */ + private $_database = null; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection = null; + + /** + * @param array $tableColumn + * @param string $table + * @param string $database + * @param \Doctrine\DBAL\Connection $conn + */ + 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 SchemaColumnDefinitionEventArgs + */ + public function setColumn(Column $column = null) + { + $this->_column = $column; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + 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..5e7383c --- /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, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating table columns are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @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..3149faa --- /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, + Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table = null; + + /** + * @var array + */ + private $_columns = null; + + /** + * @var array + */ + private $_options = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @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..55133be --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php @@ -0,0 +1,98 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when the SQL query for dropping tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaDropTableEventArgs extends SchemaEventArgs +{ + /** + * @var string|\Doctrine\DBAL\Schema\Table + */ + private $_table = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var string + */ + private $_sql = null; + + /** + * @param string|\Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct($table, AbstractPlatform $platform) + { + if ( ! $table instanceof Table && !is_string($table)) { + throw new \InvalidArgumentException('SchemaCreateTableEventArgs 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 + */ + 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..a3509fb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php @@ -0,0 +1,56 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs; + +/** + * Base class for schema related events. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @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..248d43e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php @@ -0,0 +1,122 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection, + Doctrine\DBAL\Schema\Index; + +/** + * Event Arguments used when the portable index definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaIndexDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Index + */ + private $_index = null; + + /** + * Raw index data as fetched from the database + * + * @var array + */ + private $_tableIndex = null; + + /** + * @var string + */ + private $_table = null; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection = null; + + /** + * @param array $tableIndex + * @param string $table + * @param \Doctrine\DBAL\Connection $conn + */ + 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 + */ + 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..0869dd9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Container for all DBAL events. + * + * This class cannot be instantiated. + * + * @author Roman Borschel + * @since 2.0 + */ +final class Events +{ + 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/Id/TableGenerator.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php new file mode 100644 index 0000000..c52a40b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php @@ -0,0 +1,160 @@ +. + */ + +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 Connection $conn + * @param string $generatorTableName + */ + 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; + } + + /** + * Generate the next unused value for the given sequence name + * + * @param string + * @return int + */ + 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 occured 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..e340397 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\DBAL\Id; + +use Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +class TableGeneratorSchemaVisitor implements \Doctrine\DBAL\Schema\Visitor\Visitor +{ + /** + * @var string + */ + private $generatorTableName; + + public function __construct($generatorTableName = 'sequences') + { + $this->generatorTableName = $generatorTableName; + } + + /** + * @param Schema $schema + */ + 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)); + } + + /** + * @param Table $table + */ + public function acceptTable(Table $table) + { + } + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * @param Sequence $sequence + */ + 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..52d87d2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php @@ -0,0 +1,42 @@ +. +*/ + +namespace Doctrine\DBAL; + +/** + * Contains all DBAL LockModes + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class LockMode +{ + const NONE = 0; + const OPTIMISTIC = 1; + const PESSIMISTIC_READ = 2; + const PESSIMISTIC_WRITE = 4; + + 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..7d70b90 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Includes executed SQLs in a Debug Stack + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DebugStack implements SQLLogger +{ + /** @var array $queries Executed SQL queries. */ + public $queries = array(); + + /** @var boolean $enabled If Debug Stack is enabled (log queries) or not. */ + public $enabled = true; + + public $start = null; + + 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..a332258 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php @@ -0,0 +1,61 @@ +. + */ + +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 + * @version $Revision$ + * @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..6725cc5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Chains multiple SQLLogger + * + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Christophe Coevoet + */ +class LoggerChain implements SQLLogger +{ + private $loggers = array(); + + /** + * Adds a logger in the chain + * + * @param SQLLogger $logger + */ + 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..9564f4c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Interface for SQL loggers. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @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 $params The SQL parameters. + * @param array $types The SQL parameter types. + * @return void + */ + public function startQuery($sql, array $params = null, array $types = null); + + /** + * Mark 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..e53b5f6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -0,0 +1,2857 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException, + Doctrine\DBAL\Connection, + Doctrine\DBAL\Types, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Index, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ColumnDiff, + Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Events, + Doctrine\Common\EventManager, + Doctrine\DBAL\Event\SchemaCreateTableEventArgs, + Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs, + Doctrine\DBAL\Event\SchemaDropTableEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs, + 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 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 + */ + protected $doctrineTypeMapping = null; + + /** + * Contains a list of all columns that should generate parseable column comments for type-detection + * in reverse engineering scenarios. + * + * @var array + */ + 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 + */ + 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; + } + + /** + * Gets the SQL snippet that declares a boolean column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getBooleanTypeDeclarationSQL(array $columnDef); + + /** + * Gets the SQL snippet that declares a 4 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getIntegerTypeDeclarationSQL(array $columnDef); + + /** + * Gets the SQL snippet that declares an 8 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getBigIntTypeDeclarationSQL(array $columnDef); + + /** + * Gets the SQL snippet that declares a 2 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getSmallIntTypeDeclarationSQL(array $columnDef); + + /** + * Gets 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(); + + /** + * Initialize Doctrine Type Mappings with the platform defaults + * and with all additional type mappings. + */ + private function initializeAllDoctrineTypeMappings() + { + $this->initializeDoctrineTypeMappings(); + + foreach (Type::getTypesMap() as $typeName => $className) { + foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) { + $this->doctrineTypeMapping[$dbType] = $typeName; + } + } + } + + /** + * Gets 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); + } + + /** + * Get the SQL Snippet to create a GUID/UUID field. + * + * By default this maps directly to a VARCHAR and only maps to more + * special datatypes when the underlying databases support this datatype. + * + * @param array $field + * + * @return string + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return $this->getVarcharTypeDeclarationSQL($field); + } + + /** + * @param integer $length + * @param boolean $fixed + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + throw DBALException::notSupported('VARCHARs not supported by Platform.'); + } + + /** + * Gets the SQL snippet used to declare a CLOB column type. + * + * @param array $field + * + * @return string + */ + abstract public function getClobTypeDeclarationSQL(array $field); + + /** + * Gets 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(); + + /** + * Register 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; + } + + /** + * Get the Doctrine type that is mapped for the given database column type. + * + * @param string $dbType + * + * @return string + */ + 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]; + } + + /** + * Check 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]); + } + + /** + * Initialize 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 Type $doctrineType + * + * @return boolean + */ + public function isCommentedDoctrineType(Type $doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + + return in_array($doctrineType->getName(), $this->doctrineTypeComments); + } + + /** + * Mark this type as to be commented in ALTER TABLE and CREATE TABLE statements. + * + * @param string|Type $doctrineType + * + * @return void + */ + public function markDoctrineTypeCommented($doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + + $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType; + } + + /** + * Get the comment to append to a column comment that helps parsing this type in reverse engineering. + * + * @param Type $doctrineType + * @return string + */ + public function getDoctrineTypeComment(Type $doctrineType) + { + return '(DC2Type:' . $doctrineType->getName() . ')'; + } + + /** + * Return the comment of a passed column modified by potential doctrine type comment hints. + * + * @param 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 all SQL wildcard characters of the platform. + * + * @return array + */ + public function getWildcards() + { + return array('%', '_'); + } + + /** + * Returns the regular expression operator. + * + * @return string + */ + public function getRegexpExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns global unique identifier + * + * @return string to get global unique identifier + */ + public function getGuidExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns 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 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 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 lowest value of a column + * + * @param string $column the column to use + * @return string + */ + public function getMinExpression($column) + { + return 'MIN(' . $column . ')'; + } + + /** + * Returns the total sum of a column + * + * @param string $column the column to use + * @return string + */ + public function getSumExpression($column) + { + return 'SUM(' . $column . ')'; + } + + // scalar functions + + /** + * Returns 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 length of a text field. + * + * @param string $column + * + * @return string + */ + public function getLengthExpression($column) + { + return 'LENGTH(' . $column . ')'; + } + + /** + * Returns 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 . ')'; + } + + /** + * Rounds 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 remainder of the division operation + * $expression1 / $expression2. + * + * @param string $expression1 + * @param string $expression2 + * + * @return string + */ + public function getModExpression($expression1, $expression2) + { + return 'MOD(' . $expression1 . ', ' . $expression2 . ')'; + } + + /** + * Trim a string, leading/trailing/both and with a given char which defaults to space. + * + * @param string $str + * @param integer $pos + * @param string $char has to be quoted already + * + * @return string + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $posStr = ''; + $trimChar = ($char != false) ? $char . ' FROM ' : ''; + + switch ($pos) { + case self::TRIM_LEADING: + $posStr = 'LEADING '.$trimChar; + break; + + case self::TRIM_TRAILING: + $posStr = 'TRAILING '.$trimChar; + break; + + case self::TRIM_BOTH: + $posStr = 'BOTH '.$trimChar; + break; + } + + return 'TRIM(' . $posStr . $str . ')'; + } + + /** + * rtrim + * returns the string $str with proceeding space characters removed + * + * @param string $str literal string or column name + * + * @return string + */ + public function getRtrimExpression($str) + { + return 'RTRIM(' . $str . ')'; + } + + /** + * ltrim + * returns the string $str with leading space characters removed + * + * @param string $str literal string or column name + * + * @return string + */ + public function getLtrimExpression($str) + { + return 'LTRIM(' . $str . ')'; + } + + /** + * upper + * Returns the string $str with all characters changed 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 . ')'; + } + + /** + * lower + * Returns the string $str with all characters changed 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 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 $startPos position to start at, beginning of string by default + * + * @return string + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the current system date. + * + * @return string + */ + public function getNowExpression() + { + return 'NOW()'; + } + + /** + * return string to call a function 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 $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 series of strings concatinated + * + * concat() accepts an arbitrary number of parameters. Each parameter + * must contain an expression + * + * @param string $arg1, $arg2 ... $argN strings that will be concatenated. + * + * @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 a logical expression + */ + public function getNotExpression($expression) + { + return 'NOT(' . $expression . ')'; + } + + /** + * Returns the SQL to check if a value is one in a set of + * given values. + * + * in() accepts an arbitrary number of parameters. The first parameter + * must always specify the value that should be matched against. Successive + * must contain a logical expression or an array with logical expressions. + * These expressions will be matched against the first parameter. + * + * @param string $column the value that should be matched against + * @param string|array $values values that will be matched against $column + * + * @return string logical expression + */ + public function getInExpression($column, $values) + { + if ( ! is_array($values)) { + $values = array($values); + } + + // TODO: fix this code: the method does not exist + $values = $this->getIdentifiers($values); + + if (count($values) == 0) { + throw new \InvalidArgumentException('Values must not be empty.'); + } + + return $column . ' IN (' . implode(', ', $values) . ')'; + } + + /** + * Returns SQL that checks if a expression is null. + * + * @param string $expression the expression that should be compared to null + * + * @return string logical expression + */ + public function getIsNullExpression($expression) + { + return $expression . ' IS NULL'; + } + + /** + * Returns SQL that checks if a expression is not null. + * + * @param string $expression the expression that should be compared to null + * + * @return string logical expression + */ + public function getIsNotNullExpression($expression) + { + return $expression . ' IS NOT NULL'; + } + + /** + * Returns 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 logical expression + */ + public function getBetweenExpression($expression, $value1, $value2) + { + return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2; + } + + public function getAcosExpression($value) + { + return 'ACOS(' . $value . ')'; + } + + public function getSinExpression($value) + { + return 'SIN(' . $value . ')'; + } + + public function getPiExpression() + { + return 'PI()'; + } + + public function getCosExpression($value) + { + return 'COS(' . $value . ')'; + } + + /** + * Calculate the difference in days between the two passed dates. + * + * Computes diff = date1 - date2 + * + * @param string $date1 + * @param string $date2 + * + * @return string + */ + public function getDateDiffExpression($date1, $date2) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Add the number of given days to a date. + * + * @param string $date + * @param integer $days + * + * @return string + */ + public function getDateAddDaysExpression($date, $days) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Substract the number of given days to a date. + * + * @param string $date + * @param integer $days + * + * @return string + */ + public function getDateSubDaysExpression($date, $days) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Add the number of given months to a date. + * + * @param string $date + * @param integer $months + * + * @return string + */ + public function getDateAddMonthExpression($date, $months) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Substract the number of given months to a date. + * + * @param string $date + * @param integer $months + * + * @return string + */ + public function getDateSubMonthExpression($date, $months) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Gets SQL bit AND comparison expression + * + * @param string $value1 + * @param string $value2 + * + * @return string + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' & ' . $value2 . ')'; + } + + /** + * Gets SQL bit OR comparison expression + * + * @param string $value1 + * @param string $value2 + * + * @return string + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' | ' . $value2 . ')'; + } + + 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 + * @param integer $lockMode + * + * @return string + */ + public function appendLockHint($fromClause, $lockMode) + { + return $fromClause; + } + + /** + * Get the sql snippet to append to any SELECT statement which locks rows in shared read lock. + * + * This defaults to the ASNI 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(); + } + + /** + * Get 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 ASNI SQL standard. + * + * @return string + */ + public function getWriteLockSQL() + { + return $this->getForUpdateSQL(); + } + + /** + * Get the SQL snippet to drop an existing database + * + * @param string $database name of the database that should be dropped + * + * @return string + */ + public function getDropDatabaseSQL($database) + { + return 'DROP DATABASE ' . $database; + } + + /** + * Drop a Table + * + * @throws \InvalidArgumentException + * + * @param Table|string $table + * + * @return string + */ + public function getDropTableSQL($table) + { + $tableArg = $table; + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } else if(!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; + } + + /** + * Get SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction. + * + * @param Table|string $table + * + * @return string + */ + public function getDropTemporaryTableSQL($table) + { + return $this->getDropTableSQL($table); + } + + /** + * Drop index from a table + * + * @param Index|string $name + * @param string|Table $table + * + * @return string + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } else if(!is_string($index)) { + throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + return 'DROP INDEX ' . $index; + } + + /** + * Get drop constraint sql + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param string|Table $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; + } + + /** + * @param ForeignKeyConstraint|string $foreignKey + * @param 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; + } + + /** + * Gets the SQL statement(s) to create a table with the specified name, columns and constraints + * on this platform. + * + * @param string $table The name of the table. + * @param integer $createFlags + * + * @return array The sequence of SQL statements. + */ + 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()) { + $platform = $this; + $options['primary'] = array_map(function ($columnName) use ($table, $platform) { + return $table->getColumn($columnName)->getQuotedName($platform); + }, $index->getColumns()); + $options['primary_index'] = $index; + } else { + $options['indexes'][$index->getName()] = $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 = array(); + $columnData['name'] = $column->getQuotedName($this); + $columnData['type'] = $column->getType(); + $columnData['length'] = $column->getLength(); + $columnData['notnull'] = $column->getNotNull(); + $columnData['fixed'] = $column->getFixed(); + $columnData['unique'] = false; // TODO: what do we do about this? + $columnData['version'] = $column->hasPlatformOption("version") ? $column->getPlatformOption('version') : false; + + if (strtolower($columnData['type']) == "string" && $columnData['length'] === null) { + $columnData['length'] = 255; + } + + $columnData['unsigned'] = $column->getUnsigned(); + $columnData['precision'] = $column->getPrecision(); + $columnData['scale'] = $column->getScale(); + $columnData['default'] = $column->getDefault(); + $columnData['columnDefinition'] = $column->getColumnDefinition(); + $columnData['autoincrement'] = $column->getAutoincrement(); + $columnData['comment'] = $this->getColumnComment($column); + + 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) { + if ($this->getColumnComment($column)) { + $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getName(), $this->getColumnComment($column)); + } + } + } + + return array_merge($sql, $columnSql); + } + + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + return "COMMENT ON COLUMN " . $tableName . "." . $columnName . " IS '" . $comment . "'"; + } + + /** + * Gets 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; + } + + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE TEMPORARY TABLE"; + } + + /** + * Gets the SQL to create a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + * + * @throws DBALException + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Gets the SQL statement to change a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Gets the SQL to create a constraint on a table on this platform. + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param string|Table $table + * + * @return string + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this); + + $columns = array(); + foreach ($constraint->getColumns() as $column) { + $columns[] = $column; + } + $columnList = '('. implode(', ', $columns) . ')'; + + $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().' + ); + } + } else if ($constraint instanceof ForeignKeyConstraint) { + $query .= ' FOREIGN KEY'; + + $foreignColumns = array(); + foreach ($constraint->getForeignColumns() as $column) { + $foreignColumns[] = $column; + } + + $referencesClause = ' REFERENCES '.$constraint->getForeignTableName(). ' ('.implode(', ', $foreignColumns).')'; + } + $query .= ' '.$columnList.$referencesClause; + + return $query; + } + + /** + * Gets the SQL to create an index on a table on this platform. + * + * @param Index $index + * @param string|Table $table name of the table on which the index is to be created + * + * @return string + */ + public function getCreateIndexSQL(Index $index, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + $name = $index->getQuotedName($this); + $columns = $index->getColumns(); + + 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) . ')'; + + return $query; + } + + /** + * Adds additional flags for index generation + * + * @param Index $index + * + * @return string + */ + protected function getCreateIndexSQLFlags(Index $index) + { + return $index->isUnique() ? 'UNIQUE ' : ''; + } + + /** + * Get SQL to create an unnamed primary key constraint. + * + * @param Index $index + * @param string|Table $table + * + * @return string + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getColumns()) . ')'; + } + + /** + * 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 identifier name to be quoted + * + * @return string quoted identifier string + */ + public function quoteIdentifier($str) + { + if (strpos($str, ".") !== false) { + $parts = array_map(array($this, "quoteIdentifier"), explode(".", $str)); + + return implode(".", $parts); + } + + return $this->quoteSingleIdentifier($str); + } + + /** + * Quote a single identifier (no dot chain separation) + * + * @param string $str + * + * @return string + */ + public function quoteSingleIdentifier($str) + { + $c = $this->getIdentifierQuoteCharacter(); + + return $c . str_replace($c, $c.$c, $str) . $c; + } + + /** + * Create a new foreign key + * + * @param ForeignKeyConstraint $foreignKey ForeignKey instance + * @param string|Table $table 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. + * + * The method returns an array of sql statements, since some platforms need several statements. + * + * @param TableDiff $diff + * + * @return array + */ + public function getAlterTableSQL(TableDiff $diff) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param Column $column + * @param 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 Column $column + * @param 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 ColumnDiff $columnDiff + * @param 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 Column $column + * @param 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 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(); + } + + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $tableName = $diff->name; + + $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; + } + + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $tableName = false !== $diff->newName ? $diff->newName : $diff->name; + + $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); + } + + return $sql; + } + + /** + * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions. + * + * @param TableDiff $diff + * + * @return array + */ + protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff) + { + return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff)); + } + + /** + * Get 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); + } + + /** + * Obtain DBMS specific SQL code portion needed to declare a generic type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param array $field 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 '" . $field['comment'] . "'"; + } + + return $name . ' ' . $columnDef; + } + + /** + * Gets 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'] . ')'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set a default value + * declaration to be used in statements like CREATE TABLE. + * + * @param array $field 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", "BigInteger", "SmallInteger"))) { + $default = " DEFAULT ".$field['default']; + } else if ((string)$field['type'] == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL()) { + $default = " DEFAULT ".$this->getCurrentTimestampSQL(); + } else if ((string) $field['type'] == 'Boolean') { + $default = " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; + } + } + } + return $default; + } + + /** + * Obtain DBMS specific SQL code portion needed to set a CHECK constraint + * declaration to be used in statements like CREATE TABLE. + * + * @param array $definition 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); + } + + /** + * Obtain DBMS specific SQL code portion needed to set a unique + * constraint declaration to be used in statements like CREATE TABLE. + * + * @param string $name name of the unique constraint + * @param Index $index index definition + * + * @return string DBMS specific SQL code portion needed + * to set a constraint + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + if (count($index->getColumns()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return 'CONSTRAINT ' . $name . ' UNIQUE (' + . $this->getIndexFieldDeclarationListSQL($index->getColumns()) + . ')'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param string $name name of the index + * @param Index $index index definition + * + * @return string DBMS specific SQL code portion needed to set an index + */ + public function getIndexDeclarationSQL($name, Index $index) + { + $type = ''; + + if ($index->isUnique()) { + $type = 'UNIQUE '; + } + + if (count($index->getColumns()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return $type . 'INDEX ' . $name . ' (' + . $this->getIndexFieldDeclarationListSQL($index->getColumns()) + . ')'; + } + + /** + * getCustomTypeDeclarationSql + * Obtail 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']; + } + + /** + * getIndexFieldDeclarationList + * Obtain 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); + } + + /** + * A method to return 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; + } + + /** + * Get sql query to show a list of database. + * + * @return string + */ + public function getShowDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * 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; + } + + /** + * Return the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param ForeignKeyConstraint $foreignKey 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 given referential action in uppercase if valid, otherwise throws + * an exception + * + * @throws \InvalidArgumentException if unknown referential action given + * + * @param string $action foreign key referential action + * + * @return string + */ + 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); + } + } + + /** + * 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 ForeignKeyConstraint $foreignKey + * + * @return string + */ + 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->getLocalColumns()) + . ') REFERENCES ' + . $foreignKey->getQuotedForeignTableName($this) . ' (' + . implode(', ', $foreignKey->getForeignColumns()) . ')'; + + return $sql; + } + + /** + * Obtain 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'; + } + + /** + * Obtain 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 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 ''; + } + + /** + * Obtain 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 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 ''; + } + + /** + * 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). + * + * @param mixed $item + * + * @return mixed + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $k => $value) { + if (is_bool($value)) { + $item[$k] = (int) $value; + } + } + } else if (is_bool($item)) { + $item = (int) $item; + } + + return $item; + } + + /** + * Gets the SQL specific for the platform to get the current date. + * + * @return string + */ + public function getCurrentDateSQL() + { + return 'CURRENT_DATE'; + } + + /** + * Gets the SQL specific for the platform to get the current time. + * + * @return string + */ + public function getCurrentTimeSQL() + { + return 'CURRENT_TIME'; + } + + /** + * Gets the SQL specific for the platform to get the current timestamp + * + * @return string + */ + public function getCurrentTimestampSQL() + { + return 'CURRENT_TIMESTAMP'; + } + + /** + * Get sql for transaction isolation level Connection constant + * + * @param integer $level + * + * @return string + */ + 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); + } + } + + public function getListDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListSequencesSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableConstraintsSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableColumnsSQL($table, $database = null) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTablesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListUsersSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get the SQL to list all views of a database or user. + * + * @param string $database + * + * @return string + */ + public function getListViewsSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get 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 + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableForeignKeysSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getCreateViewSQL($name, $sql) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getDropViewSQL($name) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get the SQL snippet to drop an existing sequence + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + */ + public function getDropSequenceSQL($sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getSequenceNextValSQL($sequenceName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * create a new database + * + * @param string $database name of the database that should be created + * + * @return string + */ + public function getCreateDatabaseSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get sql to set the transaction isolation level + * + * @param integer $level + * + * @return string + */ + public function getSetTransactionIsolationSQL($level) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtain DBMS specific SQL to be used to create datetime fields in + * statements like CREATE TABLE + * + * @param array $fieldDeclaration + * + * @return string + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtain 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); + } + + + /** + * Obtain DBMS specific SQL to be used to create date fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtain DBMS specific SQL to be used to create time fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + 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 recieve an auto-generated value from the + * database on insert of a row. + * + * @return boolean + */ + public function supportsIdentityColumns() + { + return false; + } + + /** + * Whether the platform supports indexes. + * + * @return boolean + */ + public function supportsIndexes() + { + return true; + } + + /** + * 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; + } + + /** + * Does the platform supports foreign key constraints? + * + * @return boolean + */ + public function supportsForeignKeyConstraints() + { + return true; + } + + /** + * Does 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; + } + + /** + * Can this platform 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; + } + + /** + * 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; + } + + /** + * Does this plaform support to add inline column comments as postfix. + * + * @return boolean + */ + public function supportsInlineColumnComments() + { + return false; + } + + /** + * Does this platform support the propriortary synatx "COMMENT ON asset" + * + * @return boolean + */ + public function supportsCommentOnStatement() + { + return false; + } + + public function getIdentityColumnNullInsertSQL() + { + return ""; + } + + /** + * Does this platform views ? + * + * @return boolean + */ + public function supportsViews() + { + return true; + } + + /** + * 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'; + } + + /** + * Modify limit query + * + * @param string $query + * @param integer $limit + * @param integer $offset + * + * @return string + */ + 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 $limit + * @param integer $offset + * + * @return string + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ($limit !== null) { + $query .= ' LIMIT ' . $limit; + } + + if ($offset !== null) { + $query .= ' OFFSET ' . $offset; + } + + return $query; + } + + /** + * Does 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 databse identifier, like tables or column names. + * + * @return integer + */ + public function getMaxIdentifierLength() + { + return 63; + } + + /** + * Get the insert sql for an empty insert statement + * + * @param string $tableName + * @param string $identifierColumnName + * + * @return string $sql + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)'; + } + + /** + * Generate 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'; + } + + /** + * Generate SQL to create a new savepoint + * + * @param string $savepoint + * + * @return string + */ + public function createSavePoint($savepoint) + { + return 'SAVEPOINT ' . $savepoint; + } + + /** + * Generate SQL to release a savepoint + * + * @param string $savepoint + * + * @return string + */ + public function releaseSavePoint($savepoint) + { + return 'RELEASE SAVEPOINT ' . $savepoint; + } + + /** + * Generate SQL to rollback a savepoint + * + * @param string $savepoint + * + * @return string + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TO SAVEPOINT ' . $savepoint; + } + + /** + * Return the keyword list instance of this platform. + * + * Throws exception if no keyword list is specified. + * + * @throws DBALException + * + * @return \Doctrine\DBAL\Platforms\Keywords\KeywordList + */ + 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; + } + + /** + * The class name of the reserved keywords list. + * + * @return string + */ + protected function getReservedKeywordsClass() + { + throw DBALException::notSupported(__METHOD__); + } +} 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..114cd7d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php @@ -0,0 +1,545 @@ +. +*/ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\TableDiff; + +class DB2Platform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@inheritDoc} + */ + public function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'smallint' => 'smallint', + 'bigint' => 'bigint', + 'integer' => 'integer', + 'time' => 'time', + 'date' => 'date', + 'varchar' => 'string', + 'character' => 'string', + 'clob' => 'text', + '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} + */ + 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 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'; + } + + public function getListDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListSequencesSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableConstraintsSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * This code fragment is originally from the Zend_Db_Adapter_Db2 class. + * + * @license New BSD License + * @param string $table + * @param string $database + * @return string + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno, + c.typename, c.default, c.nulls, c.length, c.scale, + c.identity, tc.type AS tabconsttype, k.colseq + 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"; + } + + public function getListTablesSQL() + { + return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'"; + } + + public function getListUsersSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT NAME, COLNAMES, UNIQUERULE FROM SYSIBM.SYSINDEXES WHERE TBNAME = UPPER('" . $table . "')"; + } + + public function getListTableForeignKeysSQL($table) + { + return "SELECT TBNAME, RELNAME, REFTBNAME, DELETERULE, UPDATERULE, FKCOLNAMES, PKCOLNAMES ". + "FROM SYSIBM.SYSRELS WHERE TBNAME = UPPER('".$table."')"; + } + + public function getCreateViewSQL($name, $sql) + { + return "CREATE VIEW ".$name." AS ".$sql; + } + + public function getDropViewSQL($name) + { + return "DROP VIEW ".$name; + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getSequenceNextValSQL($sequenceName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@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 'VALUES CURRENT DATE'; + } + + /** + * {@inheritDoc} + */ + public function getCurrentTimeSQL() + { + return 'VALUES CURRENT TIME'; + } + + /** + * {@inheritDoc} + */ + public function getCurrentTimestampSQL() + { + return "VALUES CURRENT TIMESTAMP"; + } + + /** + * {@inheritDoc} + */ + public function getIndexDeclarationSQL($name, Index $index) + { + return $this->getUniqueConstraintDeclarationSQL($name, $index); + } + + /** + * {@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(); + + $queryParts = array(); + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + 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; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $queryParts[] = 'ALTER ' . ($columnDiff->oldColumnName) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'RENAME ' . $oldColumnName . ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . implode(" ", $queryParts); + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff)); + + if ($diff->newName !== false) { + $sql[] = 'RENAME TABLE TO ' . $diff->newName; + } + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + if (isset($field['notnull']) && $field['notnull'] && !isset($field['default'])) { + if (in_array((string)$field['type'], array("Integer", "BigInteger", "SmallInteger"))) { + $field['default'] = 0; + } else if((string)$field['type'] == "DateTime") { + $field['default'] = "00-00-00 00:00:00"; + } else if ((string)$field['type'] == "Date") { + $field['default'] = "00-00-00"; + } else if((string)$field['type'] == "Time") { + $field['default'] = "00:00:00"; + } else { + $field['default'] = ''; + } + } + + unset($field['default']); // @todo this needs fixing + 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)'; + } + + 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); + } + + 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..47bb364 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php @@ -0,0 +1,495 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException, + Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Index, + Doctrine\DBAL\Schema\Table; + +/** + * 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} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return 'DATE_ADD(' . $date . ', INTERVAL ' . $days . ' DAY)'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return 'DATE_SUB(' . $date . ', INTERVAL ' . $days . ' DAY)'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return 'DATE_ADD(' . $date . ', INTERVAL ' . $months . ' MONTH)'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return 'DATE_SUB(' . $date . ', INTERVAL ' . $months . ' MONTH)'; + } + + /** + * {@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 initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'boolean' => 'boolean', + 'varchar' => 'string', + 'integer' => 'integer', + 'blob' => 'text', + '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; + } + + 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'; + } + + public function getListTablesSQL() + { + return "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE' AND TABLE_SCHEMA=DATABASE()"; + } + + 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" . + " FROM DATA_DICTIONARY.COLUMNS" . + " WHERE TABLE_SCHEMA=" . $database . " AND TABLE_NAME = '" . $table . "'"; + } + + 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 getDropIndexSQL($index, $table=null) + { + if ($index instanceof Index) { + $indexName = $index->getQuotedName($this); + } else if (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); + } else if(!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; + } + + /** + * @param Index $index + * @param Table $table + * + * @return string + */ + 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->newName; + } + + 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(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->oldColumnName) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + $sql = array(); + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . 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); + } else if(!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'; + } + } + } else if (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..77c1c67 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php @@ -0,0 +1,438 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * DB2 Keywords + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Keywords extends KeywordList +{ + public function getName() + { + return 'DB2'; + } + + 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..c6d3187 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php @@ -0,0 +1,340 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Drizzle Keywordlist + * + * @author Kim Hemsø Rasmussen + */ +class DrizzleKeywords extends KeywordList +{ + public function getName() + { + return 'drizzle'; + } + + 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..f30bb36 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php @@ -0,0 +1,63 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Abstract interface for a SQL reserved keyword dictionary. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +abstract class KeywordList +{ + private $keywords = null; + + /** + * Check if the given word is a keyword of this dialect/vendor platform. + * + * @param string $word + * @return bool + */ + public function isKeyword($word) + { + if ($this->keywords === null) { + $this->initializeKeywords(); + } + + return isset($this->keywords[strtoupper($word)]); + } + + protected function initializeKeywords() + { + $this->keywords = array_flip(array_map('strtoupper', $this->getKeywords())); + } + + abstract protected function getKeywords(); + + /** + * 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..8adac11 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php @@ -0,0 +1,243 @@ +. + */ + + +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 + */ +class MsSQLKeywords extends KeywordList +{ + public function getName() + { + return 'MsSQL'; + } + + protected function getKeywords() + { + return array( + 'ADD', + 'CURRENT_TIMESTAMP', + 'GROUP', + 'OPENQUERY', + 'SERIALIZABLE', + 'ALL', + 'CURRENT_USER', + 'HAVING', + 'OPENROWSET', + 'SESSION_USER', + 'ALTER', + 'CURSOR', + 'HOLDLOCK', + 'OPTION', + 'SET', + 'AND', + 'DATABASE', + 'IDENTITY', + 'OR', + 'SETUSER', + 'ANY', + 'DBCC', + 'IDENTITYCOL', + 'ORDER', + 'SHUTDOWN', + 'AS', + 'DEALLOCATE', + 'IDENTITY_INSERT', + 'OUTER', + 'SOME', + 'ASC', + 'DECLARE', + 'IF', + 'OVER', + 'STATISTICS', + 'AUTHORIZATION', + 'DEFAULT', + 'IN', + 'PERCENT', + 'SUM', + 'AVG', + 'DELETE', + 'INDEX', + 'PERM', + 'SYSTEM_USER', + 'BACKUP', + 'DENY', + 'INNER', + 'PERMANENT', + 'TABLE', + 'BEGIN', + 'DESC', + 'INSERT', + 'PIPE', + 'TAPE', + 'BETWEEN', + 'DISK', + 'INTERSECT', + 'PLAN', + 'TEMP', + 'BREAK', + 'DISTINCT', + 'INTO', + 'PRECISION', + 'TEMPORARY', + 'BROWSE', + 'DISTRIBUTED', + 'IS', + 'PREPARE', + 'TEXTSIZE', + 'BULK', + 'DOUBLE', + 'ISOLATION', + 'PRIMARY', + 'THEN', + 'BY', + 'DROP', + 'JOIN', + 'PRINT', + 'TO', + 'CASCADE', + 'DUMMY', + 'KEY', + 'PRIVILEGES', + 'TOP', + 'CASE', + 'DUMP', + 'KILL', + 'PROC', + 'TRAN', + 'CHECK', + 'ELSE', + 'LEFT', + 'PROCEDURE', + 'TRANSACTION', + 'CHECKPOINT', + 'END', + 'LEVEL', + 'PROCESSEXIT', + 'TRIGGER', + 'CLOSE', + 'ERRLVL', + 'LIKE', + 'PUBLIC', + 'TRUNCATE', + 'CLUSTERED', + 'ERROREXIT', + 'LINENO', + 'RAISERROR', + 'TSEQUAL', + 'COALESCE', + 'ESCAPE', + 'LOAD', + 'READ', + 'UNCOMMITTED', + 'COLUMN', + 'EXCEPT', + 'MAX', + 'READTEXT', + 'UNION', + 'COMMIT', + 'EXEC', + 'MIN', + 'RECONFIGURE', + 'UNIQUE', + 'COMMITTED', + 'EXECUTE', + 'MIRROREXIT', + 'REFERENCES', + 'UPDATE', + 'COMPUTE', + 'EXISTS', + 'NATIONAL', + 'REPEATABLE', + 'UPDATETEXT', + 'CONFIRM', + 'EXIT', + 'NOCHECK', + 'REPLICATION', + 'USE', + 'CONSTRAINT', + 'FETCH', + 'NONCLUSTERED', + 'RESTORE', + 'USER', + 'CONTAINS', + 'FILE', + 'NOT', + 'RESTRICT', + 'VALUES', + 'CONTAINSTABLE', + 'FILLFACTOR', + 'NULL', + 'RETURN', + 'VARYING', + 'CONTINUE', + 'FLOPPY', + 'NULLIF', + 'REVOKE', + 'VIEW', + 'CONTROLROW', + 'FOR', + 'OF', + 'RIGHT', + 'WAITFOR', + 'CONVERT', + 'FOREIGN', + 'OFF', + 'ROLLBACK', + 'WHEN', + 'COUNT', + 'FREETEXT', + 'OFFSETS', + 'ROWCOUNT', + 'WHERE', + 'CREATE', + 'FREETEXTTABLE', + 'ON', + 'ROWGUIDCOL', + 'WHILE', + 'CROSS', + 'FROM', + 'ONCE', + 'RULE', + 'WITH', + 'CURRENT', + 'FULL', + 'ONLY', + 'SAVE', + 'WORK', + 'CURRENT_DATE', + 'GOTO', + 'OPEN', + 'SCHEMA', + 'WRITETEXT', + 'CURRENT_TIME', + 'GRANT', + 'OPENDATASOURCE', + 'SELECT', + ); + } +} 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..c4ad5d6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php @@ -0,0 +1,269 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MySQL 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 + */ +class MySQLKeywords extends KeywordList +{ + public function getName() + { + return 'MySQL'; + } + + 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..9f34ba6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php @@ -0,0 +1,157 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Oracle 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 + */ +class OracleKeywords extends KeywordList +{ + public function getName() + { + return 'Oracle'; + } + + 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/PostgreSQLKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php new file mode 100644 index 0000000..7950f6a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php @@ -0,0 +1,131 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL Keywordlist + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author Marcelo Santos Araujo + */ +class PostgreSQLKeywords extends KeywordList +{ + public function getName() + { + return 'PostgreSQL'; + } + + 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..a61922b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php @@ -0,0 +1,116 @@ +. + */ + + +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(); + + public function __construct(array $keywordLists) + { + $this->keywordLists = $keywordLists; + } + + 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; + } + + private function addViolation($asset, $violatedPlatforms) + { + if ( ! $violatedPlatforms) { + return; + } + + $this->violations[] = $asset . ' keyword violations: ' . implode(', ', $violatedPlatforms); + } + + public function acceptColumn(Table $table, Column $column) + { + $this->addViolation( + 'Table ' . $table->getName() . ' column ' . $column->getName(), + $this->isReservedWord($column->getName()) + ); + } + + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + + } + + public function acceptIndex(Table $table, Index $index) + { + + } + + public function acceptSchema(Schema $schema) + { + + } + + public function acceptSequence(Sequence $sequence) + { + + } + + 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/SQLiteKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php new file mode 100644 index 0000000..d45b994 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php @@ -0,0 +1,164 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SQLite Keywords + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class SQLiteKeywords extends KeywordList +{ + public function getName() + { + return 'SQLite'; + } + + 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/MySqlPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php new file mode 100644 index 0000000..2cf573f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -0,0 +1,722 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException, + Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Index, + Doctrine\DBAL\Schema\Table; + +/** + * 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 +{ + /** + * {@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} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return 'DATE_ADD(' . $date . ', INTERVAL ' . $days . ' DAY)'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return 'DATE_SUB(' . $date . ', INTERVAL ' . $days . ' DAY)'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return 'DATE_ADD(' . $date . ', INTERVAL ' . $months . ' MONTH)'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return 'DATE_SUB(' . $date . ', INTERVAL ' . $months . ' MONTH)'; + } + + public function getListDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + 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". + * + * @param string $table + * @param string $currentDatabase + * @return string + */ + 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; + } + + public function getListViewsSQL($database) + { + return "SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = '".$database."'"; + } + + 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; + } + + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + 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} + */ + public function getClobTypeDeclarationSQL(array $field) + { + if ( ! empty($field['length']) && is_numeric($field['length'])) { + $length = $field['length']; + if ($length <= 255) { + return 'TINYTEXT'; + } + + if ($length <= 65532) { + return 'TEXT'; + } + + if ($length <= 16777215) { + 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. + * + * @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 'COLLATE ' . $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 getShowDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + public function getListTablesSQL() + { + return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"; + } + + public function getListTableColumnsSQL($table, $database = null) + { + if ($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 CollactionName ". + "FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '" . $database . "' AND TABLE_NAME = '" . $table . "'"; + } + + return 'DESCRIBE ' . $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 . ') '; + + if (isset($options['comment'])) { + $comment = trim($options['comment'], " '"); + + $query .= sprintf("COMMENT = '%s' ", str_replace("'", "''", $comment)); + } + + if ( ! isset($options['charset'])) { + $options['charset'] = 'utf8'; + } + + if ( ! isset($options['collate'])) { + $options['collate'] = 'utf8_unicode_ci'; + } + + $query .= 'DEFAULT CHARACTER SET ' . $options['charset']; + $query .= ' COLLATE ' . $options['collate']; + + if ( ! isset($options['engine'])) { + $options['engine'] = 'InnoDB'; + } + $query .= ' ENGINE = ' . $options['engine']; + + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = array(); + $queryParts = array(); + if ($diff->newName !== false) { + $queryParts[] = 'RENAME TO ' . $diff->newName; + } + + 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(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->oldColumnName) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + $sql = array(); + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . 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->name; + + foreach ($diff->removedIndexes as $remKey => $remIndex) { + + foreach ($diff->addedIndexes as $addKey => $addIndex) { + if ($remIndex->getColumns() == $addIndex->getColumns()) { + + $columns = $addIndex->getColumns(); + $type = ''; + if ($addIndex->isUnique()) { + $type = 'UNIQUE '; + } + + $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', '; + $query .= 'ADD ' . $type . 'INDEX ' . $addIndex->getName(); + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')'; + + $sql[] = $query; + + unset($diff->removedIndexes[$remKey]); + unset($diff->addedIndexes[$addKey]); + + break; + } + } + } + + $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff)); + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } else if ($index->hasFlag('fulltext')) { + $type .= 'FULLTEXT '; + } + + 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); + } else if(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); + } else if(!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' => 'blob', + 'varbinary' => 'blob', + 'set' => 'simple_array', + ); + } + + /** + * {@inheritDoc} + */ + public function getVarcharMaxLength() + { + 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); + } else if(!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 getBlobTypeDeclarationSQL(array $field) + { + 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..cd5c774 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php @@ -0,0 +1,822 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\DBALException; + +/** + * 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 + * + * @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} + * + * 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 getDateAddDaysExpression($date, $days) + { + return '(' . $date . '+' . $days . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return '(' . $date . '-' . $days . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return "ADD_MONTHS(" . $date . ", " . $months . ")"; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return "ADD_MONTHS(" . $date . ", -" . $months . ")"; + } + + /** + * {@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(); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@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} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + public function getListDatabasesSQL() + { + return 'SELECT username FROM all_users'; + } + + public function getListSequencesSQL($database) + { + return "SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ". + "WHERE SEQUENCE_OWNER = '".strtoupper($database)."'"; + } + + /** + * {@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 = strtoupper($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' ORDER BY uind_col.column_position ASC"; + } + + public function getListTablesSQL() + { + return 'SELECT * FROM sys.user_tables'; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return 'SELECT view_name, text FROM sys.user_views'; + } + + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + public function getCreateAutoincrementSql($name, $table, $start = 1) + { + $table = strtoupper($table); + $sql = array(); + + $indexName = $table . '_AI_PK'; + + $idx = new Index($indexName, array($name), true, true); + + $sql[] = 'DECLARE + constraints_Count NUMBER; +BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = \''.$table.'\' AND CONSTRAINT_TYPE = \'P\'; + IF constraints_Count = 0 OR constraints_Count = \'\' THEN + EXECUTE IMMEDIATE \''.$this->getCreateConstraintSQL($idx, $table).'\'; + END IF; +END;'; + + $sequenceName = $table . '_SEQ'; + $sequence = new Sequence($sequenceName, $start); + $sql[] = $this->getCreateSequenceSQL($sequence); + + $triggerName = $table . '_AI_PK'; + $sql[] = 'CREATE TRIGGER ' . $triggerName . ' + BEFORE INSERT + ON ' . $table . ' + FOR EACH ROW +DECLARE + last_Sequence NUMBER; + last_InsertID NUMBER; +BEGIN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $name . ' FROM DUAL; + IF (:NEW.' . $name . ' IS NULL OR :NEW.'.$name.' = 0) THEN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $name . ' FROM DUAL; + ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = \'' . $sequenceName . '\'; + SELECT :NEW.' . $name . ' 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; + } + + public function getDropAutoincrementSql($table) + { + $table = strtoupper($table); + $trigger = $table . '_AI_PK'; + + $sql[] = 'DROP TRIGGER ' . $trigger; + $sql[] = $this->getDropSequenceSQL($table.'_SEQ'); + + $indexName = $table . '_AI_PK'; + $sql[] = $this->getDropConstraintSQL($indexName, $table); + + return $sql; + } + + public function getListTableForeignKeysSQL($table) + { + $table = strtoupper($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."'"; + } + + public function getListTableConstraintsSQL($table) + { + $table = strtoupper($table); + return 'SELECT * FROM user_constraints WHERE table_name = \'' . $table . '\''; + } + + public function getListTableColumnsSQL($table, $database = null) + { + $table = strtoupper($table); + + $tabColumnsTableName = "user_tab_columns"; + $ownerCondition = ''; + + if (null !== $database){ + $database = strtoupper($database); + $tabColumnsTableName = "all_tab_columns"; + $ownerCondition = "AND c.owner = '".$database."'"; + } + + return "SELECT c.*, d.comments FROM $tabColumnsTableName c ". + "INNER JOIN user_col_comments d ON d.TABLE_NAME = c.TABLE_NAME AND d.COLUMN_NAME = c.COLUMN_NAME ". + "WHERE c.table_name = '" . $table . "' ".$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 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->name, $column->getName(), $comment); + } + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ADD (' . implode(', ', $fields) . ')'; + } + + $fields = array(); + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $column = $columnDiff->column; + $fields[] = $column->getQuotedName($this). ' ' . $this->getColumnDeclarationSQL('', $column->toArray()); + if ($columnDiff->hasChanged('comment') && $comment = $this->getColumnComment($column)) { + $commentsSQL[] = $this->getCommentOnColumnSQL($diff->name, $column->getName(), $comment); + } + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' MODIFY (' . implode(', ', $fields) . ')'; + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME COLUMN ' . $oldColumnName .' 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->name . ' DROP (' . implode(', ', $fields).')'; + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME TO ' . $diff->newName; + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff), $commentsSQL); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + public function prefersSequences() + { + return true; + } + + /** + * {@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); + } + + 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', + 'long' => 'string', + 'clob' => 'text', + 'nclob' => 'text', + 'raw' => 'text', + 'long raw' => 'text', + '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/PostgreSqlPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php new file mode 100644 index 0000000..2f907a2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php @@ -0,0 +1,767 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Table; + +/** + * PostgreSqlPlatform. + * + * @since 2.0 + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @todo Rename: PostgreSQLPlatform + */ +class PostgreSqlPlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTR(' . $value . ', ' . $from . ')'; + } + + return 'SUBSTR(' . $value . ', ' . $from . ', ' . $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} + */ + public function getDateDiffExpression($date1, $date2) + { + return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return "(" . $date ." + (" . $days . " || ' day')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return "(" . $date ." - (" . $days . " || ' day')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return "(" . $date ." + (" . $months . " || ' month')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return "(" . $date ." - (" . $months . " || ' month')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function prefersSequences() + { + return true; + } + + public function getListDatabasesSQL() + { + return 'SELECT datname FROM pg_database'; + } + + public function getListSequencesSQL($database) + { + return "SELECT + c.relname, n.nspname AS schemaname + FROM + pg_class c, pg_namespace n + WHERE relkind = 'S' AND n.oid = c.relnamespace AND + (n.nspname NOT LIKE 'pg_%' AND n.nspname != 'information_schema')"; + } + + public function getListTablesSQL() + { + return "SELECT tablename AS table_name, schemaname AS schema_name + FROM pg_tables WHERE schemaname NOT LIKE 'pg_%' AND schemaname != 'information_schema' AND tablename != 'geometry_columns' AND tablename != 'spatial_ref_sys'"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return 'SELECT viewname, definition FROM pg_views'; + } + + public function getListTableForeignKeysSQL($table, $database = null) + { + return "SELECT r.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'"; + } + + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + public function getListTableConstraintsSQL($table) + { + return "SELECT + 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 relname, pg_index.indisunique, pg_index.indisprimary, + pg_index.indkey, pg_index.indrelid + 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(setting,'\"\$user\"',user) from pg_catalog.pg_settings where name = 'search_path'),','))"; + } + $whereClause .= "$classAlias.relname = '" . $table . "' AND $namespaceAlias.nspname = $schema"; + + return $whereClause; + } + + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT + a.attnum, + 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_attrdef.adsrc + 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; + } + + /** + * {@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) { + $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->name . ' ' . $query; + if ($comment = $this->getColumnComment($column)) { + $commentsSQL[] = $this->getCommentOnColumnSQL($diff->name, $column->getName(), $comment); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'DROP ' . $column->getQuotedName($this); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + + foreach ($diff->changedColumns as $columnDiff) { + /** @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $oldColumnName = $columnDiff->oldColumnName; + $column = $columnDiff->column; + + if ($columnDiff->hasChanged('type')) { + $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->name . ' ' . $query; + } + + if ($columnDiff->hasChanged('default')) { + $query = 'ALTER ' . $oldColumnName . ' SET ' . $this->getDefaultValueDeclarationSQL($column->toArray()); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + + if ($columnDiff->hasChanged('notnull')) { + $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotNull() ? 'SET' : 'DROP') . ' NOT NULL'; + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + + if ($columnDiff->hasChanged('autoincrement')) { + if ($column->getAutoincrement()) { + // add autoincrement + $seqName = $diff->name . '_' . $oldColumnName . '_seq'; + + $sql[] = "CREATE SEQUENCE " . $seqName; + $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ") FROM " . $diff->name . "))"; + $query = "ALTER " . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; + $sql[] = "ALTER TABLE " . $diff->name . " " . $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->name . " " . $query; + } + } + + if ($columnDiff->hasChanged('comment') && $comment = $this->getColumnComment($column)) { + $commentsSQL[] = $this->getCommentOnColumnSQL($diff->name, $column->getName(), $comment); + } + + if ($columnDiff->hasChanged('length')) { + $query = 'ALTER ' . $column->getName() . ' TYPE ' . $column->getType()->getSqlDeclaration($column->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME COLUMN ' . $oldColumnName . ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME TO ' . $diff->newName; + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff), $commentsSQL); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + public function getCreateSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' START ' . $sequence->getInitialValue(); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof \Doctrine\DBAL\Schema\Sequence) { + $sequence = $sequence->getQuotedName($this); + } + return 'DROP SEQUENCE ' . $sequence . ' CASCADE'; + } + + /** + * {@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; + } + + /** + * {@inheritDoc} + * + * Postgres wants boolean values converted to the strings 'true'/'false'. + */ + 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'; + } + } + } else { + if (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 'true' : 'false'; + } + } + + return $item; + } + + 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} + */ + 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} + */ + 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', + '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} + */ + 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/SQLAzurePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php new file mode 100644 index 0000000..238e54f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php @@ -0,0 +1,51 @@ +. + */ + +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..be3725b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with SQLServer2005 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)'; + } +} + 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..909ab84 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with SQLServer2008 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 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'; + } +} 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..c0b31c7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php @@ -0,0 +1,927 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\DBALException; +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 + */ +class SQLServerPlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return 'DATEADD(day, ' . $days . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return 'DATEADD(day, -1 * ' . $days . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return 'DATEADD(month, ' . $months . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return 'DATEADD(month, -1 * ' . $months . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + * + * MsSql prefers "autoincrement" identity columns since sequences can only + * be emulated with a table. + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * MsSql supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@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 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); + } else if (!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()) + { + // @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; + } + } + + $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 $sql; + } + + /** + * {@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->getColumns()) . ')'; + } + + /** + * {@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 '; + } else if ($index->hasFlag('nonclustered')) { + $type .= 'NONCLUSTERED '; + } + + return $type; + } + + /** + * Extend unique key constraint with required filters + * + * @param string $sql + * @param Index $index + * + * @return string + */ + private function _appendUniqueConstraintDefinition($sql, Index $index) + { + $fields = array(); + foreach ($index->getColumns() as $field => $definition) { + if (!is_array($definition)) { + $field = $definition; + } + + $fields[] = $field . ' IS NOT NULL'; + } + + return $sql . ' WHERE ' . implode(' AND ', $fields); + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $queryParts = array(); + $sql = array(); + $columnSql = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + 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; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $queryParts[] = 'ALTER COLUMN ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = "sp_RENAME '". $diff->name. ".". $oldColumnName . "' , '".$column->getQuotedName($this)."', 'COLUMN'"; + $queryParts[] = 'ALTER COLUMN ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + $tableSql = array(); + + if ($this->onSchemaAlterTable($diff, $tableSql)) { + return array_merge($tableSql, $columnSql); + } + + foreach ($queryParts as $query) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff)); + + if ($diff->newName !== false) { + $sql[] = "sp_RENAME '" . $diff->name . "', '" . $diff->newName . "'"; + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; + } + + /** + * {@inheritDoc} + */ + public function getShowDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams + return "SELECT name FROM sysobjects WHERE type = 'U' AND name != 'sysdiagrams' ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "exec sp_columns @table_name = '" . $table . "'"; + } + + /** + * {@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 OBJECT_NAME (f.parent_object_id) = '" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "exec sp_helpindex '" . $table . "'"; + } + + /** + * {@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"; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID()'; + } + + /** + * {@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) . ')'; + } + + public function getListDatabasesSQL() + { + return 'SELECT * FROM SYS.DATABASES'; + } + + /** + * {@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} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@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} + * + * @link http://lists.bestpractical.com/pipermail/rt-devel/2005-June/007339.html + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + if ($limit > 0) { + if ($offset == 0) { + $query = preg_replace('/^(SELECT\s(DISTINCT\s)?)/i', '\1TOP ' . $limit . ' ', $query); + } else { + $orderby = stristr($query, 'ORDER BY'); + + if ( ! $orderby) { + $over = 'ORDER BY (SELECT 0)'; + } else { + $over = preg_replace('/\"[^,]*\".\"([^,]*)\"/i', '"inner_tbl"."$1"', $orderby); + } + + // Remove ORDER BY clause from $query + $query = preg_replace('/\s+ORDER BY(.*)/', '', $query); + $query = preg_replace('/\sFROM/i', ", ROW_NUMBER() OVER ($over) AS doctrine_rownum FROM", $query); + + $start = $offset + 1; + $end = $offset + $limit; + + $query = "SELECT * FROM ($query) AS doctrine_tbl WHERE doctrine_rownum BETWEEN $start AND $end"; + } + } + + return $query; + } + + /** + * {@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; + } + } + } else if (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', + 'datetimeoffset' => 'datetimetz', + 'smalldatetime' => 'datetime', + 'datetime' => 'datetime', + 'char' => 'string', + 'varchar' => 'string', + 'text' => 'text', + 'nchar' => 'string', + 'nvarchar' => 'string', + 'ntext' => 'text', + 'binary' => 'text', + 'varbinary' => 'blob', + 'image' => 'text', + '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 appendLockHint($fromClause, $lockMode) + { + // @todo coorect + if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_READ) { + return $fromClause . ' WITH (tablockx)'; + } + + if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE) { + return $fromClause . ' WITH (tablockx)'; + } + + return $fromClause; + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSQL() + { + return ' '; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\MsSQLKeywords'; + } + + /** + * {@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', 'BigInteger', 'SmallInteger'))) { + return " DEFAULT " . $field['default']; + } + + if ((string) $field['type'] == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL()) { + return " DEFAULT " . $this->getCurrentTimestampSQL(); + } + + if ((string) $field['type'] == 'Boolean') { + return " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; + } + + return " DEFAULT '" . $field['default'] . "'"; + } +} 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..6fba88d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -0,0 +1,530 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; + +/** + * The SqlitePlatform class describes the specifics and dialects of the SQLite + * database platform. + * + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @todo Rename: SQLitePlatform + */ +class SqlitePlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * {@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} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'ROUND(JULIANDAY('.$date1 . ')-JULIANDAY('.$date2.'))'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return "DATE(" . $date . ",'+". $days . " day')"; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return "DATE(" . $date . ",'-". $days . " day')"; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return "DATE(" . $date . ",'+". $months . " month')"; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return "DATE(" . $date . ",'-". $months . " month')"; + } + + /** + * {@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 $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getTinyIntTypeDeclarationSql(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getMediumIntTypeDeclarationSql(array $field) + { + return $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) + { + return 'INTEGER'; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($name, array $columns, array $options = array()) + { + $name = str_replace(".", "__", $name); + $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 ' . $name . ' (' . $queryFields . ')'; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index => $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + + if (isset($options['unique']) && ! empty($options['unique'])) { + foreach ($options['unique'] as $index => $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} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + 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"; + } + + 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)"; + } + + 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"; + } + + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + * + * SQLite does support foreign key constraints, but only in CREATE TABLE statements... + * This really limits their usefulness and requires SQLite specific handling, so + * we simply say that SQLite does NOT support foreign keys for now... + */ + public function supportsForeignKeyConstraints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsAlterTable() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + 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 int|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) + { + $pos = strpos($str, $substr, $offset); + if ($pos !== false) { + return $pos+1; + } + + return 0; + } + + 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', + 'integer unsigned' => 'integer', + ); + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords'; + } + + /** + * {@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; + } +} 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..410fa82 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php @@ -0,0 +1,119 @@ +. + */ + + +namespace Doctrine\DBAL\Portability; + +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Cache\QueryCacheProfile; + +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_ORACLE = 9; + const PORTABILITY_POSTGRESQL = 13; + const PORTABILITY_SQLITE = 13; + const PORTABILITY_OTHERVENDORS = 12; + const PORTABILITY_DRIZZLE = 13; + const PORTABILITY_SQLSRV = 13; + + /** + * @var int + */ + private $portability = self::PORTABILITY_NONE; + + /** + * @var int + */ + private $case; + + public function connect() + { + $ret = parent::connect(); + if ($ret) { + $params = $this->getParams(); + if (isset($params['portability'])) { + if ($this->_platform->getName() === "oracle") { + $params['portability'] = $params['portability'] & self::PORTABILITY_ORACLE; + } else if ($this->_platform->getName() === "postgresql") { + $params['portability'] = $params['portability'] & self::PORTABILITY_POSTGRESQL; + } else if ($this->_platform->getName() === "sqlite") { + $params['portability'] = $params['portability'] & self::PORTABILITY_SQLITE; + } else if ($this->_platform->getName() === "drizzle") { + $params['portability'] = self::PORTABILITY_DRIZZLE; + } else if ($this->_platform->getName() === 'sqlsrv') { + $params['portability'] = $params['portabililty'] & 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; + } + + public function getPortability() + { + return $this->portability; + } + + public function getFetchCase() + { + return $this->case; + } + + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + return new Statement(parent::executeQuery($query, $params, $types, $qcp), $this); + } + + /** + * Prepares an SQL statement. + * + * @param string $statement The SQL statement to prepare. + * @return \Doctrine\DBAL\Driver\Statement The prepared statement. + */ + public function prepare($statement) + { + return new Statement(parent::prepare($statement), $this); + } + + public function query() + { + $this->connect(); + + $stmt = call_user_func_array(array($this->_conn, 'query'), func_get_args()); + return new Statement($stmt, $this); + } +} 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..98076bd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php @@ -0,0 +1,195 @@ +. + */ + +namespace Doctrine\DBAL\Portability; + +use PDO; + +/** + * Portability Wrapper for a Statement + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class Statement implements \IteratorAggregate, \Doctrine\DBAL\Driver\Statement +{ + + /** + * @var int + */ + private $portability; + + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $stmt; + + /** + * @var int + */ + private $case; + + /** + * @var int + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * Wraps Statement and applies portability measures + * + * @param \Doctrine\DBAL\Driver\Statement $stmt + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct($stmt, Connection $conn) + { + $this->stmt = $stmt; + $this->portability = $conn->getPortability(); + $this->case = $conn->getFetchCase(); + } + + public function bindParam($column, &$variable, $type = null,$length = null) + { + return $this->stmt->bindParam($column, $variable, $type); + } + + public function bindValue($param, $value, $type = null) + { + return $this->stmt->bindValue($param, $value, $type); + } + + public function closeCursor() + { + return $this->stmt->closeCursor(); + } + + public function columnCount() + { + return $this->stmt->columnCount(); + } + + public function errorCode() + { + return $this->stmt->errorCode(); + } + + public function errorInfo() + { + return $this->stmt->errorInfo(); + } + + public function execute($params = null) + { + return $this->stmt->execute($params); + } + + public function setFetchMode($fetchMode, $arg1 = null, $arg2 = null) + { + $this->defaultFetchMode = $fetchMode; + $this->stmt->setFetchMode($fetchMode, $arg1, $arg2); + } + + public function getIterator() + { + $data = $this->fetchAll(); + return new \ArrayIterator($data); + } + + 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; + } + + 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; + } + + 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; + } else if (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($v)) { + $row[$k] = rtrim($v); + } + } + } + return $row; + } + + 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; + } else if (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($value)) { + $value = rtrim($value); + } + } + + return $value; + } + + 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..5d55b22 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php @@ -0,0 +1,130 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +/** + * Composite expression is responsible to build a group of similar expression. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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'; + + /** + * @var string Holds the instance type of composite expression + */ + private $type; + + /** + * @var array Each expression part of the composite expression + */ + 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 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 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); + } + + /** + * Retrieve 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) . ')'; + } + + /** + * Return 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..4f50232 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php @@ -0,0 +1,264 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +use Doctrine\DBAL\Connection; + +/** + * ExpressionBuilder class is responsible to dynamically create SQL query parts. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class ExpressionBuilder +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + + /** + * @var Doctrine\DBAL\Connection DBAL Connection + */ + private $connection = null; + + /** + * Initializes a new ExpressionBuilder. + * + * @param \Doctrine\DBAL\Connection $connection 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 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 CompositeExpression + */ + public function orX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); + } + + /** + * Creates a comparison expression. + * + * @param mixed $x Left expression + * @param string $operator One of the ExpressionBuilder::* constants. + * @param mixed $y 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 Left expression + * @param mixed $y 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 Left expression + * @param mixed $y 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 Left expression + * @param mixed $y 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 Left expression + * @param mixed $y 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 Left expression + * @param mixed $y 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 Left expression + * @param mixed $y 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 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 string + */ + public function like($x, $y) + { + return $this->comparison($x, 'LIKE', $y); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input Parameter to be quoted. + * @param string $type 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..3d803d5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php @@ -0,0 +1,1095 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\Query\Expression\CompositeExpression, + 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. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +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; + + /** + * @var Doctrine\DBAL\Connection DBAL Connection + */ + private $connection = null; + + /** + * @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() + ); + + /** + * @var string The complete SQL string for this query. + */ + private $sql; + + /** + * @var array The query parameters. + */ + private $params = array(); + + /** + * @var array The parameter type map of this query. + */ + private $paramTypes = array(); + + /** + * @var integer The type of query this is. Can be select, update or delete. + */ + private $type = self::SELECT; + + /** + * @var integer The state of the query object. Can be dirty or clean. + */ + private $state = self::STATE_CLEAN; + + /** + * @var integer The index of the first result to retrieve. + */ + private $firstResult = null; + + /** + * @var integer The maximum number of results to retrieve. + */ + private $maxResults = null; + + /** + * The counter of bound parameters used with {@see bindValue) + * + * @var int + */ + private $boundCounter = 0; + + /** + * Initializes a new QueryBuilder. + * + * @param \Doctrine\DBAL\Connection $connection 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(); + } + + /** + * Get the type of the currently built query. + * + * @return integer + */ + public function getType() + { + return $this->type; + } + + /** + * Get the associated DBAL Connection for this query builder. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Get the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() + { + return $this->state; + } + + /** + * Execute 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 mixed + */ + 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); + } + } + + /** + * Get 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; + } + + $sql = ''; + + switch ($this->type) { + 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 PDO::PARAM_* + * @return 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 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. + * + * @return array The currently defined query parameters. + */ + 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; + } + + /** + * 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 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; + } + } else if ($isArray && is_array($sqlPart[key($sqlPart)])) { + $key = key($sqlPart); + $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key]; + } else if ($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 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 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 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 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 + )); + } + + /** + * Create and add 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 $alias The alias of the table + * @return QueryBuilder This QueryBuilder instance. + */ + public function from($from, $alias) + { + 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 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 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 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 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 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 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 QueryBuilder This QueryBuilder instance. + * @see where() + */ + public function andWhere($where) + { + $where = $this->getQueryPart('where'); + $args = func_get_args(); + + 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 QueryBuilder $qb + * @see where() + */ + public function orWhere($where) + { + $where = $this->getQueryPart('where'); + $args = func_get_args(); + + 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 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 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); + } + + /** + * 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 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 QueryBuilder This QueryBuilder instance. + */ + public function andHaving($having) + { + $having = $this->getQueryPart('having'); + $args = func_get_args(); + + 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 QueryBuilder This QueryBuilder instance. + */ + public function orHaving($having) + { + $having = $this->getQueryPart('having'); + $args = func_get_args(); + + 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 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 QueryBuilder This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) + { + return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true); + } + + /** + * Get a query part by its name. + * + * @param string $queryPartName + * @return mixed $queryPart + */ + public function getQueryPart($queryPartName) + { + return $this->sqlParts[$queryPartName]; + } + + /** + * Get all query parts. + * + * @return array $sqlParts + */ + public function getQueryParts() + { + return $this->sqlParts; + } + + /** + * Reset SQL parts + * + * @param array $queryPartNames + * @return QueryBuilder + */ + public function resetQueryParts($queryPartNames = null) + { + if (is_null($queryPartNames)) { + $queryPartNames = array_keys($this->sqlParts); + } + + foreach ($queryPartNames as $queryPartName) { + $this->resetQueryPart($queryPartName); + } + + return $this; + } + + /** + * Reset single SQL part + * + * @param string $queryPartName + * @return QueryBuilder + */ + public function resetQueryPart($queryPartName) + { + $this->sqlParts[$queryPartName] = is_array($this->sqlParts[$queryPartName]) + ? array() : null; + + $this->state = self::STATE_DIRTY; + + return $this; + } + + private function getSQLForSelect() + { + $query = 'SELECT ' . implode(', ', $this->sqlParts['select']) . ' FROM '; + + $fromClauses = array(); + $joinsPending = true; + $joinAliases = array(); + + // Loop through all FROM clauses + foreach ($this->sqlParts['from'] as $from) { + $fromClause = $from['table'] . ' ' . $from['alias']; + + if ($joinsPending && isset($this->sqlParts['join'][$from['alias']])) { + foreach ($this->sqlParts['join'] as $joins) { + foreach ($joins as $join) { + $fromClause .= ' ' . strtoupper($join['joinType']) + . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'] + . ' ON ' . ((string) $join['joinCondition']); + $joinAliases[$join['joinAlias']] = true; + } + } + $joinsPending = false; + } + + $fromClauses[$from['alias']] = $fromClause; + } + + // loop through all JOIN clauses for validation purpose + $knownAliases = array_merge($fromClauses,$joinAliases); + foreach ($this->sqlParts['join'] as $fromAlias => $joins) { + if ( ! isset($knownAliases[$fromAlias]) ) { + throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases)); + } + } + + + $query .= implode(', ', $fromClauses) + . ($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']) : ''); + + return ($this->maxResults === null && $this->firstResult == null) + ? $query + : $this->connection->getDatabasePlatform()->modifyLimitQuery($query, $this->maxResults, $this->firstResult); + } + + /** + * 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(); + } + + /** + * Create 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; + } + + /** + * Create 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 mixed $type + * @return string + */ + public function createPositionalParameter($value, $type = \PDO::PARAM_STR) + { + $this->boundCounter++; + $this->setParameter($this->boundCounter, $value, $type); + return "?"; + } +} 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..e2c2b32 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\DBALException; + +/** + * Driver interface. + * Interface that all DBAL drivers must implement. + * + * @since 2.1.4 + */ +class QueryException extends DBALException +{ + 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) . "."); + } +} 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..20cafa9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php @@ -0,0 +1,234 @@ +. + */ + + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Connection; + +/** + * Utility class that parses sql statements with regard to types and parameters. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @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 bool $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. + * + * @throws SQLParserUtilsException + * @return array + */ + static public function expandListParameters($query, $params, $types) + { + $isPositional = is_int(key($params)); + $arrayPositions = array(); + $bindIndex = -1; + + 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; + + 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 = implode(", ", array_fill(0, $count, "?")); + $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, '?')) : '?'; + + 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; + 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 (isset($paramsOrTypes[$paramName])) { + return $paramsOrTypes[$paramName]; + } + + // Hash keys can be prefixed with a colon for compatibility + if (isset($paramsOrTypes[':' . $paramName])) { + 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..4a74f6c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Doctrine\DBAL\ConnectionException + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.4 + * @author Lars Strojny + */ +class SQLParserUtilsException extends DBALException +{ + 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)); + } + + 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..17a9c0f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php @@ -0,0 +1,214 @@ +. + */ + +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 + */ + protected $_namespace; + + /** + * @var bool + */ + protected $_quoted = false; + + /** + * Set name of this asset + * + * @param string $name + */ + 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 bool + */ + public function isInDefaultNamespace($defaultNamespaceName) + { + return $this->_namespace == $defaultNamespaceName || $this->_namespace === null; + } + + /** + * Get namespace name of this asset. + * + * If NULL is returned this means the default namespace is used. + * + * @return string + */ + 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 + * @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. + * + * @return string + */ + public function getFullQualifiedName($defaultNamespaceName) + { + $name = $this->getName(); + if ( ! $this->_namespace) { + $name = $defaultNamespaceName . "." . $name; + } + return strtolower($name); + } + + /** + * Check if this asset's name is quoted + * + * @return bool + */ + public function isQuoted() + { + return $this->_quoted; + } + + /** + * Check if this identifier is quoted. + * + * @param string $identifier + * @return bool + */ + protected function isIdentifierQuoted($identifier) + { + return (isset($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); + } + + /** + * Return name of this schema asset. + * + * @return string + */ + public function getName() + { + if ($this->_namespace) { + return $this->_namespace . "." . $this->_name; + } + return $this->_name; + } + + /** + * Get the quoted representation of this asset but only if it was defined with one. Otherwise + * return the plain unquoted value as inserted. + * + * @param 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); + } + + /** + * Generate 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 int $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..068d6b5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php @@ -0,0 +1,896 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs; +use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; +use Doctrine\DBAL\Types; +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. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 + */ + public function __construct(\Doctrine\DBAL\Connection $conn, AbstractPlatform $platform = null) + { + $this->_conn = $conn; + $this->_platform = $platform ?: $this->_conn->getDatabasePlatform(); + } + + /** + * Return associated platform. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_platform; + } + + /** + * Try 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; + } + } + + /** + * List the available databases for this connection + * + * @return array $databases + */ + public function listDatabases() + { + $sql = $this->_platform->getListDatabasesSQL(); + + $databases = $this->_conn->fetchAll($sql); + + return $this->_getPortableDatabasesList($databases); + } + + /** + * List the available sequences for this connection + * + * @return 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)); + } + + /** + * List 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 $database + * @return 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); + } + + /** + * List 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 Index[] $tableIndexes + */ + public function listTableIndexes($table) + { + $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); + + $tableIndexes = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableIndexesList($tableIndexes, $table); + } + + /** + * Return true if all the given tables exist. + * + * @param array $tableNames + * @return bool + */ + public function tablesExist($tableNames) + { + $tableNames = array_map('strtolower', (array)$tableNames); + return count($tableNames) == count(\array_intersect($tableNames, array_map('strtolower', $this->listTableNames()))); + } + + /** + * Return 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); + } + + /** + * Filter 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); + }) + ); + } + + protected function getFilterSchemaAssetsExpression() + { + return $this->_conn->getConfiguration()->getFilterSchemaAssetsExpression(); + } + + /** + * List the tables for this connection + * + * @return Table[] + */ + public function listTables() + { + $tableNames = $this->listTableNames(); + + $tables = array(); + foreach ($tableNames as $tableName) { + $tables[] = $this->listTableDetails($tableName); + } + + return $tables; + } + + /** + * @param string $tableName + * @return 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()); + } + + /** + * List the views this connection has + * + * @return View[] + */ + public function listViews() + { + $database = $this->_conn->getDatabase(); + $sql = $this->_platform->getListViewsSQL($database); + $views = $this->_conn->fetchAll($sql); + + return $this->_getPortableViewsList($views); + } + + /** + * List the foreign keys for the given table + * + * @param string $table The name of the table + * @return 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 + */ + public function dropDatabase($database) + { + $this->_execSql($this->_platform->getDropDatabaseSQL($database)); + } + + /** + * Drop the given table + * + * @param string $table The name of the table to drop + */ + public function dropTable($table) + { + $this->_execSql($this->_platform->getDropTableSQL($table)); + } + + /** + * Drop the index from the given table + * + * @param Index|string $index The name of the index + * @param string|Table $table The name of the table + */ + public function dropIndex($index, $table) + { + if($index instanceof Index) { + $index = $index->getQuotedName($this->_platform); + } + + $this->_execSql($this->_platform->getDropIndexSQL($index, $table)); + } + + /** + * Drop the constraint from the given table + * + * @param Constraint $constraint + * @param string $table The name of the table + */ + public function dropConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table)); + } + + /** + * Drops a foreign key from a table. + * + * @param ForeignKeyConstraint|string $table The name of the table with the foreign key. + * @param Table|string $name The name of the foreign key. + * @return boolean $result + */ + 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. + */ + public function dropSequence($name) + { + $this->_execSql($this->_platform->getDropSequenceSQL($name)); + } + + /** + * Drop a view + * + * @param string $name The name of the view + * @return boolean $result + */ + 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. + */ + public function createDatabase($database) + { + $this->_execSql($this->_platform->getCreateDatabaseSQL($database)); + } + + /** + * Create a new table. + * + * @param Table $table + * @param int $createFlags + */ + public function createTable(Table $table) + { + $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS; + $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags)); + } + + /** + * Create a new sequence + * + * @param Sequence $sequence + * @throws \Doctrine\DBAL\ConnectionException if something fails at database level + */ + public function createSequence($sequence) + { + $this->_execSql($this->_platform->getCreateSequenceSQL($sequence)); + } + + /** + * Create a constraint on a table + * + * @param Constraint $constraint + * @param string|Table $table + */ + public function createConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table)); + } + + /** + * Create a new index on a table + * + * @param Index $index + * @param string $table name of the table on which the index is to be created + */ + public function createIndex(Index $index, $table) + { + $this->_execSql($this->_platform->getCreateIndexSQL($index, $table)); + } + + /** + * Create a new foreign key + * + * @param ForeignKeyConstraint $foreignKey ForeignKey instance + * @param string|Table $table name of the table on which the foreign key is to be created + */ + public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table)); + } + + /** + * Create a new view + * + * @param View $view + */ + public function createView(View $view) + { + $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql())); + } + + /* dropAndCreate*() Methods */ + + /** + * Drop and create a constraint + * + * @param Constraint $constraint + * @param string $table + * @see dropConstraint() + * @see createConstraint() + */ + public function dropAndCreateConstraint(Constraint $constraint, $table) + { + $this->tryMethod('dropConstraint', $constraint, $table); + $this->createConstraint($constraint, $table); + } + + /** + * Drop and create a new index on a table + * + * @param string|Table $table name of the table on which the index is to be created + * @param Index $index + */ + public function dropAndCreateIndex(Index $index, $table) + { + $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table); + $this->createIndex($index, $table); + } + + /** + * Drop and create a new foreign key + * + * @param ForeignKeyConstraint $foreignKey associative array that defines properties of the foreign key to be created. + * @param string|Table $table name of the table on which the foreign key is to be created + */ + public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->tryMethod('dropForeignKey', $foreignKey, $table); + $this->createForeignKey($foreignKey, $table); + } + + /** + * Drop and create a new sequence + * + * @param Sequence $sequence + * @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); + } + + /** + * Drop and create a new table. + * + * @param Table $table + */ + public function dropAndCreateTable(Table $table) + { + $this->tryMethod('dropTable', $table->getQuotedName($this->_platform)); + $this->createTable($table); + } + + /** + * Drop and creates a new database. + * + * @param string $database The name of the database to create. + */ + public function dropAndCreateDatabase($database) + { + $this->tryMethod('dropDatabase', $database); + $this->createDatabase($database); + } + + /** + * Drop and create a new view + * + * @param View $view + */ + public function dropAndCreateView(View $view) + { + $this->tryMethod('dropView', $view->getQuotedName($this->_platform)); + $this->createView($view); + } + + /* alterTable() Methods */ + + /** + * Alter an existing tables schema + * + * @param TableDiff $tableDiff + */ + public function alterTable(TableDiff $tableDiff) + { + $queries = $this->_platform->getAlterTableSQL($tableDiff); + if (is_array($queries) && count($queries)) { + foreach ($queries as $ddlQuery) { + $this->_execSql($ddlQuery); + } + } + } + + /** + * Rename a given table to another name + * + * @param string $name The current name of the table + * @param string $newName The new name of the table + */ + 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 + */ + + protected function _getPortableDatabasesList($databases) + { + $list = array(); + foreach ($databases as $value) { + if ($value = $this->_getPortableDatabaseDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database; + } + + protected function _getPortableFunctionsList($functions) + { + $list = array(); + foreach ($functions as $value) { + if ($value = $this->_getPortableFunctionDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableFunctionDefinition($function) + { + return $function; + } + + protected function _getPortableTriggersList($triggers) + { + $list = array(); + foreach ($triggers as $value) { + if ($value = $this->_getPortableTriggerDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger; + } + + protected function _getPortableSequencesList($sequences) + { + $list = array(); + foreach ($sequences as $value) { + if ($value = $this->_getPortableSequenceDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + /** + * @param array $sequence + * @return Sequence + */ + 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; + } + + /** + * Get Table Column Definition + * + * @param array $tableColumn + * @return Column + */ + abstract protected function _getPortableTableColumnDefinition($tableColumn); + + /** + * Aggregate and group the index results according to the required data result. + * + * @param array $tableIndexRows + * @param string $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(), + ); + } 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']); + } + + if ($index) { + $indexes[$indexKey] = $index; + } + } + + return $indexes; + } + + protected function _getPortableTablesList($tables) + { + $list = array(); + foreach ($tables as $value) { + if ($value = $this->_getPortableTableDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableTableDefinition($table) + { + return $table; + } + + protected function _getPortableUsersList($users) + { + $list = array(); + foreach ($users as $value) { + if ($value = $this->_getPortableUserDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableUserDefinition($user) + { + return $user; + } + + 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; + } + + protected function _getPortableViewDefinition($view) + { + return false; + } + + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + if ($value = $this->_getPortableTableForeignKeyDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return $tableForeignKey; + } + + protected function _execSql($sql) + { + foreach ((array) $sql as $query) { + $this->_conn->executeUpdate($query); + } + } + + /** + * Create a schema instance for the current database. + * + * @return Schema + */ + public function createSchema() + { + $sequences = array(); + if($this->_platform->supportsSequences()) { + $sequences = $this->listSequences(); + } + $tables = $this->listTables(); + + return new Schema($tables, $sequences, $this->createSchemaConfig()); + } + + /** + * Create the configuration for this schema. + * + * @return 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; + } + + 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..fa6f70b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php @@ -0,0 +1,423 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use \Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * Object representation of a database column + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Column extends AbstractAsset +{ + /** + * @var \Doctrine\DBAL\Types\Type + */ + protected $_type; + + /** + * @var int + */ + protected $_length = null; + + /** + * @var int + */ + protected $_precision = 10; + + /** + * @var int + */ + protected $_scale = 0; + + /** + * @var bool + */ + protected $_unsigned = false; + + /** + * @var bool + */ + protected $_fixed = false; + + /** + * @var bool + */ + protected $_notnull = true; + + /** + * @var string + */ + protected $_default = null; + + /** + * @var bool + */ + protected $_autoincrement = false; + + /** + * @var array + */ + protected $_platformOptions = array(); + + /** + * @var string + */ + protected $_columnDefinition = null; + + /** + * @var string + */ + protected $_comment = null; + + /** + * @var array + */ + protected $_customSchemaOptions = array(); + + /** + * Create a new Column + * + * @param string $columnName + * @param \Doctrine\DBAL\Types\Type $type + * @param int $length + * @param bool $notNull + * @param mixed $default + * @param bool $unsigned + * @param bool $fixed + * @param int $precision + * @param int $scale + * @param array $platformOptions + */ + 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 int $length + * @return Column + */ + public function setLength($length) + { + if($length !== null) { + $this->_length = (int)$length; + } else { + $this->_length = null; + } + return $this; + } + + /** + * @param int $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 int $scale + * @return Column + */ + public function setScale($scale) + { + if (!is_numeric($scale)) { + $scale = 0; + } + + $this->_scale = (int)$scale; + return $this; + } + + /** + * + * @param bool $unsigned + * @return Column + */ + public function setUnsigned($unsigned) + { + $this->_unsigned = (bool)$unsigned; + return $this; + } + + /** + * + * @param bool $fixed + * @return Column + */ + public function setFixed($fixed) + { + $this->_fixed = (bool)$fixed; + return $this; + } + + /** + * @param bool $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 + * @return Column + */ + public function setColumnDefinition($value) + { + $this->_columnDefinition = $value; + return $this; + } + + public function getType() + { + return $this->_type; + } + + public function getLength() + { + return $this->_length; + } + + public function getPrecision() + { + return $this->_precision; + } + + public function getScale() + { + return $this->_scale; + } + + public function getUnsigned() + { + return $this->_unsigned; + } + + public function getFixed() + { + return $this->_fixed; + } + + public function getNotnull() + { + return $this->_notnull; + } + + public function getDefault() + { + return $this->_default; + } + + public function getPlatformOptions() + { + return $this->_platformOptions; + } + + public function hasPlatformOption($name) + { + return isset($this->_platformOptions[$name]); + } + + public function getPlatformOption($name) + { + return $this->_platformOptions[$name]; + } + + public function getColumnDefinition() + { + return $this->_columnDefinition; + } + + public function getAutoincrement() + { + return $this->_autoincrement; + } + + public function setAutoincrement($flag) + { + $this->_autoincrement = $flag; + return $this; + } + + public function setComment($comment) + { + $this->_comment = $comment; + return $this; + } + + 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; + } + + /** + * @param Visitor $visitor + */ + public function visit(\Doctrine\DBAL\Schema\Visitor $visitor) + { + $visitor->accept($this); + } + + /** + * @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..4fc4b9c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Represent the change of a column + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class ColumnDiff +{ + public $oldColumnName; + + /** + * @var Column + */ + public $column; + + /** + * @var array + */ + public $changedProperties = array(); + + public function __construct($oldColumnName, Column $column, array $changedProperties = array()) + { + $this->oldColumnName = $oldColumnName; + $this->column = $column; + $this->changedProperties = $changedProperties; + } + + public function hasChanged($propertyName) + { + return in_array($propertyName, $this->changedProperties); + } +} 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..b61a69a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php @@ -0,0 +1,433 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Compare to Schemas and return an instance of SchemaDiff + * + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Comparator +{ + /** + * @param Schema $fromSchema + * @param Schema $toSchema + * @return 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 Schema $fromSchema + * @param Schema $toSchema + * + * @return SchemaDiff + */ + public function compare(Schema $fromSchema, Schema $toSchema) + { + $diff = new SchemaDiff(); + + $foreignKeysToTable = array(); + + 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)) { + $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; + } + + private function isAutoIncrementSequenceInSchema($schema, $sequence) + { + foreach ($schema->getTables() as $table) { + if ($sequence->isAutoIncrementsFor($table)) { + return true; + } + } + + return false; + } + + /** + * + * @param Sequence $sequence1 + * @param Sequence $sequence2 + */ + 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 Table $table1 + * @param Table $table2 + * + * @return bool|TableDiff + */ + public function diffTable(Table $table1, Table $table2) + { + $changes = 0; + $tableDifferences = new TableDiff($table1->getName()); + + $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 ) { + if ( !$table2->hasColumn($columnName) ) { + $tableDifferences->removedColumns[$columnName] = $column; + $changes++; + } + } + + foreach ( $table1Columns as $columnName => $column ) { + if ( $table2->hasColumn($columnName) ) { + $changedProperties = $this->diffColumn( $column, $table2->getColumn($columnName) ); + if (count($changedProperties) ) { + $columnDiff = new ColumnDiff($column->getName(), $table2->getColumn($columnName), $changedProperties); + $tableDifferences->changedColumns[$column->getName()] = $columnDiff; + $changes++; + } + } + } + + $this->detectColumnRenamings($tableDifferences); + + $table1Indexes = $table1->getIndexes(); + $table2Indexes = $table2->getIndexes(); + + foreach ($table2Indexes as $index2Name => $index2Definition) { + foreach ($table1Indexes as $index1Name => $index1Definition) { + if ($this->diffIndex($index1Definition, $index2Definition) === false) { + unset($table1Indexes[$index1Name]); + unset($table2Indexes[$index2Name]); + } else { + if ($index1Name == $index2Name) { + $tableDifferences->changedIndexes[$index2Name] = $table2Indexes[$index2Name]; + unset($table1Indexes[$index1Name]); + unset($table2Indexes[$index2Name]); + $changes++; + } + } + } + } + + foreach ($table1Indexes as $index1Name => $index1Definition) { + $tableDifferences->removedIndexes[$index1Name] = $index1Definition; + $changes++; + } + + foreach ($table2Indexes as $index2Name => $index2Definition) { + $tableDifferences->addedIndexes[$index2Name] = $index2Definition; + $changes++; + } + + $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 $key1 => $constraint1) { + $tableDifferences->removedForeignKeys[] = $constraint1; + $changes++; + } + + foreach ($toFkeys as $key2 => $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 TableDiff $tableDifferences + */ + private function detectColumnRenamings(TableDiff $tableDifferences) + { + $renameCandidates = array(); + foreach ($tableDifferences->addedColumns as $addedColumnName => $addedColumn) { + foreach ($tableDifferences->removedColumns as $removedColumnName => $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]); + } + } + } + } + + /** + * @param ForeignKeyConstraint $key1 + * @param ForeignKeyConstraint $key2 + * @return bool + */ + public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2) + { + if (array_map('strtolower', $key1->getLocalColumns()) != array_map('strtolower', $key2->getLocalColumns())) { + return true; + } + + if (array_map('strtolower', $key1->getForeignColumns()) != array_map('strtolower', $key2->getForeignColumns())) { + 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 Column $column1 + * @param Column $column2 + * + * @return array + */ + public function diffColumn(Column $column1, Column $column2) + { + $changedProperties = array(); + if ( $column1->getType() != $column2->getType() ) { + $changedProperties[] = 'type'; + } + + if ($column1->getNotnull() != $column2->getNotnull()) { + $changedProperties[] = 'notnull'; + } + + if ($column1->getDefault() != $column2->getDefault()) { + $changedProperties[] = 'default'; + } + + if ($column1->getUnsigned() != $column2->getUnsigned()) { + $changedProperties[] = 'unsigned'; + } + + if ($column1->getType() instanceof \Doctrine\DBAL\Types\StringType) { + // check if value of length is set at all, default value assumed otherwise. + $length1 = $column1->getLength() ?: 255; + $length2 = $column2->getLength() ?: 255; + if ($length1 != $length2) { + $changedProperties[] = 'length'; + } + + if ($column1->getFixed() != $column2->getFixed()) { + $changedProperties[] = 'fixed'; + } + } + + if ($column1->getType() instanceof \Doctrine\DBAL\Types\DecimalType) { + if (($column1->getPrecision()?:10) != ($column2->getPrecision()?:10)) { + $changedProperties[] = 'precision'; + } + if ($column1->getScale() != $column2->getScale()) { + $changedProperties[] = 'scale'; + } + } + + if ($column1->getAutoincrement() != $column2->getAutoincrement()) { + $changedProperties[] = 'autoincrement'; + } + + // only allow to delete comment if its set to '' not to null. + if ($column1->getComment() !== null && $column1->getComment() != $column2->getComment()) { + $changedProperties[] = 'comment'; + } + + $options1 = $column1->getCustomSchemaOptions(); + $options2 = $column2->getCustomSchemaOptions(); + + $commonKeys = array_keys(array_intersect_key($options1, $options2)); + + foreach ($commonKeys as $key) { + if ($options1[$key] !== $options2[$key]) { + $changedProperties[] = $key; + } + } + + $diffKeys = array_keys(array_diff_key($options1, $options2) + array_diff_key($options2, $options1)); + + $changedProperties = array_merge($changedProperties, $diffKeys); + + return $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 Index $index1 + * @param Index $index2 + * @return bool + */ + 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..f80410f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Marker interface for contraints + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +interface Constraint +{ + public function getName(); + + public function getQuotedName(AbstractPlatform $platform); + + public function getColumns(); +} 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..e11c64c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php @@ -0,0 +1,214 @@ +. +*/ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; +use Doctrine\DBAL\Events; + +/** + * IBM Db2 Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class DB2SchemaManager extends AbstractSchemaManager +{ + /** + * Return a list of all tables in the current database + * + * 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} + * + * @return array + */ + public function listTableNames() + { + $sql = $this->_platform->getListTablesSQL(); + $sql .= " AND CREATOR = UPPER('".$this->_conn->getUsername()."')"; + + $tables = $this->_conn->fetchAll($sql); + + return $this->_getPortableTablesList($tables); + } + + + /** + * Get Table Column Definition + * + * @param array $tableColumn + * @return Column + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, \CASE_LOWER); + + $length = null; + $fixed = null; + $unsigned = false; + $scale = false; + $precision = false; + + $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' => ($tableColumn['default'] == "NULL") ? null : $tableColumn['default'], + '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); + } + + protected function _getPortableTablesList($tables) + { + $tableNames = array(); + foreach ($tables as $tableRow) { + $tableRow = array_change_key_case($tableRow, \CASE_LOWER); + $tableNames[] = $tableRow['name']; + } + return $tableNames; + } + + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $eventManager = $this->_platform->getEventManager(); + + $indexes = array(); + foreach($tableIndexes as $indexKey => $data) { + $data = array_change_key_case($data, \CASE_LOWER); + $unique = ($data['uniquerule'] == "D") ? false : true; + $primary = ($data['uniquerule'] == "P"); + + $indexName = strtolower($data['name']); + if ($primary) { + $keyName = 'primary'; + } else { + $keyName = $indexName; + } + + $data = array( + 'name' => $indexName, + 'columns' => explode("+", ltrim($data['colnames'], '+')), + 'unique' => $unique, + 'primary' => $primary + ); + + $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']); + } + + if ($index) { + $indexes[$indexKey] = $index; + } + } + + return $indexes; + } + + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $tableForeignKey = array_change_key_case($tableForeignKey, CASE_LOWER); + + $tableForeignKey['deleterule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['deleterule']); + $tableForeignKey['updaterule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['updaterule']); + + return new ForeignKeyConstraint( + array_map('trim', (array)$tableForeignKey['fkcolnames']), + $tableForeignKey['reftbname'], + array_map('trim', (array)$tableForeignKey['pkcolnames']), + $tableForeignKey['relname'], + array( + 'onUpdate' => $tableForeignKey['updaterule'], + 'onDelete' => $tableForeignKey['deleterule'], + ) + ); + } + + protected function _getPortableForeignKeyRuleDef($def) + { + if ($def == "C") { + return "CASCADE"; + } else if ($def == "N") { + return "SET NULL"; + } + return null; + } + + 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..f73b223 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Schema manager for the Drizzle RDBMS. + * + * @author Kim Hemsø Rasmussen + */ +class DrizzleSchemaManager extends AbstractSchemaManager +{ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableName = $tableColumn['COLUMN_NAME']; + $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' => empty($tableColumn['COLUMN_DEFAULT']) ? null : $tableColumn['COLUMN_DEFAULT'], + 'autoincrement' => (bool)$tableColumn['IS_AUTO_INCREMENT'], + 'scale' => (int)$tableColumn['NUMERIC_SCALE'], + 'precision' => (int)$tableColumn['NUMERIC_PRECISION'], + 'comment' => (isset($tableColumn['COLUMN_COMMENT']) ? $tableColumn['COLUMN_COMMENT'] : null), + ); + + return new Column($tableName, \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database['SCHEMA_NAME']; + } + + protected function _getPortableTableDefinition($table) + { + return $table['TABLE_NAME']; + } + + public function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $columns = array(); + foreach (explode(',', $tableForeignKey['CONSTRAINT_COLUMNS']) as $value) { + $columns[] = trim($value, ' `'); + } + + $ref_columns = array(); + foreach (explode(',', $tableForeignKey['REFERENCED_TABLE_COLUMNS']) as $value) { + $ref_columns[] = trim($value, ' `'); + } + + return new ForeignKeyConstraint( + $columns, + $tableForeignKey['REFERENCED_TABLE_NAME'], + $ref_columns, + $tableForeignKey['CONSTRAINT_NAME'], + array( + 'onUpdate' => $tableForeignKey['UPDATE_RULE'], + 'onDelete' => $tableForeignKey['DELETE_RULE'], + ) + ); + } + + 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..0375582 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php @@ -0,0 +1,204 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class ForeignKeyConstraint extends AbstractAsset implements Constraint +{ + /** + * @var Table + */ + protected $_localTable; + + /** + * @var array + */ + protected $_localColumnNames; + + /** + * @var string + */ + protected $_foreignTableName; + + /** + * @var array + */ + protected $_foreignColumnNames; + + /** + * @var string + */ + protected $_cascade = ''; + + /** + * @var array + */ + protected $_options; + + /** + * + * @param array $localColumnNames + * @param string $foreignTableName + * @param array $foreignColumnNames + * @param string $cascade + * @param string|null $name + */ + public function __construct(array $localColumnNames, $foreignTableName, array $foreignColumnNames, $name=null, array $options=array()) + { + $this->_setName($name); + $this->_localColumnNames = $localColumnNames; + $this->_foreignTableName = $foreignTableName; + $this->_foreignColumnNames = $foreignColumnNames; + $this->_options = $options; + } + + /** + * @return string + */ + public function getLocalTableName() + { + return $this->_localTable->getName(); + } + + /** + * @param Table $table + */ + public function setLocalTable(Table $table) + { + $this->_localTable = $table; + } + + /** + * @return array + */ + public function getLocalColumns() + { + return $this->_localColumnNames; + } + + public function getColumns() + { + return $this->_localColumnNames; + } + + /** + * @return string + */ + public function getForeignTableName() + { + return $this->_foreignTableName; + } + + /** + * Return the non-schema qualified foreign table name. + * + * @return string + */ + public function getUnqualifiedForeignTableName() + { + $parts = explode(".", $this->_foreignTableName); + return strtolower(end($parts)); + } + + /** + * Get the quoted representation of this asset but only if it was defined with one. Otherwise + * return the plain unquoted value as inserted. + * + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedForeignTableName(AbstractPlatform $platform) + { + $keywords = $platform->getReservedKeywordsList(); + $parts = explode(".", $this->getForeignTableName()); + foreach ($parts AS $k => $v) { + $parts[$k] = ($this->_quoted || $keywords->isKeyword($v)) ? $platform->quoteIdentifier($v) : $v; + } + + return implode(".", $parts); + } + + /** + * @return array + */ + public function getForeignColumns() + { + return $this->_foreignColumnNames; + } + + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + public function getOption($name) + { + return $this->_options[$name]; + } + + /** + * Gets the options associated with this constraint + * + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * Foreign Key onUpdate status + * + * @return string|null + */ + public function onUpdate() + { + return $this->_onEvent('onUpdate'); + } + + /** + * Foreign Key onDelete status + * + * @return string|null + */ + public function onDelete() + { + return $this->_onEvent('onDelete'); + } + + /** + * @param string $event + * @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; + } +} 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..56b9ea8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php @@ -0,0 +1,242 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; + +class Index extends AbstractAsset implements Constraint +{ + /** + * @var array + */ + protected $_columns; + + /** + * @var bool + */ + protected $_isUnique = false; + + /** + * @var bool + */ + protected $_isPrimary = false; + + /** + * Platform specific flags for indexes. + * + * @var array + */ + protected $_flags = array(); + + /** + * @param string $indexName + * @param array $column + * @param bool $isUnique + * @param bool $isPrimary + */ + public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = array()) + { + $isUnique = ($isPrimary)?true:$isUnique; + + $this->_setName($indexName); + $this->_isUnique = $isUnique; + $this->_isPrimary = $isPrimary; + + foreach ($columns as $column) { + $this->_addColumn($column); + } + foreach ($flags as $flag) { + $this->addFlag($flag); + } + } + + /** + * @param string $column + */ + protected function _addColumn($column) + { + if(is_string($column)) { + $this->_columns[] = $column; + } else { + throw new \InvalidArgumentException("Expecting a string as Index Column"); + } + } + + /** + * @return array + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @return array + */ + public function getUnquotedColumns() + { + return array_map(array($this, 'trimQuotes'), $this->getColumns()); + } + + /** + * Is the index neither unique nor primary key? + * + * @return bool + */ + public function isSimpleIndex() + { + return !$this->_isPrimary && !$this->_isUnique; + } + + /** + * @return bool + */ + public function isUnique() + { + return $this->_isUnique; + } + + /** + * @return bool + */ + public function isPrimary() + { + return $this->_isPrimary; + } + + /** + * @param string $columnName + * @param int $pos + * @return bool + */ + public function hasColumnAtPosition($columnName, $pos = 0) + { + $columnName = $this->trimQuotes(strtolower($columnName)); + $indexColumns = array_map('strtolower', $this->getUnquotedColumns()); + return array_search($columnName, $indexColumns) === $pos; + } + + /** + * Check if this index exactly spans the given column names in the correct order. + * + * @param array $columnNames + * @return boolean + */ + public function spansColumns(array $columnNames) + { + $sameColumns = true; + for ($i = 0; $i < count($this->_columns); $i++) { + if (!isset($columnNames[$i]) || $this->trimQuotes(strtolower($this->_columns[$i])) != $this->trimQuotes(strtolower($columnNames[$i]))) { + $sameColumns = false; + } + } + return $sameColumns; + } + + /** + * Check if the other index already fullfills all the indexing and constraint needs of the current one. + * + * @param Index $other + * @return bool + */ + 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->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 fullfill the requirements of just an + // index that has no constraints. + return true; + } else if ($other->isPrimary() != $this->isPrimary()) { + return false; + } else if ($other->isUnique() != $this->isUnique()) { + return false; + } + return true; + } + return false; + } + + /** + * Detect if the other index is a non-unique, non primary index that can be overwritten by this one. + * + * @param Index $other + * @return bool + */ + public function overrules(Index $other) + { + if ($other->isPrimary()) { + return false; + } else if ($this->isSimpleIndex() && $other->isUnique()) { + return false; + } + + if ($this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique())) { + return true; + } + return false; + } + + /** + * Add 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 bool + */ + public function hasFlag($flag) + { + return isset($this->flags[strtolower($flag)]); + } + + /** + * Remove a flag + * + * @param string $flag + * @return void + */ + public function removeFlag($flag) + { + unset($this->flags[strtolower($flag)]); + } +} + 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..a9a5964 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -0,0 +1,208 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Schema manager for the MySql RDBMS. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Roman Borschel + * @author Benjamin Eberlei + * @version $Revision$ + * @since 2.0 + */ +class MySqlSchemaManager extends AbstractSchemaManager +{ + protected function _getPortableViewDefinition($view) + { + return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); + } + + protected function _getPortableTableDefinition($table) + { + return array_shift($table); + } + + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['User'], + 'password' => $user['Password'], + ); + } + + 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'); + } + $tableIndexes[$k] = $v; + } + + return parent::_getPortableTableIndexesList($tableIndexes, $tableName); + } + + protected function _getPortableSequenceDefinition($sequence) + { + return end($sequence); + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database['Database']; + } + + /** + * Gets a portable column definition. + * + * The database type is mapped to a corresponding Doctrine mapping type. + * + * @param $tableColumn + * @return array + */ + 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']; + $decimal = ''; + } else { + $length = strtok('(), '); + $decimal = strtok('(), ') ? strtok('(), '):null; + } + $type = array(); + $fixed = null; + + if ( ! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $scale = null; + $precision = null; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + + switch ($dbType) { + case 'char': + $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 'tinyint': + case 'smallint': + case 'mediumint': + case 'int': + case 'integer': + case 'bigint': + case 'tinyblob': + case 'mediumblob': + case 'longblob': + case 'blob': + 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'] : null + ); + + if ($scale !== null && $precision !== null) { + $options['scale'] = $scale; + $options['precision'] = $precision; + } + + return new Column($tableColumn['field'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $key => $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..2a880d9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php @@ -0,0 +1,286 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Oracle Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @version $Revision$ + * @since 2.0 + */ +class OracleSchemaManager extends AbstractSchemaManager +{ + protected function _getPortableViewDefinition($view) + { + $view = \array_change_key_case($view, CASE_LOWER); + + return new View($view['view_name'], $view['text']); + } + + protected function _getPortableUserDefinition($user) + { + $user = \array_change_key_case($user, CASE_LOWER); + + return array( + 'user' => $user['username'], + ); + } + + protected function _getPortableTableDefinition($table) + { + $table = \array_change_key_case($table, CASE_LOWER); + + return $table['table_name']; + } + + /** + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + * @param array $tableIndexes + * @param string $tableName + * @return array + */ + 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'] = $tableIndex['column_name']; + $indexBuffer[] = $buffer; + } + return parent::_getPortableTableIndexesList($indexBuffer, $tableName); + } + + 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"; + } + } + + $type = array(); + $length = $unsigned = $fixed = null; + if ( ! empty($tableColumn['data_length'])) { + $length = $tableColumn['data_length']; + } + + if ( ! isset($tableColumn['column_name'])) { + $tableColumn['column_name'] = ''; + } + + if (stripos($tableColumn['data_default'], 'NULL') !== null) { + $tableColumn['data_default'] = null; + } + + $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': + $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'] : null, + 'platformDetails' => array(), + ); + + return new Column($tableColumn['column_name'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + 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' => $value['constraint_name'], + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['references_table'], + 'onDelete' => $value['delete_rule'], + ); + } + $list[$value['constraint_name']]['local'][$value['position']] = $value['local_column']; + $list[$value['constraint_name']]['foreign'][$value['position']] = $value['foreign_column']; + } + + $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']) + ); + } + + return $result; + } + + protected function _getPortableSequenceDefinition($sequence) + { + $sequence = \array_change_key_case($sequence, CASE_LOWER); + return new Sequence($sequence['sequence_name'], $sequence['increment_by'], $sequence['min_value']); + } + + protected function _getPortableFunctionDefinition($function) + { + $function = \array_change_key_case($function, CASE_LOWER); + return $function['name']; + } + + protected function _getPortableDatabaseDefinition($database) + { + $database = \array_change_key_case($database, CASE_LOWER); + return $database['username']; + } + + 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; + $result = $this->_conn->executeUpdate($query); + + $query = 'GRANT CREATE SESSION, CREATE TABLE, UNLIMITED TABLESPACE, CREATE SEQUENCE, CREATE TRIGGER TO ' . $username; + $result = $this->_conn->executeUpdate($query); + + return true; + } + + public function dropAutoincrement($table) + { + $sql = $this->_platform->getDropAutoincrementSql($table); + foreach ($sql as $query) { + $this->_conn->executeUpdate($query); + } + + return true; + } + + public function dropTable($name) + { + $this->dropAutoincrement($name); + + return parent::dropTable($name); + } +} 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..c67e8cd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php @@ -0,0 +1,365 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * PostgreSQL Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @since 2.0 + */ +class PostgreSqlSchemaManager extends AbstractSchemaManager +{ + /** + * @var array + */ + private $existingSchemaPaths; + + /** + * Get 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); + } + + /** + * Return 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); + } + + /** + * Get 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; + } + + /** + * Use this to set or reset 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); + }); + } + + 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) + ); + } + + public function dropDatabase($database) + { + $params = $this->_conn->getParams(); + $params["dbname"] = "postgres"; + $tmpPlatform = $this->_platform; + $tmpConn = $this->_conn; + + $this->_conn = \Doctrine\DBAL\DriverManager::getConnection($params); + $this->_platform = $this->_conn->getDatabasePlatform(); + + parent::dropDatabase($database); + + $this->_platform = $tmpPlatform; + $this->_conn = $tmpConn; + } + + public function createDatabase($database) + { + $params = $this->_conn->getParams(); + $params["dbname"] = "postgres"; + $tmpPlatform = $this->_platform; + $tmpConn = $this->_conn; + + $this->_conn = \Doctrine\DBAL\DriverManager::getConnection($params); + $this->_platform = $this->_conn->getDatabasePlatform(); + + parent::createDatabase($database); + + $this->_platform = $tmpPlatform; + $this->_conn = $tmpConn; + } + + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger['trigger_name']; + } + + protected function _getPortableViewDefinition($view) + { + return new View($view['viewname'], $view['definition']); + } + + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['usename'], + 'password' => $user['passwd'] + ); + } + + 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']; + } + } + + /** + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + * @param array $tableIndexes + * @param string $tableName + * @return array + */ + 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'] + ); + } + } + } + } + return parent::_getPortableTableIndexesList($buffer, $tableName); + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database['datname']; + } + + 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 ' . $sequenceName); + return new Sequence($sequenceName, $data[0]['increment_by'], $data[0]['min_value']); + } + + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + if (strtolower($tableColumn['type']) === 'varchar') { + // 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': + $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' => $tableColumn['comment'], + ); + + return new Column($tableColumn['field'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + +} 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..3075fc9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php @@ -0,0 +1,265 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; +use Doctrine\DBAL\Driver\SQLSrv\SQLSrvException; + +/** + * SQL Server Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Juozas Kaziukenas + * @since 2.0 + */ +class SQLServerSchemaManager extends AbstractSchemaManager +{ + /** + * @override + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $dbType = strtolower($tableColumn['TYPE_NAME']); + + $autoincrement = false; + if (stripos($dbType, 'identity')) { + $dbType = trim(str_ireplace('identity', '', $dbType)); + $autoincrement = true; + } + + $dbType = strtok($dbType, '(), '); + + $type = array(); + $unsigned = $fixed = null; + + if (!isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $default = $tableColumn['COLUMN_DEF']; + + while ($default != ($default2 = preg_replace("/^\((.*)\)$/", '$1', $default))) { + $default = trim($default2, "'"); + } + + $length = (int) $tableColumn['LENGTH']; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + switch ($type) { + case 'char': + if ($tableColumn['LENGTH'] == '1') { + $type = 'boolean'; + if (preg_match('/^(is|has)/', $tableColumn['name'])) { + $type = array_reverse($type); + } + } + $fixed = true; + break; + case 'text': + $fixed = false; + break; + } + switch ($dbType) { + case 'nchar': + case 'nvarchar': + case 'ntext': + // Unicode data requires 2 bytes per character + $length = $length / 2; + break; + } + + $options = array( + 'length' => ($length == 0 || !in_array($type, array('text', 'string'))) ? null : $length, + 'unsigned' => (bool) $unsigned, + 'fixed' => (bool) $fixed, + 'default' => $default !== 'NULL' ? $default : null, + 'notnull' => (bool) ($tableColumn['IS_NULLABLE'] != 'YES'), + 'scale' => $tableColumn['SCALE'], + 'precision' => $tableColumn['PRECISION'], + 'autoincrement' => $autoincrement, + ); + + return new Column($tableColumn['COLUMN_NAME'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + /** + * @override + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName=null) + { + // TODO: Remove code duplication with AbstractSchemaManager; + $result = array(); + foreach ($tableIndexRows as $tableIndex) { + $indexName = $keyName = $tableIndex['index_name']; + if (strpos($tableIndex['index_description'], 'primary key') !== false) { + $keyName = 'primary'; + } + $keyName = strtolower($keyName); + + $flags = array(); + if (strpos($tableIndex['index_description'], 'clustered') !== false) { + $flags[] = 'clustered'; + } else if (strpos($tableIndex['index_description'], 'nonclustered') !== false) { + $flags[] = 'nonclustered'; + } + + $result[$keyName] = array( + 'name' => $indexName, + 'columns' => explode(', ', $tableIndex['index_keys']), + 'unique' => strpos($tableIndex['index_description'], 'unique') !== false, + 'primary' => strpos($tableIndex['index_description'], 'primary key') !== false, + 'flags' => $flags, + ); + } + + $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']); + } + + if ($index) { + $indexes[$indexKey] = $index; + } + } + + return $indexes; + } + + /** + * @override + */ + public function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + (array) $tableForeignKey['ColumnName'], + $tableForeignKey['ReferenceTableName'], + (array) $tableForeignKey['ReferenceColumnName'], + $tableForeignKey['ForeignKey'], + array( + 'onUpdate' => str_replace('_', ' ', $tableForeignKey['update_referential_action_desc']), + 'onDelete' => str_replace('_', ' ', $tableForeignKey['delete_referential_action_desc']), + ) + ); + } + + /** + * @override + */ + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * @override + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['name']; + } + + /** + * @override + */ + protected function _getPortableViewDefinition($view) + { + // @todo + return new View($view['name'], null); + } + + /** + * List 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 Index[] $tableIndexes + */ + 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); + } + + /** + * @override + */ + 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']); + } + } + } + + return parent::alterTable($tableDiff); + } + + /** + * This function retrieves the constraints for a given column. + */ + 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..5a6ff75 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php @@ -0,0 +1,373 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * 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 +{ + /** + * @var array + */ + protected $_tables = array(); + + /** + * @var array + */ + protected $_sequences = array(); + + /** + * @var SchemaConfig + */ + protected $_schemaConfig = false; + + /** + * @param array $tables + * @param array $sequences + * @param array $views + * @param array $triggers + * @param SchemaConfig $schemaConfig + */ + public function __construct(array $tables=array(), array $sequences=array(), SchemaConfig $schemaConfig=null) + { + if ($schemaConfig == null) { + $schemaConfig = new SchemaConfig(); + } + $this->_schemaConfig = $schemaConfig; + $this->_setName($schemaConfig->getName() ?: 'public'); + + foreach ($tables as $table) { + $this->_addTable($table); + } + + foreach ($sequences as $sequence) { + $this->_addSequence($sequence); + } + } + + /** + * @return bool + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->_schemaConfig->hasExplicitForeignKeyIndexes(); + } + + /** + * @param Table $table + */ + protected function _addTable(Table $table) + { + $tableName = $table->getFullQualifiedName($this->getName()); + if(isset($this->_tables[$tableName])) { + throw SchemaException::tableAlreadyExists($tableName); + } + + $this->_tables[$tableName] = $table; + $table->setSchemaConfig($this->_schemaConfig); + } + + /** + * @param Sequence $sequence + */ + protected function _addSequence(Sequence $sequence) + { + $seqName = $sequence->getFullQualifiedName($this->getName()); + if (isset($this->_sequences[$seqName])) { + throw SchemaException::sequenceAlreadyExists($seqName); + } + $this->_sequences[$seqName] = $sequence; + } + + /** + * Get all tables of this schema. + * + * @return array + */ + public function getTables() + { + return $this->_tables; + } + + /** + * @param string $tableName + * @return Table + */ + public function getTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + if (!isset($this->_tables[$tableName])) { + throw SchemaException::tableDoesNotExist($tableName); + } + + return $this->_tables[$tableName]; + } + + /** + * @return string + */ + private function getFullQualifiedAssetName($name) + { + if ($this->isIdentifierQuoted($name)) { + $name = $this->trimQuotes($name); + } + if (strpos($name, ".") === false) { + $name = $this->getName() . "." . $name; + } + return strtolower($name); + } + + /** + * Does this schema have a table with the given name? + * + * @param string $tableName + * @return Schema + */ + public function hasTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + return isset($this->_tables[$tableName]); + } + + /** + * Get all table names, prefixed with a schema name, even the default one + * if present. + * + * @return array + */ + public function getTableNames() + { + return array_keys($this->_tables); + } + + public function hasSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + return isset($this->_sequences[$sequenceName]); + } + + /** + * @throws SchemaException + * @param string $sequenceName + * @return \Doctrine\DBAL\Schema\Sequence + */ + 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; + } + + /** + * Create a new table + * + * @param string $tableName + * @return 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; + } + + /** + * Rename a table + * + * @param string $oldTableName + * @param string $newTableName + * @return Schema + */ + public function renameTable($oldTableName, $newTableName) + { + $table = $this->getTable($oldTableName); + $table->_setName($newTableName); + + $this->dropTable($oldTableName); + $this->_addTable($table); + return $this; + } + + /** + * Drop a table from the schema. + * + * @param string $tableName + * @return Schema + */ + public function dropTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + $table = $this->getTable($tableName); + unset($this->_tables[$tableName]); + return $this; + } + + /** + * Create a new sequence + * + * @param string $sequenceName + * @param int $allocationSize + * @param int $initialValue + * @return Sequence + */ + public function createSequence($sequenceName, $allocationSize=1, $initialValue=1) + { + $seq = new Sequence($sequenceName, $allocationSize, $initialValue); + $this->_addSequence($seq); + return $seq; + } + + /** + * @param string $sequenceName + * @return Schema + */ + public function dropSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + unset($this->_sequences[$sequenceName]); + return $this; + } + + /** + * Return 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(\Doctrine\DBAL\Platforms\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(\Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + $dropSqlCollector = new DropSchemaSqlCollector($platform); + $this->visit($dropSqlCollector); + + return $dropSqlCollector->getQueries(); + } + + /** + * @param Schema $toSchema + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function getMigrateToSql(Schema $toSchema, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($this, $toSchema); + return $schemaDiff->toSql($platform); + } + + /** + * @param Schema $fromSchema + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function getMigrateFromSql(Schema $fromSchema, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $this); + return $schemaDiff->toSql($platform); + } + + /** + * @param Visitor $visitor + */ + public function visit(Visitor $visitor) + { + $visitor->acceptSchema($this); + + 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..d90da20 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Configuration for a Schema + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SchemaConfig +{ + /** + * @var bool + */ + protected $hasExplicitForeignKeyIndexes = false; + + /** + * @var int + */ + protected $maxIdentifierLength = 63; + + /** + * @var string + */ + protected $name; + + /** + * @var array + */ + protected $defaultTableOptions = array(); + + /** + * @return bool + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->hasExplicitForeignKeyIndexes; + } + + /** + * @param bool $flag + */ + public function setExplicitForeignKeyIndexes($flag) + { + $this->hasExplicitForeignKeyIndexes = (bool)$flag; + } + + /** + * @param int $length + */ + public function setMaxIdentifierLength($length) + { + $this->maxIdentifierLength = (int)$length; + } + + /** + * @return int + */ + public function getMaxIdentifierLength() + { + return $this->maxIdentifierLength; + } + + /** + * Get default namespace of schema objects. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * set default namespace name of schema objects. + * + * @param string $name the value to set. + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Get the default options that are passed to Table instances created with + * Schema#createTable(). + * + * @return array + */ + public function getDefaultTableOptions() + { + return $this->defaultTableOptions; + } + + 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..ba0d9e6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php @@ -0,0 +1,176 @@ +. + */ + +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 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class SchemaDiff +{ + /** + * All added tables + * + * @var array(string=>ezcDbSchemaTable) + */ + public $newTables = array(); + + /** + * All changed tables + * + * @var array(string=>ezcDbSchemaTableDiff) + */ + public $changedTables = array(); + + /** + * All removed tables + * + * @var array(string=>Table) + */ + public $removedTables = array(); + + /** + * @var array + */ + public $newSequences = array(); + + /** + * @var array + */ + public $changedSequences = array(); + + /** + * @var array + */ + public $removedSequences = array(); + + /** + * @var array + */ + public $orphanedForeignKeys = array(); + + /** + * Constructs an SchemaDiff object. + * + * @param array(string=>Table) $newTables + * @param array(string=>TableDiff) $changedTables + * @param array(string=>bool) $removedTables + */ + public function __construct($newTables = array(), $changedTables = array(), $removedTables = array()) + { + $this->newTables = $newTables; + $this->changedTables = $changedTables; + $this->removedTables = $removedTables; + } + + /** + * 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 AbstractPlatform $platform + * @return array + */ + public function toSaveSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, true); + } + + /** + * @param AbstractPlatform $platform + * @return array + */ + public function toSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, false); + } + + /** + * @param AbstractPlatform $platform + * @param bool $saveMode + * @return array + */ + protected function _toSql(AbstractPlatform $platform, $saveMode = false) + { + $sql = array(); + + 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..a8cb93d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php @@ -0,0 +1,126 @@ +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." + ); + } + + static public function alterTableChangeNotSupported($changeName) { + return new self ("Alter table change not supported, given '$changeName'"); + } +} \ No newline at end of file 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..87ff0fa --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php @@ -0,0 +1,120 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * Sequence Structure + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Sequence extends AbstractAsset +{ + /** + * @var int + */ + protected $_allocationSize = 1; + + /** + * @var int + */ + protected $_initialValue = 1; + + /** + * + * @param string $name + * @param int $allocationSize + * @param int $initialValue + */ + public function __construct($name, $allocationSize=1, $initialValue=1) + { + $this->_setName($name); + $this->_allocationSize = (is_numeric($allocationSize))?$allocationSize:1; + $this->_initialValue = (is_numeric($initialValue))?$initialValue:1; + } + + public function getAllocationSize() + { + return $this->_allocationSize; + } + + public function getInitialValue() + { + return $this->_initialValue; + } + + public function setAllocationSize($allocationSize) + { + $this->_allocationSize = (is_numeric($allocationSize))?$allocationSize:1; + } + + public function setInitialValue($initialValue) + { + $this->_initialValue = (is_numeric($initialValue))?$initialValue:1; + } + + /** + * Check 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 Table $table + * + * @return bool + */ + 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 Visitor $visitor + */ + 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..f980f95 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php @@ -0,0 +1,193 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * SqliteSchemaManager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Jonathan H. Wage + * @version $Revision$ + * @since 2.0 + */ +class SqliteSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + * + * @override + */ + public function dropDatabase($database) + { + if (file_exists($database)) { + unlink($database); + } + } + + /** + * {@inheritdoc} + * + * @override + */ + 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(); + } + + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + * @param array $tableIndexes + * @param string $tableName + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $indexBuffer = array(); + + // fetch primary + $stmt = $this->_conn->executeQuery( "PRAGMA TABLE_INFO ('$tableName')" ); + $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC); + foreach($indexArray as $indexColumnRow) { + if($indexColumnRow['pk'] == "1") { + $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); + } + + protected function _getPortableTableIndexDefinition($tableIndex) + { + return array( + 'name' => $tableIndex['name'], + 'unique' => (bool) $tableIndex['unique'] + ); + } + + protected function _getPortableTableColumnDefinition($tableColumn) + { + $e = explode('(', $tableColumn['type']); + $tableColumn['type'] = $e[0]; + if (isset($e[1])) { + $length = trim($e[1], ')'); + $tableColumn['length'] = $length; + } + + $dbType = strtolower($tableColumn['type']); + $length = isset($tableColumn['length']) ? $tableColumn['length'] : null; + $unsigned = (boolean) isset($tableColumn['unsigned']) ? $tableColumn['unsigned'] : false; + $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); + } + + protected function _getPortableViewDefinition($view) + { + return new View($view['name'], $view['sql']); + } +} 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..16fb033 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php @@ -0,0 +1,58 @@ +. + */ + +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 Connection + */ + protected $conn; + + public function __construct(Connection $conn) + { + $this->conn = $conn; + } + + protected function processSqlSafely(array $sql) + { + foreach ($sql as $s) { + try { + $this->conn->exec($s); + } catch(\Exception $e) { + + } + } + } + + 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..c249815 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php @@ -0,0 +1,96 @@ +. + */ + +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 +{ + /** + * Get the SQL statements that can be executed to create the schema. + * + * @param Schema $createSchema + * @return array + */ + function getCreateSchema(Schema $createSchema); + + /** + * Get the SQL Statements to update given schema with the underlying db. + * + * @param Schema $toSchema + * @param bool $noDrops + * @return array + */ + function getUpdateSchema(Schema $toSchema, $noDrops = false); + + /** + * Get the SQL Statements to drop the given schema from underlying db. + * + * @param Schema $dropSchema + * @return array + */ + function getDropSchema(Schema $dropSchema); + + /** + * Get the SQL statements to drop all schema assets from underlying db. + * + * @return array + */ + function getDropAllSchema(); + + /** + * Create the Schema + * + * @param Schema $createSchema + * @return void + */ + function createSchema(Schema $createSchema); + + /** + * Update the Schema to new schema version. + * + * @param Schema $toSchema + * @param bool $noDrops + * @return void + */ + function updateSchema(Schema $toSchema, $noDrops = false); + + /** + * Drop the given database schema from the underlying db. + * + * @param Schema $dropSchema + * @return void + */ + function dropSchema(Schema $dropSchema); + + /** + * Drop all assets from the underyling 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..38ea53a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php @@ -0,0 +1,197 @@ +. + */ +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; + + public function __construct(Connection $conn) + { + parent::__construct($conn); + $this->platform = $conn->getDatabasePlatform(); + } + + /** + * Get the SQL statements that can be executed to create the schema. + * + * @param Schema $createSchema + * @return array + */ + public function getCreateSchema(Schema $createSchema) + { + return $createSchema->toSql($this->platform); + } + + /** + * Get the SQL Statements to update given schema with the underlying db. + * + * @param Schema $toSchema + * @param bool $noDrops + * @return array + */ + 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); + } + + /** + * Get the SQL Statements to drop the given schema from underlying db. + * + * @param Schema $dropSchema + * @return array + */ + 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) { + /* @var $sequence 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(); + } + + /** + * Get the SQL statements to drop all schema assets from underlying db. + * + * @return array + */ + 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(); + } + + /** + * Create the Schema + * + * @param Schema $createSchema + * @return void + */ + public function createSchema(Schema $createSchema) + { + $this->processSql($this->getCreateSchema($createSchema)); + } + + /** + * Update the Schema to new schema version. + * + * @param Schema $toSchema + * @param bool $noDrops + * @return void + */ + public function updateSchema(Schema $toSchema, $noDrops = false) + { + $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); + } + + /** + * Drop the given database schema from the underlying db. + * + * @param Schema $dropSchema + * @return void + */ + public function dropSchema(Schema $dropSchema) + { + $this->processSqlSafely($this->getDropSchema($dropSchema)); + } + + /** + * Drop all assets from the underyling db. + * + * @return void + */ + 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..d0763c5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php @@ -0,0 +1,678 @@ +. + */ + +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 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Table extends AbstractAsset +{ + /** + * @var string + */ + protected $_name = null; + + /** + * @var array + */ + protected $_columns = array(); + + /** + * @var array + */ + protected $_indexes = array(); + + /** + * @var string + */ + protected $_primaryKeyName = false; + + /** + * @var array + */ + protected $_fkConstraints = array(); + + /** + * @var array + */ + protected $_options = array(); + + /** + * @var SchemaConfig + */ + protected $_schemaConfig = null; + + /** + * + * @param string $tableName + * @param array $columns + * @param array $indexes + * @param array $fkConstraints + * @param int $idGeneratorType + * @param array $options + */ + 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); + $this->_idGeneratorType = $idGeneratorType; + + 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 + */ + public function setSchemaConfig(SchemaConfig $schemaConfig) + { + $this->_schemaConfig = $schemaConfig; + } + + /** + * @return int + */ + protected function _getMaxIdentifierLength() + { + if ($this->_schemaConfig instanceof SchemaConfig) { + return $this->_schemaConfig->getMaxIdentifierLength(); + } else { + return 63; + } + } + + /** + * Set Primary Key + * + * @param array $columns + * @param string $indexName + * @return Table + */ + public function setPrimaryKey(array $columns, $indexName = false) + { + $primaryKey = $this->_createIndex($columns, $indexName ?: "primary", true, true); + + foreach ($columns as $columnName) { + $column = $this->getColumn($columnName); + $column->setNotnull(true); + } + + return $primaryKey; + } + + /** + * @param array $columnNames + * @param string $indexName + * @return Table + */ + public function addIndex(array $columnNames, $indexName = null) + { + if($indexName == null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "idx", $this->_getMaxIdentifierLength() + ); + } + + return $this->_createIndex($columnNames, $indexName, false, false); + } + + /** + * Drop an index from this table. + * + * @param string $indexName + * @return void + */ + public function dropPrimaryKey() + { + $this->dropIndex($this->_primaryKeyName); + $this->_primaryKeyName = false; + } + + /** + * Drop an index from this table. + * + * @param string $indexName + * @return void + */ + public function dropIndex($indexName) + { + $indexName = strtolower($indexName); + if ( ! $this->hasIndex($indexName)) { + throw SchemaException::indexDoesNotExist($indexName, $this->_name); + } + unset($this->_indexes[$indexName]); + } + + /** + * + * @param array $columnNames + * @param string $indexName + * @return Table + */ + public function addUniqueIndex(array $columnNames, $indexName = null) + { + if ($indexName === null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "uniq", $this->_getMaxIdentifierLength() + ); + } + + return $this->_createIndex($columnNames, $indexName, true, false); + } + + /** + * Check if an index begins in the order of the given columns. + * + * @param array $columnsNames + * @return bool + */ + 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 bool $isUnique + * @param bool $isPrimary + * @return Table + */ + private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary) + { + if (preg_match('(([^a-zA-Z0-9_]+))', $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); + } + } + $this->_addIndex(new Index($indexName, $columnNames, $isUnique, $isPrimary)); + return $this; + } + + /** + * @param string $columnName + * @param string $columnType + * @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; + } + + /** + * Rename Column + * + * @param string $oldColumnName + * @param string $newColumnName + * @return Table + */ + 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 Table + */ + public function changeColumn($columnName, array $options) + { + $column = $this->getColumn($columnName); + $column->setOptions($options); + return $this; + } + + /** + * Drop Column from Table + * + * @param string $columnName + * @return Table + */ + public function dropColumn($columnName) + { + $columnName = strtolower($columnName); + $column = $this->getColumn($columnName); + unset($this->_columns[$columnName]); + return $this; + } + + + /** + * Add a foreign key constraint + * + * Name is inferred from the local columns + * + * @param Table $foreignTable + * @param array $localColumns + * @param array $foreignColumns + * @param array $options + * @param string $constraintName + * @return Table + */ + 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); + } + + /** + * Add a foreign key constraint + * + * Name is to be generated by the database itsself. + * + * @deprecated Use {@link addForeignKeyConstraint} + * @param Table $foreignTable + * @param array $localColumns + * @param array $foreignColumns + * @param array $options + * @return Table + */ + public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options); + } + + /** + * Add a foreign key constraint with a given name + * + * @deprecated Use {@link addForeignKeyConstraint} + * @param string $name + * @param Table $foreignTable + * @param array $localColumns + * @param array $foreignColumns + * @param array $options + * @return Table + */ + public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + if ($foreignTable instanceof Table) { + $foreignTableName = $foreignTable->getName(); + + foreach ($foreignColumnNames as $columnName) { + if ( ! $foreignTable->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName()); + } + } + } else { + $foreignTableName = $foreignTable; + } + + foreach ($localColumnNames as $columnName) { + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + $constraint = new ForeignKeyConstraint( + $localColumnNames, $foreignTableName, $foreignColumnNames, $name, $options + ); + $this->_addForeignKeyConstraint($constraint); + + return $this; + } + + /** + * @param string $name + * @param string $value + * @return Table + */ + public function addOption($name, $value) + { + $this->_options[$name] = $value; + return $this; + } + + /** + * @param Column $column + */ + protected function _addColumn(Column $column) + { + $columnName = $column->getName(); + $columnName = strtolower($columnName); + + if (isset($this->_columns[$columnName])) { + throw SchemaException::columnAlreadyExists($this->getName(), $columnName); + } + + $this->_columns[$columnName] = $column; + } + + /** + * Add index to table + * + * @param Index $indexCandidate + * @return Table + */ + protected function _addIndex(Index $indexCandidate) + { + // check for duplicates + foreach ($this->_indexes as $existingIndex) { + if ($indexCandidate->isFullfilledBy($existingIndex)) { + return $this; + } + } + + $indexName = $indexCandidate->getName(); + $indexName = strtolower($indexName); + + if (isset($this->_indexes[$indexName]) || ($this->_primaryKeyName != false && $indexCandidate->isPrimary())) { + throw SchemaException::indexAlreadyExists($indexName, $this->_name); + } + + // remove overruled indexes + foreach ($this->_indexes as $idxKey => $existingIndex) { + if ($indexCandidate->overrules($existingIndex)) { + unset($this->_indexes[$idxKey]); + } + } + + if ($indexCandidate->isPrimary()) { + $this->_primaryKeyName = $indexName; + } + + $this->_indexes[$indexName] = $indexCandidate; + return $this; + } + + /** + * @param ForeignKeyConstraint $constraint + */ + 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 = strtolower($name); + + $this->_fkConstraints[$name] = $constraint; + // add an explicit index on the foreign key columns. If there is already an index that fullfils 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 compuation overhead in this case, however no duplicate indexes are ever added (based on columns). + $this->addIndex($constraint->getColumns()); + } + + /** + * Does Table have a foreign key constraint with the given name? + * * + * @param string $constraintName + * @return bool + */ + public function hasForeignKey($constraintName) + { + $constraintName = strtolower($constraintName); + return isset($this->_fkConstraints[$constraintName]); + } + + /** + * @param string $constraintName + * @return ForeignKeyConstraint + */ + public function getForeignKey($constraintName) + { + $constraintName = strtolower($constraintName); + if(!$this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + } + + return $this->_fkConstraints[$constraintName]; + } + + public function removeForeignKey($constraintName) + { + $constraintName = strtolower($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; + } + + + /** + * Does this table have a column with the given name? + * + * @param string $columnName + * @return bool + */ + public function hasColumn($columnName) + { + $columnName = $this->trimQuotes(strtolower($columnName)); + return isset($this->_columns[$columnName]); + } + + /** + * Get a column instance + * + * @param string $columnName + * @return Column + */ + public function getColumn($columnName) + { + $columnName = strtolower($this->trimQuotes($columnName)); + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + + return $this->_columns[$columnName]; + } + + /** + * @return Index|null + */ + public function getPrimaryKey() + { + if ( ! $this->hasPrimaryKey()) { + return null; + } + return $this->getIndex($this->_primaryKeyName); + } + + public function getPrimaryKeyColumns() + { + if ( ! $this->hasPrimaryKey()) { + throw new DBALException("Table " . $this->getName() . " has no primary key."); + } + return $this->getPrimaryKey()->getColumns(); + } + + /** + * Check if this table has a primary key. + * + * @return bool + */ + public function hasPrimaryKey() + { + return ($this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName)); + } + + /** + * @param string $indexName + * @return bool + */ + public function hasIndex($indexName) + { + $indexName = strtolower($indexName); + return (isset($this->_indexes[$indexName])); + } + + /** + * @param string $indexName + * @return Index + */ + public function getIndex($indexName) + { + $indexName = strtolower($indexName); + if ( ! $this->hasIndex($indexName)) { + throw SchemaException::indexDoesNotExist($indexName, $this->_name); + } + return $this->_indexes[$indexName]; + } + + /** + * @return array + */ + public function getIndexes() + { + return $this->_indexes; + } + + /** + * Get Constraints + * + * @return array + */ + public function getForeignKeys() + { + return $this->_fkConstraints; + } + + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + public function getOption($name) + { + return $this->_options[$name]; + } + + public function getOptions() + { + return $this->_options; + } + + /** + * @param Visitor $visitor + */ + 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 + */ + 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); + } + } +} 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..257a3bd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php @@ -0,0 +1,136 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Table 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 TableDiff +{ + /** + * @var string + */ + public $name = null; + + /** + * @var string + */ + public $newName = false; + + /** + * All added fields + * + * @var array(string=>Column) + */ + public $addedColumns; + + /** + * All changed fields + * + * @var array(string=>Column) + */ + public $changedColumns = array(); + + /** + * All removed fields + * + * @var array(string=>Column) + */ + public $removedColumns = array(); + + /** + * Columns that are only renamed from key to column instance name. + * + * @var array(string=>Column) + */ + public $renamedColumns = array(); + + /** + * All added indexes + * + * @var array(string=>Index) + */ + public $addedIndexes = array(); + + /** + * All changed indexes + * + * @var array(string=>Index) + */ + public $changedIndexes = array(); + + /** + * All removed indexes + * + * @var array(string=>bool) + */ + public $removedIndexes = array(); + + /** + * All added foreign key definitions + * + * @var array + */ + public $addedForeignKeys = array(); + + /** + * All changed foreign keys + * + * @var array + */ + public $changedForeignKeys = array(); + + /** + * All removed foreign keys + * + * @var array + */ + public $removedForeignKeys = array(); + + /** + * Constructs an TableDiff object. + * + * @param array(string=>Column) $addedColumns + * @param array(string=>Column) $changedColumns + * @param array(string=>bool) $removedColumns + * @param array(string=>Index) $addedIndexes + * @param array(string=>Index) $changedIndexes + * @param array(string=>bool) $removedIndexes + */ + public function __construct($tableName, $addedColumns = array(), + $changedColumns = array(), $removedColumns = array(), $addedIndexes = array(), + $changedIndexes = array(), $removedIndexes = array()) + { + $this->name = $tableName; + $this->addedColumns = $addedColumns; + $this->changedColumns = $changedColumns; + $this->removedColumns = $removedColumns; + $this->addedIndexes = $addedIndexes; + $this->changedIndexes = $changedIndexes; + $this->removedIndexes = $removedIndexes; + } +} 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..8283d07 --- /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 + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class View extends AbstractAsset +{ + /** + * @var string + */ + private $_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/CreateSchemaSqlCollector.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php new file mode 100644 index 0000000..51228c1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php @@ -0,0 +1,178 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +class CreateSchemaSqlCollector implements Visitor +{ + /** + * @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; + } + + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema) + { + + } + + /** + * Generate DDL Statements to create the accepted table with all its dependencies. + * + * @param Table $table + */ + public function acceptTable(Table $table) + { + $namespace = $this->getNamespace($table); + + $this->_createTableQueries[$namespace] = array_merge( + $this->_createTableQueries[$namespace], + $this->_platform->getCreateTableSQL($table) + ); + } + + public function acceptColumn(Table $table, Column $column) + { + + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + $namespace = $this->getNamespace($localTable); + + if ($this->_platform->supportsForeignKeyConstraints()) { + $this->_createFkConstraintQueries[$namespace] = array_merge( + $this->_createFkConstraintQueries[$namespace], + (array) $this->_platform->getCreateForeignKeySQL( + $fkConstraint, $localTable + ) + ); + } + } + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + + } + + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + $namespace = $this->getNamespace($sequence); + + $this->_createSequenceQueries[$namespace] = array_merge( + $this->_createSequenceQueries[$namespace], + (array)$this->_platform->getCreateSequenceSQL($sequence) + ); + } + + private function getNamespace($asset) + { + $namespace = $asset->getNamespaceName() ?: 'default'; + if ( !isset($this->_createTableQueries[$namespace])) { + $this->_createTableQueries[$namespace] = array(); + $this->_createSequenceQueries[$namespace] = array(); + $this->_createFkConstraintQueries[$namespace] = array(); + } + + return $namespace; + } + + /** + * @return array + */ + public function resetQueries() + { + $this->_createTableQueries = array(); + $this->_createSequenceQueries = array(); + $this->_createFkConstraintQueries = array(); + } + + /** + * Get all queries collected so far. + * + * @return array + */ + public function getQueries() + { + $sql = array(); + foreach (array_keys($this->_createTableQueries) as $namespace) { + if ($this->_platform->supportsSchemas()) { + // TODO: Create Schema here + } + } + foreach ($this->_createTableQueries as $schemaSql) { + $sql = array_merge($sql, $schemaSql); + } + foreach ($this->_createSequenceQueries as $schemaSql) { + $sql = array_merge($sql, $schemaSql); + } + foreach ($this->_createFkConstraintQueries as $schemaSql) { + $sql = array_merge($sql, $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..3d74cb9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php @@ -0,0 +1,160 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\SchemaException, + Doctrine\DBAL\Schema\Index; + +/** + * Gather SQL statements that allow to completly drop the current schema. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DropSchemaSqlCollector implements Visitor +{ + /** + * @var \SplObjectStorage + */ + private $constraints; + + /** + * @var \SplObjectStorage + */ + private $sequences; + + /** + * @var \SplObjectStorage + */ + private $tables; + + /** + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->platform = $platform; + $this->clearQueries(); + } + + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema) + { + + } + + /** + * @param Table $table + */ + public function acceptTable(Table $table) + { + $this->tables->attach($table); + } + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + if (strlen($fkConstraint->getName()) == 0) { + throw SchemaException::namedForeignKeyRequired($localTable, $fkConstraint); + } + + $this->constraints->attach($fkConstraint); + $this->constraints[$fkConstraint] = $localTable; + } + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + + } + + /** + * @param Sequence $sequence + */ + 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..8ae69a2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php @@ -0,0 +1,151 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +class Graphviz implements \Doctrine\DBAL\Schema\Visitor\Visitor +{ + private $output = ''; + + public function acceptColumn(Table $table, Column $column) + { + + } + + 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', + ) + ); + } + + public function acceptIndex(Table $table, Index $index) + { + + } + + 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"; + } + + public function acceptSequence(Sequence $sequence) + { + + } + + public function acceptTable(Table $table) + { + $this->output .= $this->createNode( + $table->getName(), + array( + 'label' => $this->createTableLabel( $table ), + 'shape' => 'plaintext', + ) + ); + } + + 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; + } + + private function createNode( $name, $options ) + { + $node = $name . " ["; + foreach( $options as $key => $value ) + { + $node .= $key . '=' . $value . ' '; + } + $node .= "]\n"; + return $node; + } + + private function createNodeRelation( $node1, $node2, $options ) + { + $relation = $node1 . ' -> ' . $node2 . ' ['; + foreach( $options as $key => $value ) + { + $relation .= $key . '=' . $value . ' '; + } + $relation .= "]\n"; + return $relation; + } + + /** + * Write 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->output . "}"); + } +} 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..348b2b7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +/** + * Remove 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 implements Visitor +{ + /** + * @var Schema + */ + private $schema; + + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema) + { + $this->schema = $schema; + } + + /** + * @param Table $table + */ + public function acceptTable(Table $table) + { + if ( ! $table->isInDefaultNamespace($this->schema->getName()) ) { + $this->schema->dropTable($table->getName()); + } + } + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + if ( ! $sequence->isInDefaultNamespace($this->schema->getName()) ) { + $this->schema->dropSequence($sequence->getName()); + } + } + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + 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()); + } + } + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + } +} 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..45eff81 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +/** + * Schema Visitor used for Validation or Generation purposes. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +interface Visitor +{ + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema); + + /** + * @param Table $table + */ + public function acceptTable(Table $table); + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column); + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint); + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index); + + /** + * @param Sequence $sequence + */ + 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..6453e63 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php @@ -0,0 +1,201 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Configuration; + +use Doctrine\Common\EventManager; + +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 int + */ + private $activeShardId; + + /** + * @var array + */ + private $connections; + + /** + * @var ShardManager + */ + private $shardManager; + + 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 specificy 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); + } + + /** + * Connect to a given shard + * + * @param mixed $shardId + * @return bool + */ + 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 Event\ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + + /** + * Connect to a specific connection + * + * @param string $shardId + * @return Driver + */ + 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); + } + + public function isConnected($shardId = null) + { + if ($shardId === null) { + return $this->_conn !== null; + } + + return isset($this->activeConnections[$shardId]); + } + + public function close() + { + $this->_conn = null; + $this->activeConnections = 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..6f6c5d2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php @@ -0,0 +1,98 @@ +. + */ + +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 +{ + private $conn; + private $choser; + private $currentDistributionValue; + + public function __construct(PoolingShardConnection $conn) + { + $params = $conn->getParams(); + $this->conn = $conn; + $this->choser = $params['shardChoser']; + } + + public function selectGlobal() + { + $this->conn->connect(0); + $this->currentDistributionValue = null; + } + + public function selectShard($distributionValue) + { + $shardId = $this->choser->pickShard($distributionValue, $this->conn); + $this->conn->connect($shardId); + $this->currentDistributionValue = $distributionValue; + } + + public function getCurrentDistributionValue() + { + return $this->currentDistributionValue; + } + + public function getShards() + { + $params = $this->conn->getParams(); + $shards = array(); + + foreach ($params['shards'] as $shard) { + $shards[] = array('id' => $shard['id']); + } + + return $shards; + } + + 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->selectShard($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..4001379 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php @@ -0,0 +1,296 @@ +. + */ + +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\Sharding\SingleDatabaseSynchronizer; + +/** + * 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\Sharding\SingleDatabaseSynchronizer}. + * + * @author Benjamin Eberlei + */ +class SQLAzureFederationsSynchronizer extends AbstractSchemaSynchronizer +{ + const FEDERATION_TABLE_FEDERATED = 'azure.federated'; + const FEDERATION_DISTRIBUTION_NAME = 'azure.federatedOnDistributionName'; + + + /** + * @var SQLAzureShardManager + */ + private $shardManager; + + /** + * @var SchemaSynchronizer + */ + private $synchronizer; + + public function __construct(Connection $conn, SQLAzureShardManager $shardManager, SchemaSynchronizer $sync = null) + { + parent::__construct($conn); + $this->shardManager = $shardManager; + $this->synchronizer = $sync ?: new SingleDatabaseSynchronizer($conn); + } + + /** + * Get the SQL statements that can be executed to create the schema. + * + * @param Schema $createSchema + * @return array + */ + 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; + } + + /** + * Get the SQL Statements to update given schema with the underlying db. + * + * @param Schema $toSchema + * @param bool $noDrops + * @return array + */ + public function getUpdateSchema(Schema $toSchema, $noDrops = false) + { + return $this->work($toSchema, function($synchronizer, $schema) use ($noDrops) { + return $synchronizer->getUpdateSchema($schema, $noDrops); + }); + } + + /** + * Get the SQL Statements to drop the given schema from underlying db. + * + * @param Schema $dropSchema + * @return array + */ + public function getDropSchema(Schema $dropSchema) + { + return $this->work($dropSchema, function($synchronizer, $schema) { + return $synchronizer->getDropSchema($schema); + }); + } + + /** + * Create the Schema + * + * @param Schema $createSchema + * @return void + */ + public function createSchema(Schema $createSchema) + { + $this->processSql($this->getCreateSchema($createSchema)); + } + + /** + * Update the Schema to new schema version. + * + * @param Schema $toSchema + * @return void + */ + public function updateSchema(Schema $toSchema, $noDrops = false) + { + $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); + } + + /** + * Drop the given database schema from the underlying db. + * + * @param Schema $dropSchema + * @return void + */ + public function dropSchema(Schema $dropSchema) + { + $this->processSqlSafely($this->getDropSchema($dropSchema)); + } + + /** + * Get the SQL statements to drop all schema assets from underlying db. + * + * @return array + */ + 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; + } + + /** + * Drop all assets from the underyling db. + * + * @return void + */ + public function dropAllSchema() + { + $this->processSqlSafely($this->getDropAllSchema()); + } + + private function partitionSchema(Schema $schema) + { + return array( + $this->extractSchemaFederation($schema, false), + $this->extractSchemaFederation($schema, true), + ); + } + + private function extractSchemaFederation(Schema $schema, $isFederation) + { + $partionedSchema = clone $schema; + + foreach ($partionedSchema->getTables() as $table) { + if ($isFederation) { + $table->addOption(self::FEDERATION_DISTRIBUTION_NAME, $this->shardManager->getDistributionKey()); + } + + if ( $table->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) { + $partionedSchema->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 $partionedSchema; + } + + /** + * Work on the Global/Federation based on currently existing shards and + * perform the given operation on the underyling schema synchronizer given + * the different partioned schema instances. + * + * @param 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; + } + + 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; + } + + 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..80ca3d9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php @@ -0,0 +1,238 @@ +. + */ + +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 bool + */ + private $filteringEnabled; + + /** + * @var string + */ + private $distributionKey; + + /** + * @var string + */ + private $distributionType; + + /** + * @var Connection + */ + private $conn; + + /** + * @var string + */ + private $currentDistributionValue; + + /** + * @param Connection $conn + */ + 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; + } + + /** + * Get name of the federation + * + * @return string + */ + public function getFederationName() + { + return $this->federationName; + } + + /** + * Get the distribution key + * + * @return string + */ + public function getDistributionKey() + { + return $this->distributionKey; + } + + /** + * Get the Doctrine Type name used for the distribution + * + * @return string + */ + public function getDistributionType() + { + return $this->distributionType; + } + + /** + * Enabled/Disable filtering on the fly. + * + * @param bool $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; + } + + /** + * Split Federation at a given distribution value. + * + * @param mixed $splitDistributionValue + */ + 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..2b2b457 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php @@ -0,0 +1,161 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + 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; + + public function __construct(array $excludedTables = array(), $tenantColumnName = 'tenant_id', $distributionName = null) + { + $this->excludedTables = $excludedTables; + $this->tenantColumnName = $tenantColumnName; + $this->distributionName = $distributionName ?: $tenantColumnName; + } + + /** + * @param Table $table + */ + 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'); + } + } + + private function getClusteredIndex($table) + { + foreach ($table->getIndexes() as $index) { + if ($index->isPrimary() && ! $index->hasFlag('nonclustered')) { + return $index; + } else if ($index->hasFlag('clustered')) { + return $index; + } + } + throw new \RuntimeException("No clustered index found on table " . $table->getName()); + } + + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * @param Sequence $sequence + */ + 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..c6cdabf --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php @@ -0,0 +1,37 @@ +. + */ + +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 +{ + 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..2aa9f74 --- /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 +{ + /** + * Pick a shard for the given distribution value + * + * @param string $distributionValue + * @param PoolingShardConnection $conn + * @return int + */ + 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..aa67992 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardManager.php @@ -0,0 +1,95 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\Connection; + +/** + * 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 excecuted you + * will hit the wrong shard. + * + * @author Benjamin Eberlei + */ +interface ShardManager +{ + /** + * Select global database with global data. + * + * This is the default database that is connected when no shard is + * selected. + * + * @return void + */ + function selectGlobal(); + + /** + * SELECT queries after this statement will be issued against the selected + * shard. + * + * @throws ShardingException If no value is passed as shard identifier. + * @param mixed $distributionValue + * @param array $options + * @return void + */ + function selectShard($distributionValue); + + /** + * Get the distribution value currently used for sharding. + * + * @return string + */ + function getCurrentDistributionValue(); + + /** + * Get information about the amount of shards and other details. + * + * Format is implementation specific, each shard is one element and has a + * 'name' attribute at least. + * + * @return array + */ + function getShards(); + + /** + * Query all shards in undefined order and return the results appended to + * each other. Restore the previous distribution value after execution. + * + * Using {@link 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..06dd169 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardingException.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\DBALException; + +/** + * Sharding related Exceptions + * + * @since 2.3 + */ +class ShardingException extends DBALException +{ + static public function notImplemented() + { + return new self("This functionality is not implemented with this sharding provider.", 1331557937); + } + + static public function missingDefaultFederationName() + { + return new self("SQLAzure requires a federation name to be set during sharding configuration.", 1332141280); + } + + static public function missingDefaultDistributionKey() + { + return new self("SQLAzure requires a distribution key to be set during sharding configuration.", 1332141329); + } + + static public function activeTransaction() + { + return new self("Cannot switch shard during an active transaction.", 1332141766); + } + + static public function noShardDistributionValue() + { + return new self("You have to specify a string or integer as shard distribution value.", 1332142103); + } + + 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..1bf9d74 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php @@ -0,0 +1,264 @@ +. + */ + +namespace Doctrine\DBAL; + +use PDO, + Doctrine\DBAL\Types\Type, + 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 +{ + /** + * @var string The SQL statement. + */ + protected $sql; + /** + * @var array The bound parameters. + */ + protected $params = array(); + /** + * @var array The parameter types + */ + protected $types = array(); + /** + * @var \Doctrine\DBAL\Driver\Statement The underlying driver statement. + */ + protected $stmt; + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform The underlying database platform. + */ + protected $platform; + /** + * @var \Doctrine\DBAL\Connection The connection this statement is bound to and executed on. + */ + protected $conn; + + /** + * Creates a new Statement for the given SQL and Connection. + * + * @param string $sql The SQL of the statement. + * @param \Doctrine\DBAL\Connection 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. + * @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 $params + * @return boolean TRUE on success, FALSE on failure. + */ + public function execute($params = null) + { + $logger = $this->conn->getConfiguration()->getSQLLogger(); + if ($logger) { + $logger->startQuery($this->sql, $this->params, $this->types); + } + + try { + $stmt = $this->stmt->execute($params); + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($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(); + } + + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + if ($arg2 === null) { + return $this->stmt->setFetchMode($fetchMode); + } else if ($arg3 === null) { + return $this->stmt->setFetchMode($fetchMode, $arg2); + } + + return $this->stmt->setFetchMode($fetchMode, $arg2, $arg3); + } + + public function getIterator() + { + return $this->stmt; + } + + /** + * Fetches the next row from a result set. + * + * @param integer $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 $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..6b2b8c2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console; + +/** + * 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 Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + 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) { + $fileName = realpath($fileName); + + if ( ! file_exists($fileName)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not exist.", $fileName) + ); + } else if ( ! is_readable($fileName)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not have read permissions.", $fileName) + ); + } + + $output->write(sprintf("Processing file '%s'... ", $fileName)); + $sql = file_get_contents($fileName); + + 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..7b1bb1c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php @@ -0,0 +1,133 @@ +. + */ + + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console\Command\Command, + Symfony\Component\Console\Input\InputInterface, + Symfony\Component\Console\Output\OutputInterface; +use Doctrine\DBAL\Platforms\Keywords\ReservedKeywordsValidator; + +class ReservedWordsCommand extends Command +{ + private $keywordListClasses = array( + 'mysql' => 'Doctrine\DBAL\Platforms\Keywords\MySQLKeywords', + 'mssql' => 'Doctrine\DBAL\Platforms\Keywords\MsSQLKeywords', + 'sqlite' => 'Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords', + 'pgsql' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords', + 'oracle' => 'Doctrine\DBAL\Platforms\Keywords\OracleKeywords', + 'db2' => 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords', + ); + + /** + * If you want to add or replace a keywords list use this command + * + * @param string $name + * @param string $class + */ + public function setKeywordListClass($name, $class) + { + $this->keywordListClasses[$name] = $class; + } + + /** + * @see Console\Command\Command + */ + 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 + * pgsql + * sqlite + * oracle + * mssql + * db2 (Not checked by default) +EOT + ); + } + + /** + * @see Console\Command\Command + */ + 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', 'pgsql', 'sqlite', 'oracle', 'mssql'); + } + + $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); + } + } + } +} 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..b1af34b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php @@ -0,0 +1,87 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console; + +/** + * 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 Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + 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/Helper/ConnectionHelper.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Helper/ConnectionHelper.php new file mode 100644 index 0000000..877cb64 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Helper/ConnectionHelper.php @@ -0,0 +1,74 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Helper; + +use Symfony\Component\Console\Helper\Helper, + Doctrine\DBAL\Connection; + +/** + * Doctrine CLI Connection Helper. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConnectionHelper extends Helper +{ + /** + * Doctrine Database Connection + * @var Connection + */ + protected $_connection; + + /** + * Constructor + * + * @param Connection $connection Doctrine Database Connection + */ + public function __construct(Connection $connection) + { + $this->_connection = $connection; + } + + /** + * Retrieves Doctrine Database Connection + * + * @return Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @see Helper + */ + 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..447f9ee --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php @@ -0,0 +1,64 @@ +. + */ + +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 +{ + public function getSQLDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + return serialize($value); + } + + public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\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; + } + + public function getName() + { + return Type::TARRAY; + } + + 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..7648bef --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php @@ -0,0 +1,56 @@ +. + */ + +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 +{ + public function getName() + { + return Type::BIGINT; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBigIntTypeDeclarationSQL($fieldDeclaration); + } + + 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/BlobType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php new file mode 100644 index 0000000..ff04655 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php @@ -0,0 +1,71 @@ +. + */ + +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 +{ + /** @override */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBlobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + if (is_string($value)) { + $value = fopen('data://text/plain;base64,' . base64_encode($value), 'r'); + } + + if ( ! is_resource($value)) { + throw ConversionException::conversionFailed($value, self::BLOB); + } + + return $value; + } + + public function getName() + { + return Type::BLOB; + } + + 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..f1a968d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php @@ -0,0 +1,57 @@ +. + */ + +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 +{ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBooleanTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $platform->convertBooleans($value); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (bool) $value; + } + + public function getName() + { + return Type::BOOLEAN; + } + + 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..3a19d1a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php @@ -0,0 +1,65 @@ +. + */ + + +/** + * Conversion Exception is thrown when the database to PHP conversion fails + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @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 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 + * @return 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..06de729 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php @@ -0,0 +1,59 @@ +. + */ + +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 +{ + public function getName() + { + return Type::DATETIME; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateTimeFormatString()) : null; + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat($platform->getDateTimeFormatString(), $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..e0a786c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php @@ -0,0 +1,79 @@ +. + */ + + +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. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DateTimeTzType extends Type +{ + public function getName() + { + return Type::DATETIMETZ; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTzTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateTimeTzFormatString()) : null; + } + + 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..a3f7018 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php @@ -0,0 +1,59 @@ +. + */ + +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 +{ + public function getName() + { + return Type::DATE; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateFormatString()) : null; + } + + 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..fd61b53 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DECIMAL to a PHP double. + * + * @since 2.0 + */ +class DecimalType extends Type +{ + public function getName() + { + return Type::DECIMAL; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDecimalTypeDeclarationSQL($fieldDeclaration); + } + + 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..d02ca0c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php @@ -0,0 +1,54 @@ +. + */ + + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class FloatType extends Type +{ + public function getName() + { + return Type::FLOAT; + } + + /** + * @param array $fieldDeclaration + * @param AbstractPlatform $platform + * @return string + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getFloatDeclarationSQL($fieldDeclaration); + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + 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..a8251dd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Represents a GUID/UUID datatype (both are actually synomys) in the database. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class GuidType extends StringType +{ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getGuidTypeDeclarationSQL($fieldDeclaration); + } + + public function getName() + { + return Type::GUID; + } +} + 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..bac9b3f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php @@ -0,0 +1,53 @@ +. + */ + +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 +{ + public function getName() + { + return Type::INTEGER; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getIntegerTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + 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..ca00557 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php @@ -0,0 +1,66 @@ +. + */ + +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 +{ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + return json_encode($value); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return array(); + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + + return json_decode($value, true); + } + + public function getName() + { + return Type::JSON_ARRAY; + } + + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} 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..9510d29 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php @@ -0,0 +1,64 @@ +. + */ + +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 +{ + public function getSQLDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + return serialize($value); + } + + public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\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; + } + + public function getName() + { + return Type::OBJECT; + } + + 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..719d7f1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php @@ -0,0 +1,69 @@ +. + */ + +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 +{ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (!$value) { + return null; + } + + return implode(',', $value); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return array(); + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + + return explode(',', $value); + } + + public function getName() + { + return Type::SIMPLE_ARRAY; + } + + 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..97e9aaf --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php @@ -0,0 +1,52 @@ +. + */ + +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 +{ + public function getName() + { + return Type::SMALLINT; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + 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..48c76d6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php @@ -0,0 +1,50 @@ +. + */ + +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 +{ + /** @override */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + } + + /** @override */ + public function getDefaultLength(AbstractPlatform $platform) + { + return $platform->getVarcharDefaultLength(); + } + + /** @override */ + 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..98ecbe6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php @@ -0,0 +1,56 @@ +. + */ + +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 +{ + /** @override */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (is_resource($value)) ? stream_get_contents($value) : $value; + } + + 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..8653750 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php @@ -0,0 +1,68 @@ +. + */ + +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 +{ + 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..29947be --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php @@ -0,0 +1,306 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + 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 BLOB = 'blob'; + const FLOAT = 'float'; + const GUID = 'guid'; + + /** Map of already instantiated type objects. One instance per type (flyweight). */ + private static $_typeObjects = array(); + + /** The map of supported doctrine mapping types. */ + 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::BLOB => 'Doctrine\DBAL\Types\BlobType', + self::GUID => 'Doctrine\DBAL\Types\GuidType', + ); + + /* Prevent instantiation and force 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 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 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. + * + * @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 AbstractPlatform $platform The currently used database platform. + */ + 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. + * + * @static + * @throws DBALException + * @param string $name The name of the type (as returned by getName()). + * @return \Doctrine\DBAL\Types\Type + */ + 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. + * + * @static + * @param string $name Name of the type. This should correspond to what getName() returns. + * @param string $className The class name of the custom type. + * @throws 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. + * + * @static + * @param string $name 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. + * + * @static + * @param string $name + * @param string $className + * @throws 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; + } + + /** + * Get the types array map which holds all registered types and the corresponding + * type class + * + * @return array $typesMap + */ + public static function getTypesMap() + { + return self::$_typesMap; + } + + 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 bool + */ + public function canRequireSQLConversion() + { + return false; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a database value. + * + * @param string $sqlExpr + * @param 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 AbstractPlatform $platform + * @return string + */ + public function convertToPHPValueSQL($sqlExpr, $platform) + { + return $sqlExpr; + } + + /** + * Get an array of database types that map to this Doctrine type. + * + * @param 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 AbstractPlatform $platform + * @return bool + */ + 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..7085822 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php @@ -0,0 +1,60 @@ +. + */ + + +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. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class VarDateTimeType extends DateTimeType +{ + /** + * @throws ConversionException + * @param string $value + * @param AbstractPlatform $platform + * @return \DateTime + */ + 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..3637631 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * 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.3.4'; + + /** + * 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/doctrine/dbal/phpunit.xml.dist b/vendor/doctrine/dbal/phpunit.xml.dist new file mode 100644 index 0000000..fe1d515 --- /dev/null +++ b/vendor/doctrine/dbal/phpunit.xml.dist @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + ./tests/Doctrine/Tests/DBAL + + + \ No newline at end of file diff --git a/vendor/doctrine/dbal/run-all.sh b/vendor/doctrine/dbal/run-all.sh new file mode 100644 index 0000000..80712ee --- /dev/null +++ b/vendor/doctrine/dbal/run-all.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# This script is a small convenience wrapper for running the doctrine testsuite against a large bunch of databases. +# Just create the phpunit.xmls as described in the array below and configure the specific files section +# to connect to that database. Just omit a file if you dont have that database and the tests will be skipped. + +configs[1]="mysql.phpunit.xml" +configs[2]='postgres.phpunit.xml' +configs[3]='sqlite.phpunit.xml' +configs[4]='oracle.phpunit.xml' +configs[5]='db2.phpunit.xml' +configs[6]='pdo-ibm.phpunit.xml' +configs[7]='sqlsrv.phpunit.xml' + +for i in "${configs[@]}"; do + if [ -f "$i" ]; + then + echo "RUNNING TESTS WITH CONFIG $i" + phpunit -c "$i" "$@" + fi; +done diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Changelog.md b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Changelog.md new file mode 100644 index 0000000..c015ad8 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Changelog.md @@ -0,0 +1,51 @@ +## 1.2.0 (2013-XX-XX) + + * 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/Doctrine/Bundle/DoctrineBundle/Command/CreateDatabaseDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/CreateDatabaseDoctrineCommand.php new file mode 100644 index 0000000..7e8e20e --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/CreateDatabaseDoctrineCommand.php @@ -0,0 +1,86 @@ + + * (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 databases') + ->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command') + ->setHelp(<<doctrine:database:create command creates the default +connections database: + +php app/console doctrine:database:create + +You can also optionally specify the name of a connection to create the +database for: + +php app/console doctrine:database:create --connection=default +EOT + ); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $connection = $this->getDoctrineConnection($input->getOption('connection')); + + $params = $connection->getParams(); + $name = isset($params['path']) ? $params['path'] : $params['dbname']; + + unset($params['dbname']); + + $tmpConnection = DriverManager::getConnection($params); + + // Only quote if we don't have a path + if (!isset($params['path'])) { + $name = $tmpConnection->getDatabasePlatform()->quoteSingleIdentifier($name); + } + + $error = false; + try { + $tmpConnection->getSchemaManager()->createDatabase($name); + $output->writeln(sprintf('Created database for connection named %s', $name)); + } catch (\Exception $e) { + $output->writeln(sprintf('Could not create database for connection named %s', $name)); + $output->writeln(sprintf('%s', $e->getMessage())); + $error = true; + } + + $tmpConnection->close(); + + return $error ? 1 : 0; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/DoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/DoctrineCommand.php new file mode 100644 index 0000000..208cfaf --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/DoctrineCommand.php @@ -0,0 +1,66 @@ + + * (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/Doctrine/Bundle/DoctrineBundle/Command/DropDatabaseDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/DropDatabaseDoctrineCommand.php new file mode 100644 index 0000000..8feeabf --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/DropDatabaseDoctrineCommand.php @@ -0,0 +1,102 @@ + + * (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; + +/** + * 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 databases') + ->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command') + ->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action') + ->setHelp(<<doctrine:database:drop command drops the default connections +database: + +php app/console doctrine:database:drop + +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 app/console doctrine:database:drop --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')); + + $params = $connection->getParams(); + + $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."); + } + + if ($input->getOption('force')) { + // Only quote if we don't have a path + if (!isset($params['path'])) { + $name = $connection->getDatabasePlatform()->quoteSingleIdentifier($name); + } + + try { + $connection->getSchemaManager()->dropDatabase($name); + $output->writeln(sprintf('Dropped database for connection named %s', $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/Doctrine/Bundle/DoctrineBundle/Command/GenerateEntitiesDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/GenerateEntitiesDoctrineCommand.php new file mode 100644 index 0000000..911f262 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/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(<<doctrine:generate:entities command generates entity classes +and method stubs from your mapping information: + +You have to limit generation of entities: + +* To a bundle: + + php app/console doctrine:generate:entities MyCustomBundle + +* To a single entity: + + php app/console doctrine:generate:entities MyCustomBundle:User + php app/console doctrine:generate:entities MyCustomBundle/Entity/User + +* To a namespace + + php app/console doctrine:generate:entities 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 app/console doctrine:generate:entities 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 app/console doctrine:generate:entities 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')->getEntityNamespace(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/Doctrine/Bundle/DoctrineBundle/Command/ImportMappingDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/ImportMappingDoctrineCommand.php new file mode 100644 index 0000000..b54203e --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/ImportMappingDoctrineCommand.php @@ -0,0 +1,132 @@ + + * (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(<<doctrine:mapping:import command imports mapping information +from an existing database: + +php app/console doctrine:mapping:import "MyCustomBundle" xml + +You can also optionally specify which entity manager to import from with the +--em option: + +php app/console doctrine:mapping:import "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 app/console doctrine:mapping:import "MyCustomBundle" xml --filter=MyMatchedEntity + +Use the --force option, if you want to override existing mapping files: + +php app/console doctrine:mapping:import "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.'/'.$className.'.php'; + } else { + $path = $destPath.'/'.$className.'.orm.'.$type; + } + $output->writeln(sprintf(' > writing %s', $path)); + $code = $exporter->exportClassMetadata($class); + if (!is_dir($dir = dirname($path))) { + mkdir($dir, 0777, true); + } + file_put_contents($path, $code); + } + } else { + $output->writeln('Database does not have any mapping information.', 'ERROR'); + $output->writeln('', 'ERROR'); + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearMetadataCacheDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearMetadataCacheDoctrineCommand.php new file mode 100644 index 0000000..f415c8e --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearMetadataCacheDoctrineCommand.php @@ -0,0 +1,64 @@ + + * (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') + ->setHelp(<<doctrine:cache:clear-metadata command clears all metadata +cache for the default entity manager: + +php app/console doctrine:cache:clear-metadata + +You can also optionally specify the --em option to specify +which entity manager to clear the cache for: + +php app/console doctrine:cache:clear-metadata --em=default +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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearQueryCacheDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearQueryCacheDoctrineCommand.php new file mode 100644 index 0000000..0097069 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearQueryCacheDoctrineCommand.php @@ -0,0 +1,64 @@ + + * (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') + ->setHelp(<<doctrine:cache:clear-query command clears all query cache for +the default entity manager: + +php app/console doctrine:cache:clear-query + +You can also optionally specify the --em option to specify +which entity manager to clear the cache for: + +php app/console doctrine:cache:clear-query --em=default +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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearResultCacheDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearResultCacheDoctrineCommand.php new file mode 100644 index 0000000..4dabe78 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearResultCacheDoctrineCommand.php @@ -0,0 +1,64 @@ + + * (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') + ->setHelp(<<doctrine:cache:clear-result command clears all result cache +for the default entity manager: + +php app/console doctrine:cache:clear-result + +You can also optionally specify the --em option to specify +which entity manager to clear the cache for: + +php app/console doctrine:cache:clear-result --em=default +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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ConvertMappingDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ConvertMappingDoctrineCommand.php new file mode 100644 index 0000000..e92c853 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ConvertMappingDoctrineCommand.php @@ -0,0 +1,80 @@ + + * (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 Symfony\Component\Console\Output\Output; +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') + ->setHelp(<<doctrine:mapping:convert command converts mapping information +between supported formats: + +php app/console doctrine:mapping:convert xml /path/to/output +EOT + ); + } + + /** + * {@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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/CreateSchemaDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/CreateSchemaDoctrineCommand.php new file mode 100644 index 0000000..6bc704b --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/CreateSchemaDoctrineCommand.php @@ -0,0 +1,69 @@ + + * (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 Symfony\Component\Console\Output\Output; +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') + ->setHelp(<<doctrine:schema:create command executes the SQL needed to +generate the database schema for the default entity manager: + +php app/console doctrine:schema:create + +You can also generate the database schema for a specific entity manager: + +php app/console doctrine:schema:create --em=default + +Finally, instead of executing the SQL, you can output the SQL: + +php app/console doctrine:schema:create --dump-sql +EOT + ); + } + + /** + * {@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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/DoctrineCommandHelper.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/DoctrineCommandHelper.php new file mode 100644 index 0000000..11b7589 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/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 + */ + static public 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 + */ + static public 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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/DropSchemaDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/DropSchemaDoctrineCommand.php new file mode 100644 index 0000000..4391f6d --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/DropSchemaDoctrineCommand.php @@ -0,0 +1,69 @@ + + * (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 Symfony\Component\Console\Output\Output; +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') + ->setHelp(<<doctrine:schema:drop command generates the SQL needed to +drop the database schema of the default entity manager: + +php app/console doctrine:schema:drop --dump-sql + +Alternatively, you can execute the generated queries: + +php app/console doctrine:schema:drop --force + +You can also optionally specify the name of a entity manager to drop the +schema for: + +php app/console doctrine:schema:drop --em=default +EOT + ); + } + + /** + * {@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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/EnsureProductionSettingsDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/EnsureProductionSettingsDoctrineCommand.php new file mode 100644 index 0000000..8012a9e --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/EnsureProductionSettingsDoctrineCommand.php @@ -0,0 +1,64 @@ + + * (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 Symfony\Component\Console\Output\Output; +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') + ->setHelp(<<doctrine:ensure-production-settings command ensures that +Doctrine is properly configured for a production environment.: + +php app/console doctrine:ensure-production-settings + +You can also optionally specify the --em option to specify +which entity manager to use: + +php app/console doctrine:ensure-production-settings --em=default +EOT + ); + } + + /** + * {@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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/InfoDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/InfoDoctrineCommand.php new file mode 100644 index 0000000..1fffd35 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/InfoDoctrineCommand.php @@ -0,0 +1,63 @@ + + * (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\Mapping\MappingException; +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') + ->setDescription('Shows basic information about all mapped entities') + ->setHelp(<<doctrine:mapping:info shows basic information about which +entities exist and possibly if their mapping information contains errors or +not. + +php app/console doctrine:mapping:info + +If you are using multiple entity managers you can pick your choice with the +--em option: + +php app/console doctrine:mapping:info --em=default +EOT + ); + } + + /** + * {@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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunDqlDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunDqlDoctrineCommand.php new file mode 100644 index 0000000..440ca00 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunDqlDoctrineCommand.php @@ -0,0 +1,69 @@ + + * (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 Symfony\Component\Console\Output\Output; +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(<<doctrine:query:dql command executes the given DQL query and +outputs the results: + +php app/console doctrine:query:dql "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 app/console doctrine:query:dql "SELECT u FROM UserBundle:User u" --hydrate=array + +Additionally you can specify the first result and maximum amount of results to +show: + +php app/console doctrine:query:dql "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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunSqlDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunSqlDoctrineCommand.php new file mode 100644 index 0000000..bca222a --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunSqlDoctrineCommand.php @@ -0,0 +1,59 @@ + + * (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 Symfony\Component\Console\Output\Output; +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(<<doctrine:query:sql command executes the given SQL query and +outputs the results: + +php app/console doctrine:query:sql "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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/UpdateSchemaDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/UpdateSchemaDoctrineCommand.php new file mode 100644 index 0000000..6dc1b82 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/UpdateSchemaDoctrineCommand.php @@ -0,0 +1,73 @@ + + * (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 Symfony\Component\Console\Output\Output; +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') + ->setDescription('Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:schema:update 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: + +php app/console doctrine:schema:update --dump-sql + +Alternatively, you can execute the generated queries: + +php app/console doctrine:schema:update --force + +You can also update the database schema for a specific entity manager: + +php app/console doctrine:schema:update --em=default +EOT + ); + } + + /** + * {@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/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ValidateSchemaCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ValidateSchemaCommand.php new file mode 100644 index 0000000..0f51b46 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ValidateSchemaCommand.php @@ -0,0 +1,65 @@ + + * (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') + ->setDescription('Validates the doctrine mapping files') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:schema:validate checks the current mappings +for valid forward and reverse mappings. + +php app/console doctrine:schema:validate + +You can also optionally specify the --em option to specify +which entity manager use for the validation. + +php app/console doctrine:schema:validate --em=default +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/Doctrine/Bundle/DoctrineBundle/ConnectionFactory.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/ConnectionFactory.php new file mode 100644 index 0000000..5377002 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/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/Doctrine/Bundle/DoctrineBundle/Controller/ProfilerController.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Controller/ProfilerController.php new file mode 100644 index 0000000..3c34b21 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Controller/ProfilerController.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\Controller; + +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 { + $results = $connection->executeQuery('EXPLAIN '.$query['sql'], $query['params'], $query['types']) + ->fetchAll(\PDO::FETCH_ASSOC); + } catch (\Exception $e) { + return new Response('This query cannot be explained.'); + } + + return $this->container->get('templating')->renderResponse('DoctrineBundle:Collector:explain.html.twig', array( + 'data' => $results, + 'query' => $query, + )); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DataCollector/DoctrineDataCollector.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DataCollector/DoctrineDataCollector.php new file mode 100644 index 0000000..7a39cfb --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DataCollector/DoctrineDataCollector.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\DataCollector; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\Tools\SchemaValidator; +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(); + + 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) { + $entities[$name][] = $class->getName(); + $classErrors = $validator->validateClass($class); + + if (!empty($classErrors)) { + $errors[$name][$class->getName()] = $classErrors; + } + } + } + + $this->data['entities'] = $entities; + $this->data['errors'] = $errors; + } + + public function getEntities() + { + return $this->data['entities']; + } + + public function getMappingErrors() + { + return $this->data['errors']; + } + + 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/Doctrine/Bundle/DoctrineBundle/DependencyInjection/Configuration.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..d1c82ac --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/Configuration.php @@ -0,0 +1,428 @@ + + * (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') + ->children() + ->scalarNode('driver')->defaultValue('pdo_mysql')->end() + ->scalarNode('platform_service')->end() + ->scalarNode('schema_filter')->end() + ->booleanNode('logging')->defaultValue($this->debug)->end() + ->booleanNode('profiling')->defaultValue($this->debug)->end() + ->scalarNode('driver_class')->end() + ->scalarNode('wrapper_class')->end() + ->booleanNode('keep_slave')->end() + ->arrayNode('options') + ->useAttributeAsKey('key') + ->prototype('scalar')->end() + ->end() + ->arrayNode('mapping_types') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->end() + ; + + $slaveNode = $connectionNode + ->children() + ->arrayNode('slaves') + ->useAttributeAsKey('name') + ->prototype('array') + ; + $this->configureDbalDriverNode($slaveNode); + + 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('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 dbname as service name instead of SID for Oracle')->end() + ->scalarNode('sessionMode') + ->info('The session mode to use for the oci8 driver') + ->end() + ->booleanNode('pooled')->info('True to use a pooled server with the oci8 driver')->end() + ->booleanNode('MultipleActiveResultSets')->info('Configuring MultipleActiveResultSets for the pdo_sqlsrv driver')->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) + { + $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() + ->booleanNode('auto_generate_proxy_classes')->defaultFalse()->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 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')) + ->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() + ->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() + ->end() + ; + + return $node; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php new file mode 100644 index 0000000..55bdbdb --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php @@ -0,0 +1,471 @@ + + * (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\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; + +/** + * DoctrineExtension is an extension for the Doctrine DBAL and ORM library. + * + * @author Jonathan H. Wage + * @author Fabien Potencier + * @author Benjamin Eberlei + */ +class DoctrineExtension extends AbstractDoctrineExtension +{ + private $defaultConnection; + private $entityManagers; + + /** + * {@inheritDoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + if (!empty($config['dbal'])) { + $this->dbalLoad($config['dbal'], $container); + } + + if (!empty($config['orm'])) { + $this->ormLoad($config['orm'], $container); + } + } + + /** + * 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); + + 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')); + $logger = new Reference($profilingLoggerId); + $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', array($name, $logger)); + + if (null !== $logger) { + $chainLogger = new DefinitionDecorator('doctrine.dbal.logger.chain'); + $chainLogger->addMethodCall('addLogger', array($logger)); + + $loggerId = 'doctrine.dbal.logger.chain.'.$name; + $container->setDefinition($loggerId, $chainLogger); + $logger = new Reference($loggerId); + } + } + unset($connection['profiling']); + + 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 + $def = $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); + + $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'], + )) + ; + } + + 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']); + + foreach (array( + 'options' => 'driverOptions', + 'driver_class' => 'driverClass', + 'wrapper_class' => 'wrapperClass', + 'keep_slave' => 'keepSlave', + ) as $old => $new) { + if (isset($options[$old])) { + $options[$new] = $options[$old]; + unset($options[$old]); + } + } + + if (!empty($options['slaves'])) { + $nonRewrittenKeys = array( + 'driver' => true, 'driverOptions' => true, 'driverClass' => true, + 'wrapperClass' => true, 'keepSlave' => true, + 'platform' => true, 'slaves' => true, 'master' => 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']); + } + + 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'])); + + 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() + )); + } + + $def->addTag('doctrine.event_listener', array('event' => 'loadClassMetadata')); + } + } + + /** + * 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) + { + if ($entityManager['auto_mapping'] && count($this->entityManagers) > 1) { + throw new \LogicException('You cannot enable "auto_mapping" when several entity managers are defined.'); + } + + $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new DefinitionDecorator('doctrine.orm.configuration')); + + $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container); + $this->loadOrmCacheDrivers($entityManager, $container); + + $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(\Doctrine\ORM\Version::VERSION, "2.3.0-DEV") >= 0) { + $methods = array_merge($methods, array( + 'setNamingStrategy' => new Reference($entityManager['naming_strategy']), + )); + } + 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']); + $managerConfiguratorDef = $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) + ); + } + + /** + * 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-mappings1/, bundle-mappings2/] + * alias: BundleAlias + * arbitrary_key: + * type: xml + * dir: %kernel.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)); + } + + /** + * {@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'; + } + + /** + * 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->loadObjectManagerCacheDriver($entityManager, $container, 'metadata_cache'); + $this->loadObjectManagerCacheDriver($entityManager, $container, 'result_cache'); + $this->loadObjectManagerCacheDriver($entityManager, $container, 'query_cache'); + } + + /** + * {@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/Doctrine/Bundle/DoctrineBundle/DoctrineBundle.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DoctrineBundle.php new file mode 100644 index 0000000..b23207d --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DoctrineBundle.php @@ -0,0 +1,134 @@ + + * (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 Symfony\Component\Console\Application; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ContainerBuilder; +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')); + } + + /** + * {@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'); + // See https://github.com/symfony/symfony/pull/3419 for usage of + // references + $container =& $this->container; + + $this->autoloader = function($class) use ($namespace, $dir, &$container) { + if (0 === strpos($class, $namespace)) { + $fileName = str_replace('\\', '', substr($class, strlen($namespace) +1)); + $file = $dir.DIRECTORY_SEPARATOR.$fileName.'.php'; + + if (!is_file($file) && $container->getParameter('doctrine.orm.auto_generate_proxy_classes')) { + $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()) { + $classes = $em->getMetadataFactory()->getAllMetadata(); + + foreach ($classes as $classMetadata) { + if ($classMetadata->name == $originalClassName) { + $em->getProxyFactory()->generateProxyClasses(array($classMetadata)); + } + } + } + } + + clearstatcache(true, $file); + } + + if (file_exists($file)) { + require $file; + } + } + }; + spl_autoload_register($this->autoloader); + } + } + + /** + * {@inheritDoc} + */ + public function shutdown() + { + if (null !== $this->autoloader) { + spl_autoload_unregister($this->autoloader); + $this->autoloader = null; + } + } + + /** + * {@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/Doctrine/Bundle/DoctrineBundle/LICENSE b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/LICENSE new file mode 100644 index 0000000..655a5ce --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/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/Doctrine/Bundle/DoctrineBundle/ManagerConfigurator.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/ManagerConfigurator.php new file mode 100644 index 0000000..851be3e --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/ManagerConfigurator.php @@ -0,0 +1,90 @@ + + * (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(); + + /** + * Construct. + * + * @param array $enabledFilters + */ + 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); + } + + /** + * Enable filters for an given entity manager + * + * @param EntityManager $entityManager + * + * @return null + */ + 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); + } + } + } + + /** + * Set defaults parameters for a given filter + * + * @param string $name Filter name + * @param SQLFilter $filter Filter object + * + * @return null + */ + 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/Doctrine/Bundle/DoctrineBundle/Mapping/ClassMetadataCollection.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/ClassMetadataCollection.php new file mode 100644 index 0000000..1f5d6c5 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/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/Doctrine/Bundle/DoctrineBundle/Mapping/DisconnectedMetadataFactory.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/DisconnectedMetadataFactory.php new file mode 100644 index 0000000..274ae94 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/DisconnectedMetadataFactory.php @@ -0,0 +1,29 @@ + + * (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 DisconnectedMetadataFactory extends MetadataFactory +{ + /** + * @return string + */ + protected function getClassMetadataFactoryClass() + { + return 'Doctrine\\ORM\\Tools\\DisconnectedClassMetadataFactory'; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/MetadataFactory.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/MetadataFactory.php new file mode 100644 index 0000000..3e9105a --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/MetadataFactory.php @@ -0,0 +1,211 @@ + + * (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 Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Doctrine\ORM\Mapping\MappingException; + +/** + * This class provides methods to access Doctrine entity class metadata for a + * given bundle, namespace or entity class. + * + * @author Fabien Potencier + */ +class MetadataFactory +{ + 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); + + 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())); + } elseif (!$path) { + 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(isset($r) ? $r->getNamespaceName() : $all[0]->name); + } + + /** + * 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->getAllMetadata() as $metadata) { + if ($metadata->name === $entity) { + return new ClassMetadataCollection(array($metadata)); + } + } + + return new ClassMetadataCollection(array()); + } + + /** + * @return array + */ + private function getAllMetadata() + { + $metadata = array(); + foreach ($this->registry->getManagers() as $em) { + $class = $this->getClassMetadataFactoryClass(); + /** @var $cmf \Doctrine\ORM\Mapping\ClassMetadataFactory */ + $cmf = new $class(); + $cmf->setEntityManager($em); + foreach ($cmf->getAllMetadata() as $m) { + $metadata[] = $m; + } + } + + return $metadata; + } + + /** + * @return string + */ + protected function getClassMetadataFactoryClass() + { + return 'Doctrine\\ORM\\Mapping\\ClassMetadataFactory'; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/README.md b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/README.md new file mode 100644 index 0000000..31391c5 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/README.md @@ -0,0 +1,56 @@ +# Doctrine Bundle + +Doctrine DBAL & ORM Bundle for the Symfony Framework. + +Because Symfony 2 does not want to force or suggest a specific persistence solutions on the users +this bundle was removed from the core of the Symfony 2 framework. Doctrine2 will still be a major player +in the Symfony world and the bundle is maintained by developers in the Doctrine and Symfony communities. + + IMPORTANT: This bundle is developed for Symfony 2.1 and up. For Symfony 2.0 applications the DoctrineBundle + is still shipped with the core Symfony repository. + +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. + +## Installation + +### 1. Old deps and bin/vendors way + +Add the following snippets to "deps" files: + + [doctrine-dbal] + git=http://github.com/doctrine/dbal.git + + [doctrine-orm] + git=http://github.com/doctrine/doctrine2.git + + [DoctrineBundle] + git=http://github.com/doctrine/DoctrineBundle.git + target=/bundles/Doctrine/Bundle/DoctrineBundle + +### 2. Composer + +Add the following dependencies to your projects composer.json file: + + "require": { + # .. + "doctrine/doctrine-bundle": ">=2.1" + # .. + } + +## Documentation + +See the Resources/docs folder more a full documentation. diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Registry.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Registry.php new file mode 100644 index 0000000..63ec59e --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/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/Doctrine/Bundle/DoctrineBundle/Resources/config/dbal.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/dbal.xml new file mode 100644 index 0000000..6018109 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/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/Doctrine/Bundle/DoctrineBundle/Resources/config/orm.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/orm.xml new file mode 100644 index 0000000..e7df804 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/orm.xml @@ -0,0 +1,112 @@ + + + + + + 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\ORM\Mapping\Driver\DriverChain + 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\Mapping\DefaultNamingStrategy + Doctrine\ORM\Mapping\UnderscoreNamingStrategy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/schema/doctrine-1.0.xsd b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/schema/doctrine-1.0.xsd new file mode 100644 index 0000000..3b11cd5 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/schema/doctrine-1.0.xsd @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/db.html.twig b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/db.html.twig new file mode 100644 index 0000000..7982c60 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/db.html.twig @@ -0,0 +1,256 @@ +{% extends app.request.isXmlHttpRequest ? 'WebProfilerBundle:Profiler:ajax_layout.html.twig' : 'WebProfilerBundle:Profiler:layout.html.twig' %} + +{% block toolbar %} + {% set icon %} + Database + {{ collector.querycount }} + {% if collector.querycount > 0 %} + in {{ '%0.2f'|format(collector.time * 1000) }} ms + {% endif %} + {% if collector.invalidEntityCount > 0 %} + + {% endif %} + {% endset %} + {% set text %} +
+ DB Queries + {{ collector.querycount }} +
+
+ Query time + {{ '%0.2f'|format(collector.time * 1000) }} ms +
+
+ Invalid entities + {{ collector.invalidEntityCount }} +
+ {% endset %} + {% include 'WebProfilerBundle:Profiler:toolbar_item.html.twig' with { 'link': profiler_url } %} +{% endblock %} + +{% block menu %} + + + Doctrine + + {{ collector.querycount }} + {{ '%0.0f'|format(collector.time * 1000) }} ms + + +{% endblock %} + +{% block panel %} + {% 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 %} +

Queries

+ + {% for connection, queries in collector.queries %} +

Connection {{ connection }}

+ {% if queries is empty %} +

+ No queries. +

+ {% else %} +
    + {% for i, query in queries %} +
  • +
    +
    + + + - + Shrink query + + {{ query.sql|doctrine_minify_query|raw }} + +
    + + {{ query.sql|doctrine_pretty_query(i, loop.parent.loop.index)|raw }} + + + + Parameters: {{ query.params|yaml_encode }}
    + [Display runnable query]
    + Time: {{ '%0.2f'|format(query.executionMS * 1000) }} ms +
    + {% if query.explainable %} + [ + + + - + Explain query + ] + {% else %} + This query cannot be explained + {% endif %} +
    + {% if query.explainable %} +
    + {% endif %} +
  • + {% endfor %} +
+ {% endif %} + {% endfor %} + +

Database Connections

+ + {% if collector.connections %} + {% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.connections} only %} + {% else %} +

+ No connections. +

+ {% endif %} + +

Entity Managers

+ + {% if collector.managers %} + {% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.managers} only %} + {% else %} +

+ No entity managers. +

+ {% endif %} + +

Mapping

+ + {% for manager, classes in collector.entities %} +

Manager {{ manager }}

+ {% if classes is empty %} +

No loaded entities.

+ {% else %} + + + + + + + + + {% for class in classes %} + + + + + {% endfor %} + +
ClassMapping errors
{{ class }} + {% if collector.mappingErrors[manager][class] is defined %} +
    + {% for error in collector.mappingErrors[manager][class] %} +
  • {{ error }}
  • + {% endfor %} +
+ {% else %} + Valid + {% endif %} +
+ {% endif %} + {% endfor %} + + + + +{% endblock %} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/explain.html.twig b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/explain.html.twig new file mode 100644 index 0000000..c25385b --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/explain.html.twig @@ -0,0 +1,20 @@ +Explanation: + + + + + {% for label in data[0]|keys %} + + {% endfor %} + + + + {% for row in data %} + + {% for item in row %} + + {% endfor %} + + {% endfor %} + +
{{ label }}
{{ item }}
\ No newline at end of file diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Twig/DoctrineExtension.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Twig/DoctrineExtension.php new file mode 100644 index 0000000..c739e50 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Twig/DoctrineExtension.php @@ -0,0 +1,322 @@ + + * (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; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * 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( + 'doctrine_minify_query' => new \Twig_Filter_Method($this, 'minifyQuery'), + 'doctrine_pretty_query' => new \Twig_Filter_Function('SqlFormatter::format'), + 'doctrine_replace_query_parameters' => new \Twig_Filter_Method($this, 'replaceQueryParameters'), + ); + } + + /** + * Get the possible combinations of elements from the given array + * + * @param array $elements + * @param integer $combinationsLevel + * + * @return array + */ + private function getPossibleCombinations($elements, $combinationsLevel) + { + $baseCount = count($elements); + $result = array(); + + if ($combinationsLevel == 1) { + 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($parameters, $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, $keywords = array(), $required = 1) + { + // 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); + } + + // Remove unneeded boilerplate HTML + $result = str_replace(array("
"), array(""), $result);
+
+        return $result;
+    }
+
+    /**
+     * Escape parameters of a SQL query
+     * DON'T USE THIS FUNCTION OUTSIDE ITS INTEDED SCOPE
+     *
+     * @internal
+     *
+     * @param mixed $parameter
+     *
+     * @return string
+     */
+    static public 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;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Return a query with the parameters replaced
+     *
+     * @param string $query
+     * @param array $parameters
+     *
+     * @return string
+     */
+    public function replaceQueryParameters($query, $parameters)
+    {
+        $i = 0;
+
+        $result = preg_replace_callback(
+            '/\?|(:[a-z0-9_]+)/i',
+            function ($matches) use ($parameters, &$i) {
+                $key = substr($matches[0], 1);
+                if (!isset($parameters[$i]) && !isset($parameters[$key])) {
+                    return $matches[0];
+                }
+
+                $value = isset($parameters[$i]) ? $parameters[$i] : $parameters[$key];
+                $result = DoctrineExtension::escapeFunction($value);
+                $i++;
+
+                return $result;
+            },
+            $query
+        );
+
+        $result = \SqlFormatter::highlight($result);
+        $result = str_replace(array("
"), array(""), $result);
+
+        return $result;
+    }
+
+    /**
+     * Get the name of the extension
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return 'doctrine_extension';
+    }
+
+}
diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/composer.json b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/composer.json
new file mode 100644
index 0000000..da575c2
--- /dev/null
+++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/composer.json
@@ -0,0 +1,48 @@
+{
+    "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"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.2",
+        "symfony/framework-bundle": "~2.2",
+        "symfony/doctrine-bridge": "~2.2",
+        "doctrine/dbal": ">=2.2,<2.5-dev",
+        "jdorn/sql-formatter": "~1.1"
+    },
+    "require-dev": {
+        "doctrine/orm": ">=2.2,<2.5-dev",
+        "symfony/yaml": "~2.2",
+        "symfony/validator": "~2.2"
+    },
+    "suggest": {
+        "symfony/web-profiler-bundle": "to use the data collector",
+        "doctrine/orm": "The Doctrine ORM integration is optional in the bundle."
+    },
+    "autoload": {
+        "psr-0": { "Doctrine\\Bundle\\DoctrineBundle": "" }
+    },
+    "target-dir": "Doctrine/Bundle/DoctrineBundle",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.2.x-dev"
+        }
+    }
+}
diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/phpunit.xml.dist b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/phpunit.xml.dist
new file mode 100644
index 0000000..d401031
--- /dev/null
+++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/phpunit.xml.dist
@@ -0,0 +1,35 @@
+
+
+
+    
+        
+            ./Tests
+        
+    
+
+    
+        
+            benchmark
+        
+    
+
+    
+        
+            .
+            
+                ./Resources
+                ./Tests
+            
+        
+    
+
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..54e9e8e
--- /dev/null
+++ b/vendor/doctrine/orm/README.markdown
@@ -0,0 +1,19 @@
+# Doctrine 2 ORM
+
+Master: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=master)](http://travis-ci.org/doctrine/doctrine2)
+2.2: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.2)](http://travis-ci.org/doctrine/doctrine2)
+2.1: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2)
+
+Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.2+ 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 Hibernates 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)
+
diff --git a/vendor/doctrine/orm/composer.json b/vendor/doctrine/orm/composer.json
new file mode 100644
index 0000000..b413385
--- /dev/null
+++ b/vendor/doctrine/orm/composer.json
@@ -0,0 +1,32 @@
+{
+    "name": "doctrine/orm",
+    "type": "library","version":"2.3.4",
+    "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"}
+    ],
+    "require": {
+        "php": ">=5.3.2",
+        "ext-pdo": "*",
+        "doctrine/dbal": "2.3.*",
+        "symfony/console": "2.*"
+    },
+    "suggest": {
+        "symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
+    },
+    "autoload": {
+        "psr-0": { "Doctrine\\ORM": "lib/" }
+    },
+    "bin": ["bin/doctrine", "bin/doctrine.php"],
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.3.x-dev"
+        }
+    }
+}
diff --git a/vendor/doctrine/orm/doctrine-mapping.xsd b/vendor/doctrine/orm/doctrine-mapping.xsd
new file mode 100644
index 0000000..3a27867
--- /dev/null
+++ b/vendor/doctrine/orm/doctrine-mapping.xsd
@@ -0,0 +1,520 @@
+
+
+
+
+   
+    
+  
+
+  
+    
+      
+        
+        
+        
+      
+      
+    
+  
+  
+  
+    
+      
+    
+    
+  
+  
+  
+    
+       
+       
+       
+       
+       
+      
+    
+    
+  
+  
+  
+    
+      
+      
+      
+      
+      
+      
+      
+    
+  
+  
+  
+    
+      
+    
+    
+    
+    
+  
+  
+  
+    
+      
+      
+    
+    
+  
+
+  
+    
+    
+    
+  
+
+  
+    
+      
+      
+    
+  
+
+  
+    
+    
+    
+    
+  
+
+  
+    
+      
+      
+    
+  
+
+  
+    
+  
+
+  
+    
+    
+  
+
+  
+    
+      
+    
+    
+  
+
+  
+    
+      
+      
+      
+    
+  
+
+  
+    
+      
+      
+    
+  
+
+  
+    
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+    
+    
+    
+    
+    
+    
+    
+    
+    
+  
+  
+  
+    
+      
+      
+    
+    
+    
+  
+
+  
+    
+      
+      
+    
+    
+  
+
+  
+    
+      
+        
+          
+        
+        
+      
+    
+  
+
+  
+    
+      
+      
+      
+    
+  
+  
+  
+    
+      
+      
+      
+    
+  
+  
+   
+     
+       
+       
+       
+       
+       
+      
+     
+  
+  
+   
+     
+       
+       
+      
+     
+  
+  
+   
+     
+       
+      
+      
+     
+  
+
+  
+    
+      
+      
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+  
+  
+  
+    
+      
+    
+    
+    
+    
+    
+    
+    
+  
+  
+  
+    
+      
+    
+    
+    
+    
+  
+  
+  
+    
+      
+      
+    
+    
+  
+  
+  
+    
+      
+    
+    
+    
+    
+  
+  
+  
+    
+      
+      
+    
+    
+  
+  
+  
+    
+      
+    
+    
+    
+    
+  
+  
+  
+    
+      
+      
+    
+    
+  
+
+  
+    
+      
+    
+    
+    
+  
+
+  
+    
+      
+      
+      
+      
+    
+    
+    
+    
+    
+    
+    
+    
+  
+
+  
+    
+      
+    
+      
+      
+      
+    
+  
+
+  
+    
+      
+    
+    
+  
+
+  
+    
+      
+      
+    
+    
+  
+
+  
+    
+      
+    
+    
+    
+    
+    
+    
+    
+    
+    
+  
+
+  
+    
+      
+      
+    
+    
+  
+
+  
+    
+      
+      
+      
+    
+    
+    
+    
+  
+
+  
+      
+          
+          
+      
+      
+  
+
+  
+    
+      
+    
+    
+    
+    
+  
+
+  
+    
+      
+      
+    
+  
+
+  
+    
+      
+      
+      
+      
+    
+    
+    
+    
+    
+    
+    
+    
+    
+  
+
+  
+    
+      
+      
+      
+    
+    
+    
+    
+    
+    
+    
+    
+  
+  
+  
+    
+      
+      
+        
+        
+        
+      
+      
+    
+    
+    
+    
+    
+    
+    
+  
+  
+  
+    
+      
+      
+        
+        
+        
+      
+      
+    
+    
+    
+    
+    
+    
+    
+    
+  
+
+  
+    
+      
+      
+    
+  
+
+  
+    
+      
+      
+      
+    
+    
+  
+
+  
+    
+      
+      
+    
+  
+
+  
+    
+      
+      
+    
+    
+  
+
+
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..ab10395
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php
@@ -0,0 +1,829 @@
+.
+ */
+
+namespace Doctrine\ORM;
+
+use Doctrine\Common\Util\ClassUtils;
+use Doctrine\Common\Collections\ArrayCollection;
+
+use Doctrine\DBAL\Types\Type;
+use Doctrine\DBAL\Cache\QueryCacheProfile;
+
+use Doctrine\ORM\Query\QueryException;
+use Doctrine\ORM\Mapping;
+
+/**
+ * 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;
+
+    /**
+     * @var \Doctrine\Common\Collections\ArrayCollection The parameter map of this query.
+     */
+    protected $parameters;
+
+    /**
+     * @var ResultSetMapping The user-specified ResultSetMapping to use.
+     */
+    protected $_resultSetMapping;
+
+    /**
+     * @var \Doctrine\ORM\EntityManager The entity manager used by this query object.
+     */
+    protected $_em;
+
+    /**
+     * @var array The map of query hints.
+     */
+    protected $_hints = array();
+
+    /**
+     * @var integer The hydration mode.
+     */
+    protected $_hydrationMode = self::HYDRATE_OBJECT;
+
+    /**
+     * @param \Doctrine\DBAL\Cache\QueryCacheProfile
+     */
+    protected $_queryCacheProfile;
+
+    /**
+     * @var boolean Boolean value that indicates whether or not expire the result cache.
+     */
+    protected $_expireResultCache = false;
+
+    /**
+     * @param \Doctrine\DBAL\Cache\QueryCacheProfile
+     */
+    protected $_hydrationCacheProfile;
+
+    /**
+     * Initializes a new instance of a class derived from AbstractQuery.
+     *
+     * @param \Doctrine\ORM\EntityManager $entityManager
+     */
+    public function __construct(EntityManager $em)
+    {
+        $this->_em = $em;
+        $this->parameters = new ArrayCollection();
+    }
+
+    /**
+     * 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 = array();
+    }
+
+    /**
+     * 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 mixed 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 a collection of query parameters.
+     *
+     * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters
+     *
+     * @return \Doctrine\ORM\AbstractQuery This query 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;
+    }
+
+    /**
+     * Sets a query parameter.
+     *
+     * @param string|integer $key The parameter position or name.
+     * @param mixed $value The parameter value.
+     * @param string $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 \Doctrine\ORM\AbstractQuery 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;
+        }
+
+        $parameter = new Query\Parameter($key, $value, $type);
+
+        $this->parameters->add($parameter);
+
+        return $this;
+    }
+
+    /**
+     * Process an individual parameter value
+     *
+     * @param mixed $value
+     * @return array
+     */
+    public function processParameterValue($value)
+    {
+        switch (true) {
+            case is_array($value):
+                foreach ($value as $key => $paramValue) {
+                    $paramValue  = $this->processParameterValue($paramValue);
+                    $value[$key] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
+                }
+
+                return $value;
+
+            case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value)):
+                return $this->convertObjectParameterToScalarValue($value);
+
+            case ($value instanceof Mapping\ClassMetadata):
+                return $value->name;
+
+            default:
+                return $value;
+        }
+    }
+
+    private function convertObjectParameterToScalarValue($value)
+    {
+        $class = $this->_em->getClassMetadata(get_class($value));
+
+        if ($class->isIdentifierComposite) {
+            throw new \InvalidArgumentException(
+                "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 seperately."
+            );
+        }
+
+        $values = ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED)
+            ? $this->_em->getUnitOfWork()->getEntityIdentifier($value)
+            : $class->getIdentifierValues($value);
+
+        $value = $values[$class->getSingleIdentifierFieldName()];
+
+        if (null === $value) {
+            throw new \InvalidArgumentException(
+                "Binding entities to query parameters only allowed for entities that have an identifier."
+            );
+        }
+
+        return $value;
+    }
+
+    /**
+     * Sets the ResultSetMapping that should be used for hydration.
+     *
+     * @param ResultSetMapping $rsm
+     * @return \Doctrine\ORM\AbstractQuery
+     */
+    public function setResultSetMapping(Query\ResultSetMapping $rsm)
+    {
+        $this->_resultSetMapping = $rsm;
+
+        return $this;
+    }
+
+    /**
+     * 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 \Doctrine\ORM\AbstractQuery
+     */
+    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 \Doctrine\ORM\AbstractQuery
+     */
+    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 implictly enables caching.
+     *
+     * @param \Doctrine\Common\Cache\Cache $driver Cache driver
+     * @return \Doctrine\ORM\AbstractQuery
+     */
+    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 \Doctrine\ORM\AbstractQuery 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 \Doctrine\ORM\AbstractQuery 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 \Doctrine\ORM\AbstractQuery 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 AbstractQuery
+     */
+    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 \Doctrine\ORM\AbstractQuery 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).
+     *
+     * @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.
+     *
+     * @throws NonUniqueResultException
+     * @param int $hydrationMode
+     * @return mixed
+     */
+    public function getOneOrNullResult($hydrationMode = null)
+    {
+        $result = $this->execute(null, $hydrationMode);
+
+        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 QueryException If the query result is not unique.
+     */
+    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 \Doctrine\ORM\AbstractQuery
+     */
+    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;
+    }
+
+    /**
+     * 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 \Doctrine\Common\Collections\ArrayCollection|array $parameters The query parameters.
+     * @param integer $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);
+        }
+
+        $stmt = $this->_doExecute();
+
+        return $this->_em->newHydrator($this->_hydrationMode)->iterate(
+            $stmt, $this->_resultSetMapping, $this->_hints
+        );
+    }
+
+    /**
+     * Executes the query.
+     *
+     * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters Query parameters.
+     * @param integer $hydrationMode Processing mode to be used during the hydration process.
+     * @return mixed
+     */
+    public function execute($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;
+        }
+
+        $data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
+            $stmt, $this->_resultSetMapping, $this->_hints
+        );
+
+        $setCacheEntry($data);
+
+        return $data;
+    }
+
+    /**
+     * 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 \Doctrine\ORM\AbstractQuery 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();
+    }
+}
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..a835488
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php
@@ -0,0 +1,683 @@
+.
+ */
+
+namespace Doctrine\ORM;
+
+use Doctrine\Common\Cache\Cache,
+    Doctrine\Common\Cache\ArrayCache,
+    Doctrine\Common\Annotations\AnnotationRegistry,
+    Doctrine\Common\Annotations\AnnotationReader,
+    Doctrine\Common\Persistence\Mapping\Driver\MappingDriver,
+    Doctrine\ORM\Mapping\Driver\AnnotationDriver,
+    Doctrine\ORM\Mapping\QuoteStrategy,
+    Doctrine\ORM\Mapping\DefaultQuoteStrategy,
+    Doctrine\ORM\Mapping\NamingStrategy,
+    Doctrine\ORM\Mapping\DefaultNamingStrategy,
+    Doctrine\Common\Annotations\SimpleAnnotationReader,
+    Doctrine\Common\Annotations\CachedReader;
+
+/**
+ * Configuration container for all configuration options of Doctrine.
+ * It combines all configuration options from DBAL & ORM.
+ *
+ * @since 2.0
+ * @internal When adding a new configuration option just write a getter/setter pair.
+ * @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
+     */
+    public function setProxyDir($dir)
+    {
+        $this->_attributes['proxyDir'] = $dir;
+    }
+
+    /**
+     * Gets the directory where Doctrine generates any necessary proxy class files.
+     *
+     * @return string
+     */
+    public function getProxyDir()
+    {
+        return isset($this->_attributes['proxyDir'])
+            ? $this->_attributes['proxyDir']
+            : null;
+    }
+
+    /**
+     * Gets a boolean flag that indicates whether proxy classes should always be regenerated
+     * during each script execution.
+     *
+     * @return boolean
+     */
+    public function getAutoGenerateProxyClasses()
+    {
+        return isset($this->_attributes['autoGenerateProxyClasses'])
+            ? $this->_attributes['autoGenerateProxyClasses']
+            : true;
+    }
+
+    /**
+     * Sets a boolean flag that indicates whether proxy classes should always be regenerated
+     * during each script execution.
+     *
+     * @param boolean $bool
+     */
+    public function setAutoGenerateProxyClasses($bool)
+    {
+        $this->_attributes['autoGenerateProxyClasses'] = $bool;
+    }
+
+    /**
+     * Gets the namespace where proxy classes reside.
+     *
+     * @return string
+     */
+    public function getProxyNamespace()
+    {
+        return isset($this->_attributes['proxyNamespace'])
+            ? $this->_attributes['proxyNamespace']
+            : null;
+    }
+
+    /**
+     * Sets the namespace where proxy classes reside.
+     *
+     * @param string $ns
+     */
+    public function setProxyNamespace($ns)
+    {
+        $this->_attributes['proxyNamespace'] = $ns;
+    }
+
+    /**
+     * Sets the cache driver implementation that is used for metadata caching.
+     *
+     * @param MappingDriver $driverImpl
+     * @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;
+    }
+
+    /**
+     * Add 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
+     */
+    public function addEntityNamespace($alias, $namespace)
+    {
+        $this->_attributes['entityNamespaces'][$alias] = $namespace;
+    }
+
+    /**
+     * Resolves a registered namespace alias to the full namespace.
+     *
+     * @param string $entityNamespaceAlias
+     * @throws ORMException
+     * @return string
+     */
+    public function getEntityNamespace($entityNamespaceAlias)
+    {
+        if ( ! isset($this->_attributes['entityNamespaces'][$entityNamespaceAlias])) {
+            throw ORMException::unknownEntityNamespace($entityNamespaceAlias);
+        }
+
+        return trim($this->_attributes['entityNamespaces'][$entityNamespaceAlias], '\\');
+    }
+
+    /**
+     * Set the entity alias map
+     *
+     * @param array $entityNamespaces
+     */
+    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.
+     *
+     * @throws ORMException
+     * @return MappingDriver
+     */
+    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
+     */
+    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
+     */
+    public function setQueryCacheImpl(Cache $cacheImpl)
+    {
+        $this->_attributes['queryCacheImpl'] = $cacheImpl;
+    }
+
+    /**
+     * Gets the cache driver implementation that is used for the hydration cache (SQL cache).
+     *
+     * @return \Doctrine\Common\Cache\Cache
+     */
+    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
+     */
+    public function setHydrationCacheImpl(Cache $cacheImpl)
+    {
+        $this->_attributes['hydrationCacheImpl'] = $cacheImpl;
+    }
+
+    /**
+     * Gets the cache driver implementation that is used for metadata caching.
+     *
+     * @return \Doctrine\Common\Cache\Cache
+     */
+    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
+     */
+    public function setMetadataCacheImpl(Cache $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.
+     */
+    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.
+     * @throws ORMException
+     * @return string The DQL query.
+     */
+    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.
+     */
+    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.
+     * @throws ORMException
+     * @return array         A tuple with the first element being the SQL string and the second
+     *                       element being the ResultSetMapping.
+     */
+    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.
+     *
+     * @throws ORMException If a configuration setting has a value that is not
+     *                      suitable for a production environment.
+     */
+    public function ensureProductionSettings()
+    {
+        if ( ! $this->getQueryCacheImpl()) {
+            throw ORMException::queryCacheNotConfigured();
+        }
+
+        if ( ! $this->getMetadataCacheImpl()) {
+            throw ORMException::metadataCacheNotConfigured();
+        }
+
+        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
+     * @param string $className
+     * @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
+     */
+    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.
+     */
+    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
+     * @param string $className
+     * @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
+     */
+    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.
+     */
+    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
+     * @param string $className
+     * @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
+     */
+    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.
+     */
+    public function setCustomDatetimeFunctions(array $functions)
+    {
+        foreach ($functions as $name => $className) {
+            $this->addCustomDatetimeFunction($name, $className);
+        }
+    }
+
+    /**
+     * Set the custom hydrator modes in one pass.
+     *
+     * @param array An array of ($modeName => $hydrator)
+     */
+    public function setCustomHydrationModes($modes)
+    {
+        $this->_attributes['customHydrationModes'] = array();
+
+        foreach ($modes as $modeName => $hydrator) {
+            $this->addCustomHydrationMode($modeName, $hydrator);
+        }
+    }
+
+    /**
+     * Get the hydrator class for the given hydration mode name.
+     *
+     * @param string $modeName The hydration mode name.
+     * @return string $hydrator The hydrator class name.
+     */
+    public function getCustomHydrationMode($modeName)
+    {
+        return isset($this->_attributes['customHydrationModes'][$modeName])
+            ? $this->_attributes['customHydrationModes'][$modeName]
+            : null;
+    }
+
+    /**
+     * Add a custom hydration mode.
+     *
+     * @param string $modeName The hydration mode name.
+     * @param string $hydrator The hydrator class name.
+     */
+    public function addCustomHydrationMode($modeName, $hydrator)
+    {
+        $this->_attributes['customHydrationModes'][$modeName] = $hydrator;
+    }
+
+    /**
+     * Set a class metadata factory.
+     *
+     * @param string $cmfName
+     */
+    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'];
+    }
+
+    /**
+     * Add 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;
+    }
+
+    /**
+     * Set default repository class.
+     *
+     * @since 2.2
+     * @param string $className
+     * @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';
+    }
+
+    /**
+     * Set naming strategy.
+     *
+     * @since 2.3
+     * @param NamingStrategy $namingStrategy
+     */
+    public function setNamingStrategy(NamingStrategy $namingStrategy)
+    {
+        $this->_attributes['namingStrategy'] = $namingStrategy;
+    }
+
+    /**
+     * Get naming strategy..
+     *
+     * @since 2.3
+     * @return NamingStrategy
+     */
+    public function getNamingStrategy()
+    {
+        if ( ! isset($this->_attributes['namingStrategy'])) {
+            $this->_attributes['namingStrategy'] = new DefaultNamingStrategy();
+        }
+
+        return $this->_attributes['namingStrategy'];
+    }
+
+    /**
+     * Set quote strategy.
+     *
+     * @since 2.3
+     * @param Doctrine\ORM\Mapping\QuoteStrategy $quoteStrategy
+     */
+    public function setQuoteStrategy(QuoteStrategy $quoteStrategy)
+    {
+        $this->_attributes['quoteStrategy'] = $quoteStrategy;
+    }
+
+    /**
+     * Get 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'];
+    }
+}
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..50e7382
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php
@@ -0,0 +1,902 @@
+.
+ */
+
+namespace Doctrine\ORM;
+
+use Exception,
+    Doctrine\Common\EventManager,
+    Doctrine\Common\Persistence\ObjectManager,
+    Doctrine\DBAL\Connection,
+    Doctrine\DBAL\LockMode,
+    Doctrine\ORM\Mapping\ClassMetadata,
+    Doctrine\ORM\Mapping\ClassMetadataFactory,
+    Doctrine\ORM\Query\ResultSetMapping,
+    Doctrine\ORM\Proxy\ProxyFactory,
+    Doctrine\ORM\Query\FilterCollection;
+
+/**
+ * The EntityManager is the central access point to ORM functionality.
+ *
+ * @since   2.0
+ * @author  Benjamin Eberlei 
+ * @author  Guilherme Blanco 
+ * @author  Jonathan Wage 
+ * @author  Roman Borschel 
+ */
+class EntityManager implements ObjectManager
+{
+    /**
+     * 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 EntityRepository instances.
+     *
+     * @var array
+     */
+    private $repositories = array();
+
+    /**
+     * 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 maintained (cached) hydrators. One instance per type.
+     *
+     * @var array
+     */
+    private $hydrators = array();
+
+    /**
+     * The proxy factory used to create dynamic proxies.
+     *
+     * @var \Doctrine\ORM\Proxy\ProxyFactory
+     */
+    private $proxyFactory;
+
+    /**
+     * 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;
+
+    /**
+     * 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->unitOfWork   = new UnitOfWork($this);
+        $this->proxyFactory = new ProxyFactory(
+            $this,
+            $config->getProxyDir(),
+            $config->getProxyNamespace(),
+            $config->getAutoGenerateProxyClasses()
+        );
+    }
+
+    /**
+     * Gets the database connection object used by the EntityManager.
+     *
+     * @return \Doctrine\DBAL\Connection
+     */
+    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;
+    }
+
+    /**
+     * 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()
+    {
+        if ($this->expressionBuilder === null) {
+            $this->expressionBuilder = new Query\Expr;
+        }
+
+        return $this->expressionBuilder;
+    }
+
+    /**
+     * Starts a transaction on the underlying database connection.
+     */
+    public function beginTransaction()
+    {
+        $this->conn->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 Returns the non-empty value returned from the closure or true instead
+     */
+    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;
+        }
+    }
+
+    /**
+     * Commits a transaction on the underlying database connection.
+     */
+    public function commit()
+    {
+        $this->conn->commit();
+    }
+
+    /**
+     * Performs a rollback on the underlying database connection.
+     */
+    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
+     *
+     * @return \Doctrine\ORM\Mapping\ClassMetadata
+     * @internal Performance-sensitive method.
+     */
+    public function getClassMetadata($className)
+    {
+        return $this->metadataFactory->getMetadataFor($className);
+    }
+
+    /**
+     * Creates a new Query object.
+     *
+     * @param string $dql The DQL string.
+     * @return \Doctrine\ORM\Query
+     */
+    public function createQuery($dql = "")
+    {
+        $query = new Query($this);
+
+        if ( ! empty($dql)) {
+            $query->setDql($dql);
+        }
+
+        return $query;
+    }
+
+    /**
+     * Creates a Query from a named query.
+     *
+     * @param string $name
+     * @return \Doctrine\ORM\Query
+     */
+    public function createNamedQuery($name)
+    {
+        return $this->createQuery($this->config->getNamedQuery($name));
+    }
+
+    /**
+     * Creates a native SQL query.
+     *
+     * @param string $sql
+     * @param ResultSetMapping $rsm The ResultSetMapping to use.
+     * @return NativeQuery
+     */
+    public function createNativeQuery($sql, ResultSetMapping $rsm)
+    {
+        $query = new NativeQuery($this);
+
+        $query->setSql($sql);
+        $query->setResultSetMapping($rsm);
+
+        return $query;
+    }
+
+    /**
+     * Creates a NativeQuery from a named native query.
+     *
+     * @param string $name
+     * @return \Doctrine\ORM\NativeQuery
+     */
+    public function createNamedNativeQuery($name)
+    {
+        list($sql, $rsm) = $this->config->getNamedNativeQuery($name);
+
+        return $this->createNativeQuery($sql, $rsm);
+    }
+
+    /**
+     * Create a QueryBuilder instance
+     *
+     * @return QueryBuilder $qb
+     */
+    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 object $entity
+     * @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
+     * @param mixed $id
+     * @param integer $lockMode
+     * @param integer $lockVersion
+     *
+     * @return object
+     */
+    public function find($entityName, $id, $lockMode = LockMode::NONE, $lockVersion = null)
+    {
+        $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];
+        }
+
+        $unitOfWork = $this->getUnitOfWork();
+
+        // Check identity map first
+        if (($entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) {
+            if ( ! ($entity instanceof $class->name)) {
+                return null;
+            }
+
+            switch ($lockMode) {
+                case LockMode::OPTIMISTIC:
+                    $this->lock($entity, $lockMode, $lockVersion);
+                    break;
+
+                case LockMode::PESSIMISTIC_READ:
+                case LockMode::PESSIMISTIC_WRITE:
+                    $persister = $unitOfWork->getEntityPersister($class->name);
+                    $persister->refresh($sortedId, $entity, $lockMode);
+                    break;
+            }
+
+            return $entity; // Hit!
+        }
+
+        $persister = $unitOfWork->getEntityPersister($class->name);
+
+        switch ($lockMode) {
+            case LockMode::NONE:
+                return $persister->load($sortedId);
+
+            case LockMode::OPTIMISTIC:
+                if ( ! $class->isVersioned) {
+                    throw OptimisticLockException::notVersioned($class->name);
+                }
+
+                $entity = $persister->load($sortedId);
+
+                $unitOfWork->lock($entity, $lockMode, $lockVersion);
+
+                return $entity;
+
+            default:
+                if ( ! $this->getConnection()->isTransactionActive()) {
+                    throw TransactionRequiredException::transactionRequired();
+                }
+
+                return $persister->load($sortedId, null, null, array(), $lockMode);
+        }
+    }
+
+    /**
+     * 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.
+     */
+    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;
+    }
+
+    /**
+     * 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)
+    {
+        $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 $entityName if given, only entities of this type will get detached
+     */
+    public function clear($entityName = null)
+    {
+        $this->unitOfWork->clear($entityName);
+    }
+
+    /**
+     * 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.
+     */
+    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 $object The instance to make managed and persistent.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    public function merge($entity)
+    {
+        if ( ! is_object($entity)) {
+            throw ORMInvalidArgumentException::invalidObject('EntityManager#merge()' , $entity);
+        }
+
+        $this->errorIfClosed();
+
+        return $this->unitOfWork->merge($entity);
+    }
+
+    /**
+     * Creates a copy of the given entity. Can create a shallow or a deep copy.
+     *
+     * @param object $entity  The entity to copy.
+     * @return object  The new entity.
+     * @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.");
+    }
+
+    /**
+     * Acquire a lock on the given entity.
+     *
+     * @param object $entity
+     * @param int $lockMode
+     * @param int $lockVersion
+     * @throws OptimisticLockException
+     * @throws PessimisticLockException
+     */
+    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 EntityRepository The repository class.
+     */
+    public function getRepository($entityName)
+    {
+        $entityName = ltrim($entityName, '\\');
+
+        if (isset($this->repositories[$entityName])) {
+            return $this->repositories[$entityName];
+        }
+
+        $metadata = $this->getClassMetadata($entityName);
+        $repositoryClassName = $metadata->customRepositoryClassName;
+
+        if ($repositoryClassName === null) {
+            $repositoryClassName = $this->config->getDefaultRepositoryClassName();
+        }
+
+        $repository = new $repositoryClassName($this, $metadata);
+
+        $this->repositories[$entityName] = $repository;
+
+        return $repository;
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Gets the EventManager used by the EntityManager.
+     *
+     * @return \Doctrine\Common\EventManager
+     */
+    public function getEventManager()
+    {
+        return $this->eventManager;
+    }
+
+    /**
+     * Gets the Configuration used by the EntityManager.
+     *
+     * @return \Doctrine\ORM\Configuration
+     */
+    public function getConfiguration()
+    {
+        return $this->config;
+    }
+
+    /**
+     * Throws an exception if the EntityManager is closed or currently not active.
+     *
+     * @throws ORMException If the EntityManager is closed.
+     */
+    private function errorIfClosed()
+    {
+        if ($this->closed) {
+            throw ORMException::entityManagerClosed();
+        }
+    }
+
+    /**
+     * Check if the Entity manager is open or closed.
+     *
+     * @return bool
+     */
+    public function isOpen()
+    {
+        return (!$this->closed);
+    }
+
+    /**
+     * Gets the UnitOfWork used by the EntityManager to coordinate operations.
+     *
+     * @return \Doctrine\ORM\UnitOfWork
+     */
+    public function getUnitOfWork()
+    {
+        return $this->unitOfWork;
+    }
+
+    /**
+     * 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.
+     *
+     * @param int $hydrationMode
+     * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator
+     */
+    public function getHydrator($hydrationMode)
+    {
+        if ( ! isset($this->hydrators[$hydrationMode])) {
+            $this->hydrators[$hydrationMode] = $this->newHydrator($hydrationMode);
+        }
+
+        return $this->hydrators[$hydrationMode];
+    }
+
+    /**
+     * Create a new instance for the given hydration mode.
+     *
+     * @param  int $hydrationMode
+     * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator
+     */
+    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);
+    }
+
+    /**
+     * Gets the proxy factory used by the EntityManager to create entity proxies.
+     *
+     * @return ProxyFactory
+     */
+    public function getProxyFactory()
+    {
+        return $this->proxyFactory;
+    }
+
+    /**
+     * Helper method to initialize a lazy loading proxy or persistent collection.
+     *
+     * This method is a no-op for other objects
+     *
+     * @param object $obj
+     */
+    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.
+     */
+    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());
+    }
+
+    /**
+     * Gets the enabled filters.
+     *
+     * @return FilterCollection The active filter collection.
+     */
+    public function getFilters()
+    {
+        if (null === $this->filterCollection) {
+            $this->filterCollection = new FilterCollection($this);
+        }
+
+        return $this->filterCollection;
+    }
+
+    /**
+     * Checks whether the state of the filter collection is clean.
+     *
+     * @return boolean True, if the filter collection is clean.
+     */
+    public function isFiltersStateClean()
+    {
+        return null === $this->filterCollection || $this->filterCollection->isClean();
+    }
+
+    /**
+     * Checks whether the Entity Manager has filters.
+     *
+     * @return True, if the EM has a filter collection.
+     */
+    public function hasFilters()
+    {
+        return null !== $this->filterCollection;
+    }
+}
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..087c65d
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityNotFoundException.php
@@ -0,0 +1,34 @@
+.
+ */
+
+namespace Doctrine\ORM;
+
+/**
+ * Exception thrown when a Proxy fails to retrieve an Entity result.
+ *
+ * @author robo
+ * @since 2.0
+ */
+class EntityNotFoundException extends ORMException
+{
+    public function __construct()
+    {
+        parent::__construct('Entity 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..fc5ae83
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php
@@ -0,0 +1,277 @@
+.
+ */
+
+namespace Doctrine\ORM;
+
+use Doctrine\DBAL\LockMode;
+use Doctrine\Common\Persistence\ObjectRepository;
+
+use Doctrine\Common\Collections\Selectable;
+use Doctrine\Common\Collections\Criteria;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\ExpressionBuilder;
+
+/**
+ * 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 ClassMetadata $classMetadata The class descriptor.
+     */
+    public function __construct($em, Mapping\ClassMetadata $class)
+    {
+        $this->_entityName = $class->name;
+        $this->_em         = $em;
+        $this->_class      = $class;
+    }
+
+    /**
+     * Create a new QueryBuilder instance that is prepopulated for this entity name
+     *
+     * @param string $alias
+     * @return QueryBuilder $qb
+     */
+    public function createQueryBuilder($alias)
+    {
+        return $this->_em->createQueryBuilder()
+            ->select($alias)
+            ->from($this->_entityName, $alias);
+    }
+
+    /**
+     * Create 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.
+     */
+    public function clear()
+    {
+        $this->_em->clear($this->_class->rootEntityName);
+    }
+
+    /**
+     * Finds an entity by its primary key / identifier.
+     *
+     * @param mixed $id The identifier.
+     * @param integer $lockMode
+     * @param integer $lockVersion
+     *
+     * @return object The entity.
+     */
+    public function find($id, $lockMode = LockMode::NONE, $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
+     */
+    public function findOneBy(array $criteria, array $orderBy = null)
+    {
+        $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
+
+        return $persister->load($criteria, null, null, array(), 0, 1, $orderBy);
+    }
+
+    /**
+     * Adds support for magic finders.
+     *
+     * @return array|object The found entity/entities.
+     * @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 ArrayCollection($persister->loadCriteria($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..4346054
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php
@@ -0,0 +1,77 @@
+.
+*/
+
+namespace Doctrine\ORM\Event;
+
+use Doctrine\Common\EventArgs;
+use Doctrine\ORM\EntityManager;
+
+/**
+ * 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 EventArgs
+{
+    /**
+     * @var \Doctrine\ORM\EntityManager
+     */
+    private $em;
+
+    /**
+     * @var object
+     */
+    private $entity;
+
+    /**
+     * Constructor
+     *
+     * @param object $entity
+     * @param \Doctrine\ORM\EntityManager $em
+     */
+    public function __construct($entity, EntityManager $em)
+    {
+        $this->entity = $entity;
+        $this->em     = $em;
+    }
+
+    /**
+     * Retrieve associated Entity.
+     *
+     * @return object
+     */
+    public function getEntity()
+    {
+        return $this->entity;
+    }
+
+    /**
+     * Retrieve associated EntityManager.
+     *
+     * @return \Doctrine\ORM\EntityManager
+     */
+    public function getEntityManager()
+    {
+        return $this->em;
+    }
+}
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..4c2cb07
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php
@@ -0,0 +1,76 @@
+.
+ */
+
+namespace Doctrine\ORM\Event;
+
+use Doctrine\Common\EventArgs;
+use Doctrine\ORM\Mapping\ClassMetadataInfo;
+use Doctrine\ORM\EntityManager;
+
+/**
+ * Class that holds event arguments for a loadMetadata event.
+ *
+ * @author Jonathan H. Wage 
+ * @since  2.0
+ */
+class LoadClassMetadataEventArgs extends EventArgs
+{
+    /**
+     * @var \Doctrine\ORM\Mapping\ClassMetadata
+     */
+    private $classMetadata;
+
+    /**
+     * @var \Doctrine\ORM\EntityManager
+     */
+    private $em;
+
+    /**
+     * Constructor.
+     *
+     * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $classMetadata
+     * @param \Doctrine\ORM\EntityManager $em
+     */
+    public function __construct(ClassMetadataInfo $classMetadata, EntityManager $em)
+    {
+        $this->classMetadata = $classMetadata;
+        $this->em            = $em;
+    }
+
+    /**
+     * Retrieve associated ClassMetadata.
+     *
+     * @return \Doctrine\ORM\Mapping\ClassMetadataInfo
+     */
+    public function getClassMetadata()
+    {
+        return $this->classMetadata;
+    }
+
+    /**
+     * Retrieve associated EntityManager.
+     *
+     * @return \Doctrine\ORM\EntityManager
+     */
+    public function getEntityManager()
+    {
+        return $this->em;
+    }
+}
+
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..309994f
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php
@@ -0,0 +1,84 @@
+.
+ */
+
+namespace Doctrine\ORM\Event;
+
+/**
+ * Provides event arguments for the onClear event.
+ *
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link        www.doctrine-project.org
+ * @since       2.0
+ * @author      Roman Borschel 
+ * @author      Benjamin Eberlei 
+ */
+class OnClearEventArgs extends \Doctrine\Common\EventArgs
+{
+    /**
+     * @var \Doctrine\ORM\EntityManager
+     */
+    private $em;
+
+    /**
+     * @var string
+     */
+    private $entityClass;
+
+    /**
+     * Constructor.
+     *
+     * @param \Doctrine\ORM\EntityManager $em
+     * @param string $entityClass Optional entity class
+     */
+    public function __construct($em, $entityClass = null)
+    {
+        $this->em          = $em;
+        $this->entityClass = $entityClass;
+    }
+
+    /**
+     * Retrieve 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
+     */
+    public function getEntityClass()
+    {
+        return $this->entityClass;
+    }
+
+    /**
+     * Check 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..1e18d2a
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php
@@ -0,0 +1,84 @@
+.
+ */
+
+namespace Doctrine\ORM\Event;
+
+use Doctrine\ORM\EntityManager;
+
+/**
+ * Provides event arguments for the preFlush event.
+ *
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link        www.doctrine-project.org
+ * @since       2.0
+ * @author      Roman Borschel 
+ * @author      Benjamin Eberlei 
+ */
+class OnFlushEventArgs extends \Doctrine\Common\EventArgs
+{
+    /**
+     * @var Doctirne\ORM\EntityManager
+     */
+    private $em;
+
+    //private $entitiesToPersist = array();
+    //private $entitiesToRemove = array();
+
+    /**
+     * Constructor.
+     *
+     * @param \Doctrine\ORM\EntityManager $em
+     */
+    public function __construct(EntityManager $em)
+    {
+        $this->em = $em;
+    }
+
+    /**
+     * Retrieve associated EntityManager.
+     *
+     * @return \Doctrine\ORM\EntityManager
+     */
+    public function getEntityManager()
+    {
+        return $this->em;
+    }
+
+    /*
+    public function addEntityToPersist($entity)
+    {
+
+    }
+
+    public function addEntityToRemove($entity)
+    {
+
+    }
+
+    public function addEntityToUpdate($entity)
+    {
+
+    }
+
+    public function getEntitiesToPersist()
+    {
+        return $this->_entitiesToPersist;
+    }
+    */
+}
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..5f9735c
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php
@@ -0,0 +1,58 @@
+.
+ */
+namespace Doctrine\ORM\Event;
+
+use Doctrine\ORM\EntityManager;
+use Doctrine\Common\EventArgs;
+
+/**
+ * Provides event arguments for the postFlush event.
+ *
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link        www.doctrine-project.org
+ * @since       2.0
+ * @author      Daniel Freudenberger 
+ */
+class PostFlushEventArgs extends EventArgs
+{
+    /**
+     * @var \Doctrine\ORM\EntityManager
+     */
+    private $em;
+
+    /**
+     * Constructor.
+     *
+     * @param \Doctrine\ORM\EntityManager $em
+     */
+    public function __construct(EntityManager $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/PreFlushEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php
new file mode 100644
index 0000000..82f2b7e
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php
@@ -0,0 +1,50 @@
+.
+ */
+
+namespace Doctrine\ORM\Event;
+
+/**
+ * Provides event arguments for the preFlush event.
+ *
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link        www.doctrine-project.com
+ * @since       2.0
+ * @author      Roman Borschel 
+ * @author      Benjamin Eberlei 
+ */
+class PreFlushEventArgs extends \Doctrine\Common\EventArgs
+{
+    /**
+     * @var EntityManager
+     */
+    private $_em;
+
+    public function __construct($em)
+    {
+        $this->_em = $em;
+    }
+
+    /**
+     * @return 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..188417e
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php
@@ -0,0 +1,129 @@
+.
+ */
+
+namespace Doctrine\ORM\Event;
+
+use Doctrine\Common\EventArgs,
+    Doctrine\ORM\EntityManager;
+
+/**
+ * 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 \Doctrine\ORM\EntityManager $em
+     * @param array $changeSet
+     */
+    public function __construct($entity, EntityManager $em, array &$changeSet)
+    {
+        parent::__construct($entity, $em);
+
+        $this->entityChangeSet = &$changeSet;
+    }
+
+    /**
+     * Retrieve entity changeset.
+     *
+     * @return array
+     */
+    public function getEntityChangeSet()
+    {
+        return $this->entityChangeSet;
+    }
+
+    /**
+     * Check if field has a changeset.
+     *
+     * @return boolean
+     */
+    public function hasChangedField($field)
+    {
+        return isset($this->entityChangeSet[$field]);
+    }
+
+    /**
+     * Get 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];
+    }
+
+    /**
+     * Get 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];
+    }
+
+    /**
+     * Set the new value of this field.
+     *
+     * @param string $field
+     * @param mixed $value
+     */
+    public function setNewValue($field, $value)
+    {
+        $this->assertValidField($field);
+
+        $this->entityChangeSet[$field][1] = $value;
+    }
+
+    /**
+     * Assert the field exists in changeset.
+     *
+     * @param string $field
+     */
+    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..812a43e
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Events.php
@@ -0,0 +1,146 @@
+.
+ */
+
+namespace Doctrine\ORM;
+
+/**
+ * Container for all ORM events.
+ *
+ * This class cannot be instantiated.
+ *
+ * @author Roman Borschel 
+ * @since 2.0
+ */
+final class Events
+{
+    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 preFlush event occurs when the EntityManager#flush() operation is invoked,
+     * but before any changes to managed entites 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..ef98caf
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php
@@ -0,0 +1,48 @@
+.
+ */
+
+namespace Doctrine\ORM\Id;
+
+use Doctrine\ORM\EntityManager;
+
+abstract class AbstractIdGenerator
+{
+    /**
+     * Generates an identifier for an entity.
+     *
+     * @param \Doctrine\ORM\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..0597c3b
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php
@@ -0,0 +1,70 @@
+.
+ */
+
+namespace Doctrine\ORM\Id;
+
+use Doctrine\ORM\EntityManager;
+use Doctrine\ORM\ORMException;
+
+/**
+ * Special generator for application-assigned identifiers (doesnt 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.
+     *
+     * @param object $entity
+     * @return mixed
+     * @override
+     */
+    public function generate(EntityManager $em, $entity)
+    {
+        $class      = $em->getClassMetadata(get_class($entity));
+        $idFields   = $class->getIdentifierFieldNames();
+        $identifier = array();
+
+        foreach ($idFields as $idField) {
+            $value = $class->reflFields[$idField]->getValue($entity);
+
+            if ( ! isset($value)) {
+                throw ORMException::entityMissingAssignedIdForField($entity, $idField);
+            }
+
+            if (isset($class->associationMappings[$idField])) {
+                if ( ! $em->getUnitOfWork()->isInIdentityMap($value)) {
+                    throw ORMException::entityMissingForeignAssignedId($entity, $value);
+                }
+
+                // NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
+                $value = current($em->getUnitOfWork()->getEntityIdentifier($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..e387023
--- /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 $seqName 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..ad61312
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php
@@ -0,0 +1,63 @@
+.
+ */
+
+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;
+
+    /**
+     * @param string $seqName 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..5b5d831
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php
@@ -0,0 +1,106 @@
+.
+ */
+
+namespace Doctrine\ORM\Id;
+
+use Serializable, Doctrine\ORM\EntityManager;
+
+/**
+ * Represents an ID generator that uses a database sequence.
+ *
+ * @since 2.0
+ * @author Roman Borschel 
+ */
+class SequenceGenerator extends AbstractIdGenerator implements Serializable
+{
+    private $_allocationSize;
+    private $_sequenceName;
+    private $_nextValue = 0;
+    private $_maxValue = null;
+
+    /**
+     * Initializes a new sequence generator.
+     *
+     * @param \Doctrine\ORM\EntityManager $em The EntityManager to use.
+     * @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;
+    }
+
+    /**
+     * Generates an ID for the given entity.
+     *
+     * @param object $entity
+     * @return integer|float The generated value.
+     * @override
+     */
+    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|float
+     */
+    public function getCurrentMaxValue()
+    {
+        return $this->_maxValue;
+    }
+
+    /**
+     * Gets the next value that will be returned by generate().
+     *
+     * @return integer|float
+     */
+    public function getNextValue()
+    {
+        return $this->_nextValue;
+    }
+
+    public function serialize()
+    {
+        return serialize(array(
+            'allocationSize' => $this->_allocationSize,
+            'sequenceName'   => $this->_sequenceName
+        ));
+    }
+
+    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..7e84b0a
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php
@@ -0,0 +1,81 @@
+.
+ */
+
+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
+{
+    private $_tableName;
+    private $_sequenceName;
+    private $_allocationSize;
+    private $_nextValue;
+    private $_maxValue;
+
+    public function __construct($tableName, $sequenceName = 'default', $allocationSize = 10)
+    {
+        $this->_tableName = $tableName;
+        $this->_sequenceName = $sequenceName;
+        $this->_allocationSize = $allocationSize;
+    }
+
+    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..3978f1b
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/UuidGenerator.php
@@ -0,0 +1,48 @@
+.
+ */
+
+namespace Doctrine\ORM\Id;
+
+use Serializable, Doctrine\ORM\EntityManager;
+
+/**
+ * Represents an ID generator that uses the database UUID expression
+ *
+ * @since 2.3
+ * @author Maarten de Keizer 
+ */
+class UuidGenerator extends AbstractIdGenerator
+{
+    
+    /**
+     * Generates an ID for the given entity.
+     *
+     * @param Doctrine\ORM\EntityManager $em The EntityManager to user
+     * @param object $entity
+     * @return string The generated value.
+     * @override
+     */
+    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..a4ae5f3
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php
@@ -0,0 +1,118 @@
+.
+ */
+
+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;
+
+    private $_nodeStates = array();
+    private $_classes = array(); // The nodes to sort
+    private $_relatedClasses = array();
+    private $_sorted = array();
+
+    /**
+     * Clears the current graph.
+     *
+     * @return void
+     */
+    public function clear()
+    {
+        $this->_classes =
+        $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;
+    }
+
+    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;
+    }
+
+    public function addDependency($fromClass, $toClass)
+    {
+        $this->_relatedClasses[$fromClass->name][] = $toClass;
+    }
+
+    public function hasClass($className)
+    {
+        return isset($this->_classes[$className]);
+    }
+
+    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..a5eae8b
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php
@@ -0,0 +1,390 @@
+.
+ */
+
+namespace Doctrine\ORM\Internal\Hydration;
+
+use PDO,
+    Doctrine\DBAL\Connection,
+    Doctrine\DBAL\Types\Type,
+    Doctrine\ORM\EntityManager,
+    Doctrine\ORM\Events,
+    Doctrine\ORM\Mapping\ClassMetadata;
+
+/**
+ * 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
+{
+    /** @var \Doctrine\ORM\Query\ResultSetMapping The ResultSetMapping. */
+    protected $_rsm;
+
+    /** @var EntityManager The EntityManager instance. */
+    protected $_em;
+
+    /** @var \Doctrine\DBAL\Platforms\AbstractPlatform The dbms Platform instance */
+    protected $_platform;
+
+    /** @var \Doctrine\ORM\UnitOfWork The UnitOfWork of the associated EntityManager. */
+    protected $_uow;
+
+    /** @var array The cache used during row-by-row hydration. */
+    protected $_cache = array();
+
+    /** @var \Doctrine\DBAL\Driver\Statement The statement that provides the data to hydrate. */
+    protected $_stmt;
+
+    /** @var array The query hints. */
+    protected $_hints;
+
+    /**
+     * Initializes a new instance of a class derived from AbstractHydrator.
+     *
+     * @param \Doctrine\ORM\EntityManager $em The EntityManager to use.
+     */
+    public function __construct(EntityManager $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
+     *
+     * @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 mixed
+     */
+    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, $this->_cache, $result);
+
+        return $result;
+    }
+
+    /**
+     * Excutes one-time preparation tasks, once each time hydration is started
+     * through {@link hydrateAll} or {@link iterate()}.
+     */
+    protected function prepare()
+    {}
+
+    /**
+     * Excutes one-time cleanup tasks at the end of a hydration that was initiated
+     * through {@link hydrateAll} or {@link iterate()}.
+     */
+    protected function cleanup()
+    {
+        $this->_rsm = null;
+
+        $this->_stmt->closeCursor();
+        $this->_stmt = null;
+    }
+
+    /**
+     * Hydrates a single row from the current statement instance.
+     *
+     * Template method.
+     *
+     * @param array $data The row data.
+     * @param array $cache The cache to use.
+     * @param mixed $result The result to fill.
+     */
+    protected function hydrateRowData(array $data, array &$cache, array &$result)
+    {
+        throw new HydrationException("hydrateRowData() not implemented by this hydrator.");
+    }
+
+    /**
+     * Hydrates all rows from the current statement instance at once.
+     */
+    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 specfic key 'scalars'.
+     *
+     * @param array $data SQL Result Row
+     * @param array &$cache Cache for column to field result information
+     * @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 &$cache, array &$id, array &$nonemptyComponents)
+    {
+        $rowData = array();
+
+        foreach ($data as $key => $value) {
+            // Parse each column name only once. Cache the results.
+            if ( ! isset($cache[$key])) {
+                switch (true) {
+                    // NOTE: Most of the times it's a field mapping, so keep it first!!!
+                    case (isset($this->_rsm->fieldMappings[$key])):
+                        $fieldName     = $this->_rsm->fieldMappings[$key];
+                        $classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
+
+                        $cache[$key]['fieldName']    = $fieldName;
+                        $cache[$key]['type']         = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
+                        $cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
+                        $cache[$key]['dqlAlias']     = $this->_rsm->columnOwnerMap[$key];
+                        break;
+
+                    case (isset($this->_rsm->scalarMappings[$key])):
+                        $cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
+                        $cache[$key]['type']      = Type::getType($this->_rsm->typeMappings[$key]);
+                        $cache[$key]['isScalar']  = true;
+                        break;
+
+                    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];
+                        $classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$this->_rsm->columnOwnerMap[$key]]);
+
+                        $cache[$key]['isMetaColumn'] = true;
+                        $cache[$key]['fieldName']    = $fieldName;
+                        $cache[$key]['dqlAlias']     = $this->_rsm->columnOwnerMap[$key];
+                        $cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]);
+                        break;
+
+                    default:
+                        // 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.
+                        continue 2;
+                }
+            }
+
+            if (isset($cache[$key]['isScalar'])) {
+                $value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
+
+                $rowData['scalars'][$cache[$key]['fieldName']] = $value;
+
+                continue;
+            }
+
+            $dqlAlias = $cache[$key]['dqlAlias'];
+
+            if ($cache[$key]['isIdentifier']) {
+                $id[$dqlAlias] .= '|' . $value;
+            }
+
+            if (isset($cache[$key]['isMetaColumn'])) {
+                if ( ! isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value !== null) {
+                    $rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
+                    if ($cache[$key]['isIdentifier']) {
+                        $nonemptyComponents[$dqlAlias] = true;
+                    }
+                }
+
+                continue;
+            }
+
+            // in an inheritance hierarchy the same field could be defined several times.
+            // We overwrite this value so long we dont 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[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) {
+                continue;
+            }
+
+            $rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
+
+            if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
+                $nonemptyComponents[$dqlAlias] = true;
+            }
+        }
+
+        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
+     * @param array $cache
+     *
+     * @return array The processed row.
+     */
+    protected function gatherScalarRowData(&$data, &$cache)
+    {
+        $rowData = array();
+
+        foreach ($data as $key => $value) {
+            // Parse each column name only once. Cache the results.
+            if ( ! isset($cache[$key])) {
+                switch (true) {
+                    // NOTE: During scalar hydration, most of the times it's a scalar mapping, keep it first!!!
+                    case (isset($this->_rsm->scalarMappings[$key])):
+                        $cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
+                        $cache[$key]['isScalar']  = true;
+                        break;
+
+                    case (isset($this->_rsm->fieldMappings[$key])):
+                        $fieldName     = $this->_rsm->fieldMappings[$key];
+                        $classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
+
+                        $cache[$key]['fieldName'] = $fieldName;
+                        $cache[$key]['type']      = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
+                        $cache[$key]['dqlAlias']  = $this->_rsm->columnOwnerMap[$key];
+                        break;
+
+                    case (isset($this->_rsm->metaMappings[$key])):
+                        // Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
+                        $cache[$key]['isMetaColumn'] = true;
+                        $cache[$key]['fieldName']    = $this->_rsm->metaMappings[$key];
+                        $cache[$key]['dqlAlias']     = $this->_rsm->columnOwnerMap[$key];
+                        break;
+
+                    default:
+                        // 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.
+                        continue 2;
+                }
+            }
+
+            $fieldName = $cache[$key]['fieldName'];
+
+            switch (true) {
+                case (isset($cache[$key]['isScalar'])):
+                    $rowData[$fieldName] = $value;
+                    break;
+
+                case (isset($cache[$key]['isMetaColumn'])):
+                    $rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
+                    break;
+
+                default:
+                    $value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
+
+                    $rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
+            }
+        }
+
+        return $rowData;
+    }
+
+    /**
+     * Register entity as managed in UnitOfWork.
+     *
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
+     * @param object $entity
+     * @param array $data
+     *
+     * @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) {
+                if (isset($class->associationMappings[$fieldName])) {
+                    $id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']];
+                } else {
+                    $id[$fieldName] = $data[$fieldName];
+                }
+            }
+        } else {
+            if (isset($class->associationMappings[$class->identifier[0]])) {
+                $id = array($class->identifier[0] => $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]);
+            } else {
+                $id = array($class->identifier[0] => $data[$class->identifier[0]]);
+            }
+        }
+
+        $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
+    }
+
+    /**
+     * When executed in a hydrate() loop we have to clear internal state to
+     * decrease memory consumption.
+     */
+    public function onClear($eventArgs)
+    {
+    }
+}
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..8f7178e
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php
@@ -0,0 +1,289 @@
+.
+ */
+
+namespace Doctrine\ORM\Internal\Hydration;
+
+use PDO, Doctrine\DBAL\Connection, 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
+{
+    private $_ce = array();
+    private $_rootAliases = array();
+    private $_isSimpleQuery = false;
+    private $_identifierMap = array();
+    private $_resultPointers = array();
+    private $_idTemplate = array();
+    private $_resultCounter = 0;
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function prepare()
+    {
+        $this->_isSimpleQuery  = count($this->_rsm->aliasMap) <= 1;
+        $this->_identifierMap  = array();
+        $this->_resultPointers = array();
+        $this->_idTemplate     = array();
+        $this->_resultCounter  = 0;
+
+        foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
+            $this->_identifierMap[$dqlAlias]  = array();
+            $this->_resultPointers[$dqlAlias] = array();
+            $this->_idTemplate[$dqlAlias]     = '';
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function hydrateAllData()
+    {
+        $result = array();
+        $cache  = array();
+
+        while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
+            $this->hydrateRowData($data, $cache, $result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function hydrateRowData(array $row, array &$cache, array &$result)
+    {
+        // 1) Initialize
+        $id = $this->_idTemplate; // initialize the id-memory
+        $nonemptyComponents = array();
+        $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents);
+
+        // Extract scalar values. They're appended at the end.
+        if (isset($rowData['scalars'])) {
+            $scalars = $rowData['scalars'];
+
+            unset($rowData['scalars']);
+
+            if (empty($rowData)) {
+                ++$this->_resultCounter;
+            }
+        }
+
+        // 2) Now hydrate the data found in the current row.
+        foreach ($rowData 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];
+                $relation = $this->getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
+
+                // Check the type of the relation (many or single-valued)
+                if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
+                    $oneToOne = false;
+
+                    if (isset($nonemptyComponents[$dqlAlias])) {
+                        if ( ! isset($baseElement[$relationAlias])) {
+                            $baseElement[$relationAlias] = array();
+                        }
+
+                        $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 if ( ! isset($baseElement[$relationAlias])) {
+                        $baseElement[$relationAlias] = array();
+                    }
+                } 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 ($coll !== null) {
+                    $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]) ) {
+                    if ($this->_rsm->isMixed) {
+                        $result[] = array($entityKey => null);
+                    } else {
+                        $result[] = null;
+                    }
+                    $resultKey = $this->_resultCounter;
+                    ++$this->_resultCounter;
+                    continue;
+                }
+
+                // Check for an existing element
+                if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
+                    $element = $rowData[$dqlAlias];
+                    if ($this->_rsm->isMixed) {
+                        $element = array($entityKey => $element);
+                    }
+
+                    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;
+                    /*if ($this->_rsm->isMixed) {
+                        $result[] =& $result[$index];
+                        ++$this->_resultCounter;
+                    }*/
+                }
+                $this->updateResultPointer($result, $index, $dqlAlias, false);
+            }
+        }
+
+        // Append scalar values to mixed result sets
+        if (isset($scalars)) {
+            if ( ! isset($resultKey) ) {
+                // this only ever happens when no object is fetched (scalar result only)
+                if (isset($this->_rsm->indexByMap['scalars'])) {
+                    $resultKey = $row[$this->_rsm->indexByMap['scalars']];
+                } else {
+                    $resultKey = $this->_resultCounter - 1;
+                }
+            }
+
+            foreach ($scalars as $name => $value) {
+                $result[$resultKey][$name] = $value;
+            }
+        }
+    }
+
+    /**
+     * 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.
+     */
+    private function updateResultPointer(array &$coll, $index, $dqlAlias, $oneToOne)
+    {
+        if ($coll === null) {
+            unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
+
+            return;
+        }
+
+        if ($index !== false) {
+            $this->_resultPointers[$dqlAlias] =& $coll[$index];
+
+            return;
+        }
+
+        if ( ! $coll) {
+            return;
+        }
+
+        if ($oneToOne) {
+            $this->_resultPointers[$dqlAlias] =& $coll;
+
+            return;
+        }
+
+        end($coll);
+        $this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
+
+        return;
+    }
+
+    /**
+     * Retrieve ClassMetadata associated to entity class name.
+     *
+     * @param string $className
+     *
+     * @return \Doctrine\ORM\Mapping\ClassMetadata
+     */
+    private function getClassMetadata($className)
+    {
+        if ( ! isset($this->_ce[$className])) {
+            $this->_ce[$className] = $this->_em->getClassMetadata($className);
+        }
+
+        return $this->_ce[$className];
+    }
+}
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..2acc332
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php
@@ -0,0 +1,56 @@
+.
+ */
+
+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
+     */
+    private $_current = null;
+
+    /**
+     * @param \Doctrine\ORM\Internal\Hydration\AbstractHydrator $hydrator
+     */
+    public function __construct($hydrator)
+    {
+        $this->_hydrator = $hydrator;
+    }
+
+    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..5018281
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php
@@ -0,0 +1,552 @@
+.
+ */
+
+namespace Doctrine\ORM\Internal\Hydration;
+
+use PDO,
+    Doctrine\ORM\Mapping\ClassMetadata,
+    Doctrine\ORM\PersistentCollection,
+    Doctrine\ORM\Query,
+    Doctrine\ORM\Event\LifecycleEventArgs,
+    Doctrine\ORM\Events,
+    Doctrine\Common\Collections\ArrayCollection,
+    Doctrine\Common\Collections\Collection,
+    Doctrine\ORM\UnitOfWork,
+    Doctrine\ORM\Proxy\Proxy;
+
+/**
+ * The ObjectHydrator constructs an object graph out of an SQL result set.
+ *
+ * @since  2.0
+ * @author Roman Borschel 
+ * @author Guilherme Blanco 
+ *
+ * @internal Highly performance-sensitive code.
+ */
+class ObjectHydrator extends AbstractHydrator
+{
+    /* Local ClassMetadata cache to avoid going to the EntityManager all the time.
+     * This local cache is maintained between hydration runs and not cleared.
+     */
+    private $_ce = array();
+
+    /* The following parts are reinitialized on every hydration run. */
+
+    private $_identifierMap;
+    private $_resultPointers;
+    private $_idTemplate;
+    private $_resultCounter;
+    private $_rootAliases = array();
+    private $_initializedCollections = array();
+    private $_existingCollections = array();
+
+
+    /** @override */
+    protected function prepare()
+    {
+        $this->_identifierMap =
+        $this->_resultPointers =
+        $this->_idTemplate = array();
+
+        $this->_resultCounter = 0;
+
+        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]    = '';
+
+            if ( ! isset($this->_ce[$className])) {
+                $this->_ce[$className] = $this->_em->getClassMetadata($className);
+            }
+
+            // 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;
+            }
+
+            if ( ! isset($this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]])) {
+                throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $this->_rsm->parentAliasMap[$dqlAlias]);
+            }
+
+            $sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
+            $sourceClass     = $this->_getClassMetadata($sourceClassName);
+            $assoc           = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
+
+            $this->_hints['fetched'][$this->_rsm->parentAliasMap[$dqlAlias]][$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->_ce[$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->_em->getUnitOfWork()->triggerEagerLoads();
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function hydrateAllData()
+    {
+        $result = array();
+        $cache  = array();
+
+        while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
+            $this->hydrateRowData($row, $cache, $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.
+     */
+    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) {
+            $value = new ArrayCollection;
+        }
+
+        if ( ! $value instanceof PersistentCollection) {
+            $value = new PersistentCollection(
+                $this->_em, $this->_ce[$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.
+     */
+    private function _getEntity(array $data, $dqlAlias)
+    {
+        $className = $this->_rsm->aliasMap[$dqlAlias];
+
+        if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
+
+            if ( ! isset($this->_rsm->metaMappings[$this->_rsm->discriminatorColumns[$dqlAlias]])) {
+                throw HydrationException::missingDiscriminatorMetaMappingColumn($className, $this->_rsm->discriminatorColumns[$dqlAlias], $dqlAlias);
+            }
+
+            $discrColumn = $this->_rsm->metaMappings[$this->_rsm->discriminatorColumns[$dqlAlias]];
+
+            if ( ! isset($data[$discrColumn])) {
+                throw HydrationException::missingDiscriminatorColumn($className, $discrColumn, $dqlAlias);
+            }
+
+            if ($data[$discrColumn] === "") {
+                throw HydrationException::emptyDiscriminatorValue($dqlAlias);
+            }
+
+            $className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]];
+
+            unset($data[$discrColumn]);
+        }
+
+        if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) {
+            $this->registerManaged($this->_ce[$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->_ce[$className];
+
+        /* @var $class ClassMetadata */
+        if ($class->isIdentifierComposite) {
+            $idHash = '';
+            foreach ($class->identifier as $fieldName) {
+                if (isset($class->associationMappings[$fieldName])) {
+                    $idHash .= $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] . ' ';
+                } else {
+                    $idHash .= $data[$fieldName] . ' ';
+                }
+            }
+            return $this->_uow->tryGetByIdHash(rtrim($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);
+        } else {
+            return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName);
+        }
+    }
+
+    /**
+     * Gets a ClassMetadata instance from the local cache.
+     * If the instance is not yet in the local cache, it is loaded into the
+     * local cache.
+     *
+     * @param string $className The name of the class.
+     * @return ClassMetadata
+     */
+    private function _getClassMetadata($className)
+    {
+        if ( ! isset($this->_ce[$className])) {
+            $this->_ce[$className] = $this->_em->getClassMetadata($className);
+        }
+
+        return $this->_ce[$className];
+    }
+
+    /**
+     * 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 $cache  The cache to use.
+     * @param array $result The result array to fill.
+     */
+    protected function hydrateRowData(array $row, array &$cache, 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, $cache, $id, $nonemptyComponents);
+
+        // Extract scalar values. They're appended at the end.
+        if (isset($rowData['scalars'])) {
+            $scalars = $rowData['scalars'];
+
+            unset($rowData['scalars']);
+
+            if (empty($rowData)) {
+                ++$this->_resultCounter;
+            }
+        }
+
+        // Hydrate the data chunks
+        foreach ($rowData 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;
+                }
+
+                // 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, so skip it.
+                    continue;
+                }
+
+                $parentClass = $this->_ce[$this->_rsm->aliasMap[$parentAlias]];
+                $oid = spl_object_hash($parentObject);
+                $relationField = $this->_rsm->relationMap[$dqlAlias];
+                $relation = $parentClass->associationMappings[$relationField];
+                $reflField = $parentClass->reflFields[$relationField];
+
+                // Check the type of the relation (many or single-valued)
+                if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
+                    $reflFieldValue = $reflField->getValue($parentObject);
+                    // PATH A: Collection-valued association
+                    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->_ce[$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($rowData[$dqlAlias], $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 ($this->_rsm->isMixed) {
+                        $result[] = $result[$index];
+                        ++$this->_resultCounter;
+                    }*/
+                }
+            }
+        }
+
+        // Append scalar values to mixed result sets
+        if (isset($scalars)) {
+            if ( ! isset($resultKey) ) {
+                if (isset($this->_rsm->indexByMap['scalars'])) {
+                    $resultKey = $row[$this->_rsm->indexByMap['scalars']];
+                } else {
+                    $resultKey = $this->_resultCounter - 1;
+                }
+            }
+
+            foreach ($scalars as $name => $value) {
+                $result[$resultKey][$name] = $value;
+            }
+        }
+    }
+
+    /**
+     * When executed in a hydrate() loop we may have to clear internal state to
+     * decrease memory consumption.
+     */
+    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..23b0abe
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php
@@ -0,0 +1,55 @@
+.
+ */
+
+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();
+        $cache  = array();
+
+        while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) {
+            $this->hydrateRowData($data, $cache, $result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function hydrateRowData(array $data, array &$cache, array &$result)
+    {
+        $result[] = $this->gatherScalarRowData($data, $cache);
+    }
+}
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..613f94c
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php
@@ -0,0 +1,190 @@
+.
+ */
+
+namespace Doctrine\ORM\Internal\Hydration;
+
+use \PDO,
+    Doctrine\DBAL\Types\Type,
+    Doctrine\ORM\Mapping\ClassMetadata,
+    Doctrine\ORM\Event\LifecycleEventArgs,
+    Doctrine\ORM\Events,
+    Doctrine\ORM\Query;
+
+class SimpleObjectHydrator extends AbstractHydrator
+{
+    /**
+     * @var ClassMetadata
+     */
+    private $class;
+
+    /**
+     * @var array
+     */
+    private $declaringClasses = array();
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function hydrateAllData()
+    {
+        $result = array();
+        $cache = array();
+
+        while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
+            $this->hydrateRowData($row, $cache, $result);
+        }
+
+        $this->_em->getUnitOfWork()->triggerEagerLoads();
+
+        return $result;
+    }
+
+    /**
+     * {@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->_em->getClassMetadata(reset($this->_rsm->aliasMap));
+
+        // We only need to add declaring classes if we have inheritance.
+        if ($this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_NONE) {
+            return;
+        }
+
+        foreach ($this->_rsm->declaringClasses as $column => $class) {
+            $this->declaringClasses[$column] = $this->_em->getClassMetadata($class);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function hydrateRowData(array $sqlResult, array &$cache, 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']);
+
+            if ( ! isset($sqlResult[$discrColumnName])) {
+                throw HydrationException::missingDiscriminatorColumn($entityName, $discrColumnName, key($this->_rsm->aliasMap));
+            }
+
+            if ($sqlResult[$discrColumnName] === '') {
+                throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap));
+            }
+
+            $entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
+
+            unset($sqlResult[$discrColumnName]);
+        }
+
+        foreach ($sqlResult as $column => $value) {
+            // Hydrate column information if not yet present
+            if ( ! isset($cache[$column])) {
+                if (($info = $this->hydrateColumnInfo($entityName, $column)) === null) {
+                    continue;
+                }
+
+                $cache[$column] = $info;
+            }
+
+            // Convert field to a valid PHP value
+            if (isset($cache[$column]['field'])) {
+                $type  = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']);
+                $value = $type->convertToPHPValue($value, $this->_platform);
+            }
+
+            // Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
+            if (isset($cache[$column]) && ( ! isset($data[$cache[$column]['name']]) || $value !== null)) {
+                $data[$cache[$column]['name']] = $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;
+    }
+
+    /**
+     * Retrieve column information form ResultSetMapping.
+     *
+     * @param string $entityName
+     * @param string $column
+     *
+     * @return array
+     */
+    protected function hydrateColumnInfo($entityName, $column)
+    {
+        switch (true) {
+            case (isset($this->_rsm->fieldMappings[$column])):
+                $class = isset($this->declaringClasses[$column])
+                    ? $this->declaringClasses[$column]
+                    : $this->class;
+
+                // If class is not part of the inheritance, ignore
+                if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) {
+                    return null;
+                }
+
+                return array(
+                    'class' => $class,
+                    'name'  => $this->_rsm->fieldMappings[$column],
+                    'field' => true,
+                );
+
+            case (isset($this->_rsm->relationMap[$column])):
+                $class = isset($this->_rsm->relationMap[$column])
+                    ? $this->_rsm->relationMap[$column]
+                    : $this->class;
+
+                // If class is not self referencing, ignore
+                if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) {
+                    return null;
+                }
+
+                // TODO: Decide what to do with associations. It seems original code is incomplete.
+                // One solution is to load the association, but it might require extra efforts.
+                return array('name' => $column);
+
+            case (isset($this->_rsm->metaMappings[$column])):
+                return array(
+                    'name' => $this->_rsm->metaMappings[$column]
+                );
+
+            default:
+                return null;
+        }
+    }
+}
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..a23c4ce
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php
@@ -0,0 +1,56 @@
+.
+ */
+
+namespace Doctrine\ORM\Internal\Hydration;
+
+use Doctrine\DBAL\Connection,
+    Doctrine\ORM\NoResultException,
+    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 || count($data[key($data)]) > 1) {
+            throw new NonUniqueResultException();
+        }
+
+        $cache  = array();
+        $result = $this->gatherScalarRowData($data[key($data)], $cache);
+
+        return array_shift($result);
+    }
+}
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/AssociationOverride.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php
new file mode 100644
index 0000000..8776be6
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php
@@ -0,0 +1,56 @@
+.
+ */
+
+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..b0a0c13
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverrides.php
@@ -0,0 +1,41 @@
+.
+ */
+
+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..ef9e6dd
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverride.php
@@ -0,0 +1,47 @@
+.
+ */
+
+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..41f680d
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverrides.php
@@ -0,0 +1,41 @@
+.
+ */
+
+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..d540774
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php
@@ -0,0 +1,167 @@
+.
+ */
+
+
+namespace Doctrine\ORM\Mapping\Builder;
+
+use Doctrine\ORM\Mapping\ClassMetadata;
+
+class AssociationBuilder
+{
+    /**
+     * @var ClassMetadataBuilder
+     */
+    protected $builder;
+
+    /**
+     * @var array
+     */
+    protected $mapping;
+
+    /**
+     * @var array
+     */
+    protected $joinColumns;
+
+    /**
+     *
+     * @var int
+     */
+    protected $type;
+
+    /**
+     * @param ClassMetadataBuilder $builder
+     * @param array $mapping
+     */
+    public function __construct(ClassMetadataBuilder $builder, array $mapping, $type)
+    {
+        $this->builder = $builder;
+        $this->mapping = $mapping;
+        $this->type = $type;
+    }
+
+    public function mappedBy($fieldName)
+    {
+        $this->mapping['mappedBy'] = $fieldName;
+        return $this;
+    }
+
+    public function inversedBy($fieldName)
+    {
+        $this->mapping['inversedBy'] = $fieldName;
+        return $this;
+    }
+
+    public function cascadeAll()
+    {
+        $this->mapping['cascade'] = array("ALL");
+        return $this;
+    }
+
+    public function cascadePersist()
+    {
+        $this->mapping['cascade'][] = "persist";
+        return $this;
+    }
+
+    public function cascadeRemove()
+    {
+        $this->mapping['cascade'][] = "remove";
+        return $this;
+    }
+
+    public function cascadeMerge()
+    {
+        $this->mapping['cascade'][] = "merge";
+        return $this;
+    }
+
+    public function cascadeDetach()
+    {
+        $this->mapping['cascade'][] = "detach";
+        return $this;
+    }
+
+    public function cascadeRefresh()
+    {
+        $this->mapping['cascade'][] = "refresh";
+        return $this;
+    }
+
+    public function fetchExtraLazy()
+    {
+        $this->mapping['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY;
+        return $this;
+    }
+
+    public function fetchEager()
+    {
+        $this->mapping['fetch'] = ClassMetadata::FETCH_EAGER;
+        return $this;
+    }
+
+    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 $onDelete
+     * @param string $columnDef
+     */
+    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;
+    }
+
+    /**
+     * @return ClassMetadataBuilder
+     */
+    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 Assocation 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..fb7bbc4
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php
@@ -0,0 +1,470 @@
+.
+ */
+
+namespace Doctrine\ORM\Mapping\Builder;
+
+use Doctrine\ORM\Mapping\ClassMetadata,
+    Doctrine\ORM\Mapping\ClassMetadataInfo;
+
+/**
+ * Builder Object for ClassMetadata
+ *
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @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;
+    }
+
+    /**
+     * Mark the class as mapped superclass.
+     *
+     * @return ClassMetadataBuilder
+     */
+    public function setMappedSuperClass()
+    {
+        $this->cm->isMappedSuperclass = true;
+
+        return $this;
+    }
+
+    /**
+     * Set custom Repository class name
+     *
+     * @param string $repositoryClassName
+     * @return ClassMetadataBuilder
+     */
+    public function setCustomRepositoryClass($repositoryClassName)
+    {
+        $this->cm->setCustomRepositoryClass($repositoryClassName);
+
+        return $this;
+    }
+
+    /**
+     * Mark class read only
+     *
+     * @return ClassMetadataBuilder
+     */
+    public function setReadOnly()
+    {
+        $this->cm->markReadOnly();
+
+        return $this;
+    }
+
+    /**
+     * Set the table name
+     *
+     * @param string $name
+     * @return ClassMetadataBuilder
+     */
+    public function setTable($name)
+    {
+        $this->cm->setPrimaryTable(array('name' => $name));
+
+        return $this;
+    }
+
+    /**
+     * Add 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;
+    }
+
+    /**
+     * Add 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;
+    }
+
+    /**
+     * Add 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;
+    }
+
+    /**
+     * Set class as root of a joined table inheritance hierachy.
+     *
+     * @return ClassMetadataBuilder
+     */
+    public function setJoinedTableInheritance()
+    {
+        $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED);
+
+        return $this;
+    }
+
+    /**
+     * Set class as root of a single table inheritance hierachy.
+     *
+     * @return ClassMetadataBuilder
+     */
+    public function setSingleTableInheritance()
+    {
+        $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);
+
+        return $this;
+    }
+
+    /**
+     * Set the discriminator column details.
+     *
+     * @param string $name
+     * @param string $type
+     */
+    public function setDiscriminatorColumn($name, $type = 'string', $length = 255)
+    {
+        $this->cm->setDiscriminatorColumn(array(
+            'name' => $name,
+            'type' => $type,
+            'length' => $length,
+        ));
+
+        return $this;
+    }
+
+    /**
+     * Add a subclass to this inheritance hierachy.
+     *
+     * @param string $name
+     * @param string $class
+     * @return ClassMetadataBuilder
+     */
+    public function addDiscriminatorMapClass($name, $class)
+    {
+        $this->cm->addDiscriminatorMapClass($name, $class);
+
+        return $this;
+    }
+
+    /**
+     * Set deferred explicit change tracking policy.
+     *
+     * @return ClassMetadataBuilder
+     */
+    public function setChangeTrackingPolicyDeferredExplicit()
+    {
+        $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT);
+
+        return $this;
+    }
+
+    /**
+     * Set notify change tracking policy.
+     *
+     * @return ClassMetadataBuilder
+     */
+    public function setChangeTrackingPolicyNotify()
+    {
+        $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_NOTIFY);
+
+        return $this;
+    }
+
+    /**
+     * Add lifecycle event
+     *
+     * @param string $methodName
+     * @param string $event
+     * @return ClassMetadataBuilder
+     */
+    public function addLifecycleEvent($methodName, $event)
+    {
+        $this->cm->addLifecycleCallback($methodName, $event);
+
+        return $this;
+    }
+
+    /**
+     * Add Field
+     *
+     * @param string $name
+     * @param string $type
+     * @param array $mapping
+     */
+    public function addField($name, $type, array $mapping = array())
+    {
+        $mapping['fieldName'] = $name;
+        $mapping['type'] = $type;
+
+        $this->cm->mapField($mapping);
+
+        return $this;
+    }
+
+    /**
+     * Create 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
+            )
+        );
+    }
+
+    /**
+     * Add 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();
+    }
+
+    /**
+     * Create a ManyToOne Assocation 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
+        );
+    }
+
+    /**
+     * Create OneToOne Assocation 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
+        );
+    }
+
+    /**
+     * Add simple inverse one-to-one assocation.
+     *
+     * @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();
+    }
+
+    /**
+     * Add simple owning one-to-one assocation.
+     *
+     * @param string $name
+     * @param string $targetEntity
+     * @param string $inversedBy
+     * @return ClassMetadataBuilder
+     */
+    public function addOwningOneToOne($name, $targetEntity, $inversedBy = null)
+    {
+        $builder = $this->createOneToOne($name, $targetEntity);
+
+        if ($inversedBy) {
+            $builder->inversedBy($inversedBy);
+        }
+
+        return $builder->build();
+    }
+
+    /**
+     * Create ManyToMany Assocation 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
+        );
+    }
+
+    /**
+     * Add a simple owning many to many assocation.
+     *
+     * @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();
+    }
+
+    /**
+     * Add a simple inverse many to many assocation.
+     *
+     * @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();
+    }
+
+    /**
+     * Create a one to many assocation 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
+        );
+    }
+
+    /**
+     * Add simple OneToMany assocation.
+     *
+     * @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/FieldBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php
new file mode 100644
index 0000000..49dd54c
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php
@@ -0,0 +1,223 @@
+.
+ */
+
+
+namespace Doctrine\ORM\Mapping\Builder;
+
+/**
+ * Field Builder
+ *
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @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;
+    }
+
+    /**
+     * Set length.
+     *
+     * @param int $length
+     * @return FieldBuilder
+     */
+    public function length($length)
+    {
+        $this->mapping['length'] = $length;
+        return $this;
+    }
+
+    /**
+     * Set nullable
+     *
+     * @param bool
+     * @return FieldBuilder
+     */
+    public function nullable($flag = true)
+    {
+        $this->mapping['nullable'] = (bool)$flag;
+        return $this;
+    }
+
+    /**
+     * Set Unique
+     *
+     * @param bool
+     * @return FieldBuilder
+     */
+    public function unique($flag = true)
+    {
+        $this->mapping['unique'] = (bool)$flag;
+        return $this;
+    }
+
+    /**
+     * Set column name
+     *
+     * @param string $name
+     * @return FieldBuilder
+     */
+    public function columnName($name)
+    {
+        $this->mapping['columnName'] = $name;
+        return $this;
+    }
+
+    /**
+     * Set Precision
+     *
+     * @param  int $p
+     * @return FieldBuilder
+     */
+    public function precision($p)
+    {
+        $this->mapping['precision'] = $p;
+        return $this;
+    }
+
+    /**
+     * Set scale.
+     *
+     * @param int $s
+     * @return FieldBuilder
+     */
+    public function scale($s)
+    {
+        $this->mapping['scale'] = $s;
+        return $this;
+    }
+
+    /**
+     * Set field as primary key.
+     *
+     * @return FieldBuilder
+     */
+    public function isPrimaryKey()
+    {
+        $this->mapping['id'] = true;
+        return $this;
+    }
+
+    /**
+     * @param  int $strategy
+     * @return FieldBuilder
+     */
+    public function generatedValue($strategy = 'AUTO')
+    {
+        $this->generatedValue = $strategy;
+        return $this;
+    }
+
+    /**
+     * Set field versioned
+     *
+     * @return FieldBuilder
+     */
+    public function isVersionField()
+    {
+        $this->version = true;
+        return $this;
+    }
+
+    /**
+     * Set 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;
+    }
+
+    /**
+     * Set column definition.
+     *
+     * @param string $def
+     * @return FieldBuilder
+     */
+    public function columnDefinition($def)
+    {
+        $this->mapping['columnDefinition'] = $def;
+        return $this;
+    }
+
+    /**
+     * Finalize 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..17edeea
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php
@@ -0,0 +1,86 @@
+.
+ */
+
+
+namespace Doctrine\ORM\Mapping\Builder;
+
+/**
+ * ManyToMany Association Builder
+ *
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link        www.doctrine-project.com
+ * @since       2.0
+ * @author      Benjamin Eberlei 
+ */
+class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder
+{
+    private $joinTableName;
+
+    private $inverseJoinColumns = array();
+
+    public function setJoinTable($name)
+    {
+        $this->joinTableName = $name;
+        return $this;
+    }
+
+    /**
+     * Add Inverse Join Columns
+     *
+     * @param string $columnName
+     * @param string $referencedColumnName
+     * @param bool $nullable
+     * @param bool $unique
+     * @param string $onDelete
+     * @param string $columnDef
+     */
+    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..6f22fe2
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php
@@ -0,0 +1,62 @@
+.
+ */
+
+
+namespace Doctrine\ORM\Mapping\Builder;
+
+/**
+ * OneToMany Association Builder
+ *
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @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;
+    }
+
+    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/ChangeTrackingPolicy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php
new file mode 100644
index 0000000..bab34a6
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php
@@ -0,0 +1,30 @@
+.
+ */
+
+namespace Doctrine\ORM\Mapping;
+
+/**
+ * @Annotation
+ * @Target("CLASS")
+ */
+final class ChangeTrackingPolicy implements Annotation
+{
+    /** @var string */
+    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..bf449a4
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
@@ -0,0 +1,543 @@
+.
+ */
+
+namespace Doctrine\ORM\Mapping;
+
+use ReflectionException;
+use Doctrine\ORM\ORMException;
+use Doctrine\ORM\EntityManager;
+use Doctrine\DBAL\Platforms;
+use Doctrine\ORM\Events;
+use Doctrine\Common\Persistence\Mapping\ReflectionService;
+use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
+use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory;
+use Doctrine\ORM\Id\IdentityGenerator;
+use Doctrine\ORM\Id\BigIntegerIdentityGenerator;
+use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
+
+/**
+ * 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 EntityManager
+     */
+    private $em;
+
+    /**
+     * @var \Doctrine\DBAL\Platforms\AbstractPlatform
+     */
+    private $targetPlatform;
+
+    /**
+     * @var \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver
+     */
+    private $driver;
+
+    /**
+     * @var \Doctrine\Common\EventManager
+     */
+    private $evm;
+
+    /**
+     * @param EntityManager $em
+     */
+    public function setEntityManager(EntityManager $em)
+    {
+        $this->em = $em;
+    }
+
+    /**
+     * {@inheritDoc}.
+     */
+    protected function initialize()
+    {
+        $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
+        $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
+        $this->evm = $this->em->getEventManager();
+        $this->initialized = true;
+    }
+
+    /**
+     * {@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);
+            $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 ($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 ($parent && $parent->isInheritanceTypeSingleTable()) {
+            $class->setPrimaryTable($parent->table);
+        }
+
+        if ($parent && $parent->containsForeignIdentifier) {
+            $class->containsForeignIdentifier = true;
+        }
+
+        if ($parent && !empty($parent->namedQueries)) {
+            $this->addInheritedNamedQueries($class, $parent);
+        }
+
+        if ($parent && !empty($parent->namedNativeQueries)) {
+            $this->addInheritedNamedNativeQueries($class, $parent);
+        }
+
+        if ($parent && !empty($parent->sqlResultSetMappings)) {
+            $this->addInheritedSqlResultSetMappings($class, $parent);
+        }
+
+        $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->wakeupReflection($class, $this->getReflectionService());
+        $this->validateRuntimeMetadata($class, $parent);
+    }
+
+    /**
+     * Validate runtime metadata is correctly defined.
+     *
+     * @param ClassMetadata $class
+     * @param $parent
+     * @throws MappingException
+     */
+    protected function validateRuntimeMetadata($class, $parent)
+    {
+        if ( ! $class->reflClass ) {
+            // only validate if there is a reflection class instance
+            return;
+        }
+
+        $class->validateIdentifier();
+        $class->validateAssocations();
+        $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 ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) {
+                // enforce discriminator map for all entities of an inheritance hierarchy, otherwise problems will occur.
+                throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
+            }
+        } 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());
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Get 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
+     */
+    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
+     * @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::illegalToManyAssocationOnMappedSuperclass($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);
+        }
+    }
+
+    /**
+     * Adds inherited named queries to the subclass mapping.
+     *
+     * @since 2.2
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
+     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
+     */
+    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
+     */
+    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
+     */
+    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
+     * @throws ORMException
+     */
+    private function completeIdGeneratorMapping(ClassMetadataInfo $class)
+    {
+        $idGenType = $class->generatorType;
+        if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) {
+            if ($this->targetPlatform->prefersSequences()) {
+                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE);
+            } else if ($this->targetPlatform->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:
+                // For PostgreSQL IDENTITY (SERIAL) we need a sequence name. It defaults to
+                // __seq in PostgreSQL for SERIAL columns.
+                // Not pretty but necessary and the simplest solution that currently works.
+                $sequenceName = null;
+                $fieldName    = $class->identifier ? $class->getSingleIdentifierFieldName() : null;
+
+                if ($this->targetPlatform instanceof Platforms\PostgreSQLPlatform) {
+                    $columnName     = $class->getSingleIdentifierColumnName();
+                    $quoted         = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
+                    $sequenceName   = $class->getTableName() . '_' . $columnName . '_seq';
+                    $definition     = array(
+                        'sequenceName' => $this->targetPlatform->fixSchemaElementName($sequenceName)
+                    );
+
+                    if ($quoted) {
+                        $definition['quoted'] = true;
+                    }
+
+                    $sequenceName = $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->targetPlatform);
+                }
+
+                $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();
+                    $columnName     = $class->getSingleIdentifierColumnName();
+                    $quoted         = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
+                    $sequenceName   = $class->getTableName() . '_' . $columnName . '_seq';
+                    $definition     = array(
+                        'sequenceName'      => $this->targetPlatform->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->targetPlatform),
+                    $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;
+    }
+}
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..cba525a
--- /dev/null
+++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
@@ -0,0 +1,2820 @@
+.
+ */
+
+namespace Doctrine\ORM\Mapping;
+
+use BadMethodCallException;
+use InvalidArgumentException;
+use RuntimeException;
+use Doctrine\DBAL\Types\Type;
+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 it's 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;
+
+    /**
+     * READ-ONLY: The name of the entity class.
+     */
+    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: 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 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'   => 
+     * )
+     * 
+ */ + 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()
+     * )
+     * 
+ */ + 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 string + */ + 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 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 wheather 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 $isVersioned + */ + public $isVersioned; + + /** + * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any). + * + * @var mixed $versionField + */ + public $versionField; + + /** + * 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 array + */ + public $reflFields = array(); + + /** + * The prototype from which new instances of the mapped class are created. + * + * @var object + */ + private $_prototype; + + /** + * 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 $namingStrategy + */ + public function __construct($entityName, NamingStrategy $namingStrategy = null) + { + $this->name = $entityName; + $this->rootEntityName = $entityName; + $this->namingStrategy = $namingStrategy ?: new DefaultNamingStrategy(); + } + + /** + * Gets the ReflectionPropertys 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; + } + + $value = $this->reflFields[$this->identifier[0]]->getValue($entity); + + if ($value !== null) { + return array($this->identifier[0] => $value); + } + + return array(); + } + + /** + * Populates the entity identifier of an entity. + * + * @param object $entity + * @param mixed $id + * @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 + */ + 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 + */ + 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', + '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->containsForeignIdentifier) { + $serialized[] = 'containsForeignIdentifier'; + } + + if ($this->isVersioned) { + $serialized[] = 'isVersioned'; + $serialized[] = 'versionField'; + } + + if ($this->lifecycleCallbacks) { + $serialized[] = 'lifecycleCallbacks'; + } + + 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"; + } + + return $serialized; + } + + /** + * Creates a new instance of the mapped class, without invoking the constructor. + * + * @return object + */ + public function newInstance() + { + if ($this->_prototype === null) { + $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name)); + } + + return clone $this->_prototype; + } + /** + * 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); + + foreach ($this->fieldMappings as $field => $mapping) { + $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. + */ + 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); + } + + /** + * Validate Identifier + * + * @throws MappingException + * @return void + */ + public function validateIdentifier() + { + // Verify & complete identifier mapping + if ( ! $this->identifier && ! $this->isMappedSuperclass) { + throw MappingException::identifierRequired($this->name); + } + + if ($this->usesIdGenerator() && $this->isIdentifierComposite) { + throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name); + } + } + + /** + * Validate association targets actually exist. + * + * @throws MappingException + * @return void + */ + public function validateAssocations() + { + foreach ($this->associationMappings as $mapping) { + if ( ! ClassLoader::classExists($mapping['targetEntity']) ) { + throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']); + } + } + } + + /** + * Validate lifecycle callbacks + * + * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService + * @throws MappingException + * @return void + */ + 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; + } + + /** + * Sets the change tracking policy used by this class. + * + * @param integer $policy + */ + 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->isIdentifierComposite) { + return $fieldName === $this->identifier[0]; + } + return in_array($fieldName, $this->identifier); + } + + /** + * Check 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; + } + + /** + * Check 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. + * @throws MappingException + * @return array The field mapping. + */ + 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. + * @throws MappingException + * @return array The mapping. + */ + 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 column name + * @return string column alias + */ + public function getFieldName($columnName) + { + return isset($this->fieldNames[$columnName]) ? + $this->fieldNames[$columnName] : $columnName; + } + + /** + * Gets the named query. + * + * @see ClassMetadataInfo::$namedQueries + * @throws MappingException + * @param string $queryName The query name + * @return string + */ + 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 + * @throws MappingException + * @param string $queryName The query name + * @return array + */ + 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 + * @throws MappingException + * @param string $name The result set mapping name + * @return array + */ + 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 validated & complete. + * @throws MappingException + * @return array The validated and completed field mapping. + */ + 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']); + } + + 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'])) { + if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) { + $mapping['targetEntity'] = $this->namespace . '\\' . $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 (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::illegalToManyIdentifierAssoaction($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. + * @throws RuntimeException + * @throws MappingException + * @return array The validated & completed mapping.@override + */ + 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']), + 'referencedColumnName' => $this->namingStrategy->referenceColumnName() + )); + } + + $uniqueContraintColumns = 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 { + $uniqueContraintColumns[] = $joinColumn['name']; + } + } + + if (empty($joinColumn['name'])) { + $joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName']); + } + + 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 ($uniqueContraintColumns) { + 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' => $uniqueContraintColumns + ); + } + + $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::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']); + } + + return $mapping; + } + + /** + * Validates and completes the mapping. + * + * @param array $mapping The mapping to validate and complete. + * @throws MappingException + * @throws InvalidArgumentException + * @return array The validated and completed mapping.@override + */ + 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; + } + + 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']); + } + if ( ! isset($mapping['joinTable']['joinColumns'])) { + $mapping['joinTable']['joinColumns'] = array(array( + 'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity']), + 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), + 'onDelete' => 'CASCADE')); + } + if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { + $mapping['joinTable']['inverseJoinColumns'] = array(array( + 'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity']), + '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 + */ + public function setIdentifier(array $identifier) + { + $this->identifier = $identifier; + $this->isIdentifierComposite = (count($this->identifier) > 1); + } + + /** + * Gets the mapped identifier field of this class. + * + * @return array|string $identifier + */ + 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 $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. + */ + 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 + */ + 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 + */ + 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 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. + */ + public function setSubclasses(array $subclasses) + { + foreach ($subclasses as $subclass) { + if (strpos($subclass, '\\') === false && strlen($this->namespace)) { + $this->subClasses[] = $this->namespace . '\\' . $subclass; + } else { + $this->subClasses[] = $subclass; + } + } + } + + /** + * Sets the parent class names. + * Assumes that the class names in the passed array are in the order: + * directParent -> directParentParent -> directParentParentParent ... -> root. + */ + 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 it's subclasses. + * + * @param integer $type + * @throws MappingException + * @return void + */ + 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 + * @throws MappingException + * @return void + */ + 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 + * @throws MappingException + * @param array $overrideMapping + * @return void + */ + 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']); + } + + /** + * Check if this entity is the root in any entity-inheritance-hierachy. + * + * @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']); + } + + /** + * Sets the name of the primary table the class is mapped to. + * + * @param string $tableName The table name. + * @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. + */ + public function setPrimaryTable(array $table) + { + if (isset($table['name'])) { + if ($table['name'][0] === '`') { + $table['name'] = trim($table['name'], '`'); + $this->table['quoted'] = true; + } + + $this->table['name'] = $table['name']; + } + + 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. + * @throws MappingException + * @return void + */ + public function mapField(array $mapping) + { + $this->_validateAndCompleteFieldMapping($mapping); + if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) { + throw MappingException::duplicateFieldMapping($this->name, $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 + * @throws MappingException + * @return void + */ + 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. + * + * @throws MappingException + * @param array $queryMapping + */ + 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. + * + * @throws MappingException + * @param array $queryMapping + */ + 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; + + } else if (strlen($this->namespace) > 0 && strpos($queryMapping['resultClass'], '\\') === false) { + $queryMapping['resultClass'] = $this->namespace . '\\' . $queryMapping['resultClass']; + } + + $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\'); + } + + $this->namedNativeQueries[$queryMapping['name']] = $queryMapping; + } + + /** + * INTERNAL: + * Adds a sql result set mapping to this class. + * + * @throws MappingException + * @param array $resultMapping + */ + 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; + + } else if (strlen($this->namespace) > 0 && strpos($entityResult['entityClass'], '\\') === false) { + $entityResult['entityClass'] = $this->namespace . '\\' . $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. + */ + 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. + */ + 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. + */ + 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. + */ + 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 + * @throws MappingException + * @return void + */ + protected function _storeAssociationMapping(array $assocMapping) + { + $sourceFieldName = $assocMapping['fieldName']; + + if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) { + throw MappingException::duplicateFieldMapping($this->name, $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) + { + if ($repositoryClassName !== null && strpos($repositoryClassName, '\\') === false + && strlen($this->namespace) > 0) { + $repositoryClassName = $this->namespace . '\\' . $repositoryClassName; + } + $this->customRepositoryClassName = $repositoryClassName; + } + + /** + * Dispatches the lifecycle event of the given entity to the registered + * lifecycle callbacks and lifecycle listeners. + * + * @param string $lifecycleEvent The lifecycle event. + * @param \Object $entity The Entity on which the event occured. + */ + 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 + */ + public function addLifecycleCallback($callback, $event) + { + $this->lifecycleCallbacks[$event][] = $callback; + } + + /** + * Sets the lifecycle callbacks for entities of this class. + * Any previously registered callbacks are overwritten. + * + * @param array $callbacks + */ + public function setLifecycleCallbacks(array $callbacks) + { + $this->lifecycleCallbacks = $callbacks; + } + + /** + * Sets the discriminator column definition. + * + * @param array $columnDef + * + * @param $columnDef + * @throws MappingException + * @return void + * @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 + */ + public function setDiscriminatorMap(array $map) + { + foreach ($map as $value => $className) { + $this->addDiscriminatorMapClass($value, $className); + } + } + + /** + * Add one entry of the discriminator map with a new class and corresponding name. + * + * @param string $name + * @param string $className + * @throws MappingException + * @return void + */ + public function addDiscriminatorMapClass($name, $className) + { + if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) { + $className = $this->namespace . '\\' . $className; + } + + $className = ltrim($className, '\\'); + $this->discriminatorMap[$name] = $className; + + if ($this->name == $className) { + $this->discriminatorValue = $name; + } else { + if ( ! class_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]) + ); + } + + /** + * Return the single association join column (if any). + * + * @param string $fieldName + * @throws MappingException + * @return string + */ + public function getSingleAssociationJoinColumnName($fieldName) + { + if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) { + throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); + } + return $this->associationMappings[$fieldName]['joinColumns'][0]['name']; + } + + /** + * Return the single association referenced join column name (if any). + * + * @param string $fieldName + * @throws MappingException + * @return string + */ + 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 + * @throws MappingException + * @return string + */ + 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 + */ + public function setIdGenerator($generator) + { + $this->idGenerator = $generator; + } + + /** + * Sets definition + * @param array $definition + */ + 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 + */ + public function setSequenceGeneratorDefinition(array $definition) + { + if (isset($definition['name']) && $definition['name'] == '`') { + $definition['name'] = trim($definition['name'], '`'); + $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 + * @throws MappingException + * @return void + */ + 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 + */ + 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 + */ + public function setVersionField($versionField) + { + $this->versionField = $versionField; + } + + /** + * Mark 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; + } +} 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..b233566 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php @@ -0,0 +1,46 @@ +. + */ + +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; + /** @var integer */ + public $precision = 0; // The precision for a decimal (exact numeric) column (Applies only for decimal column) + /** @var integer */ + public $scale = 0; // The scale for a decimal (exact numeric) column (Applies only for decimal column) + /** @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..cf5c2b4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ColumnResult.php @@ -0,0 +1,42 @@ +. + */ + +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..f31f082 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php @@ -0,0 +1,30 @@ +. + */ + +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/DefaultNamingStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php new file mode 100644 index 0000000..4c64385 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php @@ -0,0 +1,86 @@ +. + */ + +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) + { + return $propertyName; + } + + /** + * {@inheritdoc} + */ + public function referenceColumnName() + { + return 'id'; + } + + /** + * {@inheritdoc} + */ + public function joinColumnName($propertyName) + { + 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..b5a6fc4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php @@ -0,0 +1,140 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\ORM\Mapping\ClassMetadata; +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} + */ + public function getTableName(ClassMetadata $class, AbstractPlatform $platform) + { + return isset($class->table['quoted']) + ? $platform->quoteIdentifier($class->table['name']) + : $class->table['name']; + } + + /** + * {@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) + { + return isset($association['joinTable']['quoted']) + ? $platform->quoteIdentifier($association['joinTable']['name']) + : $association['joinTable']['name']; + } + + /** + * {@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) + { + // 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. + // And strip non alphanumeric characters + $columnName = $columnName . $counter; + $columnName = substr($columnName, -$platform->getMaxIdentifierLength()); + $columnName = preg_replace('/[^A-Za-z0-9_]/', '', $columnName); + + return $platform->getSQLResultCasing($columnName); + } + +} \ No newline at end of file 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..f5cb077 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class DiscriminatorColumn implements Annotation +{ + /** @var string */ + public $name; + /** @var string */ + public $type; + /** @var integer */ + public $length; + /** @var mixed */ + public $fieldName; // field name used in non-object hydration (array/scalar) + /** @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..d68b85b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php @@ -0,0 +1,30 @@ +. + */ + +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..bb01896 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -0,0 +1,555 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Annotations\AnnotationReader, + Doctrine\ORM\Mapping\MappingException, + Doctrine\ORM\Mapping\JoinColumn, + Doctrine\ORM\Mapping\Column, + Doctrine\Common\Persistence\Mapping\ClassMetadata, + Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver; + +/** + * 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 { + 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->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->name)) { + $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint; + } else { + $primaryTable['uniqueConstraints'][] = $uniqueConstraint; + } + } + } + + if ($tableAnnot->options !== null) { + $primaryTable['options'] = $tableAnnot->options; + } + + $metadata->setPrimaryTable($primaryTable); + } + + // 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)) { + continue; + } + + $mapping = array(); + $mapping['fieldName'] = $property->getName(); + + // Check for JoinColummn/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); + } + } + + // 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 JoinColummn/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) { + $joinTable = null; + $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 @HasLifecycleCallbacks annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) { + /* @var $method \ReflectionMethod */ + foreach ($class->getMethods() as $method) { + // filter for the declaring class only, callbacks from parents will already be registered. + if ($method->isPublic() && $method->getDeclaringClass()->getName() == $class->name) { + $annotations = $this->reader->getMethodAnnotations($method); + + if ($annotations) { + foreach ($annotations as $key => $annot) { + if ( ! is_numeric($key)) { + continue; + } + $annotations[get_class($annot)] = $annot; + } + } + + if (isset($annotations['Doctrine\ORM\Mapping\PrePersist'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::prePersist); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PostPersist'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postPersist); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PreUpdate'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preUpdate); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PostUpdate'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postUpdate); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PreRemove'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preRemove); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PostRemove'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postRemove); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PostLoad'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postLoad); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PreFlush'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preFlush); + } + } + } + } + } + + /** + * 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); + } + + /** + * 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 $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..cb5aa22 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -0,0 +1,416 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\DBAL\Schema\AbstractSchemaManager, + Doctrine\DBAL\Schema\SchemaException, + Doctrine\Common\Persistence\Mapping\Driver\MappingDriver, + Doctrine\Common\Persistence\Mapping\ClassMetadata, + Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\Common\Util\Inflector, + 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 + */ + private $tables = null; + + 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 + */ + private $namespace; + + /** + * + * @param AbstractSchemaManager $schemaManager + */ + public function __construct(AbstractSchemaManager $schemaManager) + { + $this->_sm = $schemaManager; + } + + /** + * Set tables manually instead of relying on the reverse engeneering 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; + } + } + + 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) { + /* @var $table \Doctrine\DBAL\Schema\Table */ + if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) { + $foreignKeys = $table->getForeignKeys(); + } else { + $foreignKeys = 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; + } + } + } + + /** + * {@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; + + $columns = $this->tables[$tableName]->getColumns(); + $indexes = $this->tables[$tableName]->getIndexes(); + try { + $primaryKeyColumns = $this->tables[$tableName]->getPrimaryKey()->getColumns(); + } catch(SchemaException $e) { + $primaryKeyColumns = array(); + } + + if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) { + $foreignKeys = $this->tables[$tableName]->getForeignKeys(); + } else { + $foreignKeys = array(); + } + + $allForeignKeyColumns = array(); + foreach ($foreignKeys as $foreignKey) { + $allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns()); + } + + $ids = array(); + $fieldMappings = array(); + foreach ($columns as $column) { + $fieldMapping = array(); + + if (in_array($column->getName(), $allForeignKeyColumns)) { + continue; + } else if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) { + $fieldMapping['id'] = true; + } + + $fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false); + $fieldMapping['columnName'] = $column->getName(); + $fieldMapping['type'] = strtolower((string) $column->getType()); + + if ($column->getType() instanceof \Doctrine\DBAL\Types\StringType) { + $fieldMapping['length'] = $column->getLength(); + $fieldMapping['fixed'] = $column->getFixed(); + } else if ($column->getType() instanceof \Doctrine\DBAL\Types\IntegerType) { + $fieldMapping['unsigned'] = $column->getUnsigned(); + } + $fieldMapping['nullable'] = $column->getNotNull() ? false : true; + + if (isset($fieldMapping['id'])) { + $ids[] = $fieldMapping; + } else { + $fieldMappings[] = $fieldMapping; + } + } + + if ($ids) { + // We need to check for the columns here, because we might have associations as id as well. + if (count($primaryKeyColumns) == 1) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } + + foreach ($ids as $id) { + $metadata->mapField($id); + } + } + + foreach ($fieldMappings as $fieldMapping) { + $metadata->mapField($fieldMapping); + } + + 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())) { + $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 engeneering. + 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; + } + } + } + + foreach ($foreignKeys as $foreignKey) { + $foreignTable = $foreignKey->getForeignTableName(); + $cols = $foreignKey->getColumns(); + $fkCols = $foreignKey->getForeignColumns(); + + $localColumn = current($cols); + $associationMapping = array(); + $associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true); + $associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable); + + if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) { + $associationMapping['fieldName'] = $associationMapping['fieldName'] . "2"; + } + + if ($primaryKeyColumns && in_array($localColumn, $primaryKeyColumns)) { + $associationMapping['id'] = true; + } + + for ($i = 0; $i < count($cols); $i++) { + $associationMapping['joinColumns'][] = array( + 'name' => $cols[$i], + 'referencedColumnName' => $fkCols[$i], + ); + } + + //Here we need to check if $cols are the same as $primaryKeyColums + if (!array_diff($cols,$primaryKeyColumns)) { + $metadata->mapOneToOne($associationMapping); + } else { + $metadata->mapManyToOne($associationMapping); + } + } + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + $this->reverseEngineerMappingFromDatabase(); + + return array_keys($this->classToTableNames); + } + + /** + * Set class name for a table. + * + * @param string $tableName + * @param string $className + * @return void + */ + public function setClassNameForTable($tableName, $className) + { + $this->classNamesForTables[$tableName] = $className; + } + + /** + * Set 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; + } + + /** + * Return 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); + } + + /** + * Set the namespace for the generated entities. + * + * @param string $namespace + * @return void + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace; + } +} 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..04bf2de --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -0,0 +1,66 @@ +. + */ + +require_once __DIR__.'/../Annotation.php'; +require_once __DIR__.'/../Entity.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__.'/../ElementCollection.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'; 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..02d409d --- /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 +{ +} \ No newline at end of file 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..3d60447 --- /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 +{ +} \ No newline at end of file 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..6d53f77 --- /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 +{ +} \ No newline at end of file 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..0be7c36 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -0,0 +1,731 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use SimpleXMLElement, + Doctrine\Common\Persistence\Mapping\Driver\FileDriver, + Doctrine\Common\Persistence\Mapping\ClassMetadata, + Doctrine\ORM\Mapping\MappingException; + +/** + * XmlDriver is a metadata driver that enables mapping through XML files. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate attributes + $table = array(); + if (isset($xmlRoot['table'])) { + $table['name'] = (string)$xmlRoot['table']; + } + + $metadata->setPrimaryTable($table); + + // 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 + )); + } + } + + /* not implemented specially anyway. use table = schema.table + if (isset($xmlRoot['schema'])) { + $metadata->table['schema'] = (string)$xmlRoot['schema']; + }*/ + + 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 $index) { + $columns = explode(',', (string)$index['columns']); + + if (isset($index['name'])) { + $metadata->table['indexes'][(string)$index['name']] = array( + 'columns' => $columns + ); + } else { + $metadata->table['indexes'][] = array( + 'columns' => $columns + ); + } + } + } + + // Evaluate + if (isset($xmlRoot->{'unique-constraints'})) { + $metadata->table['uniqueConstraints'] = array(); + foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $unique) { + $columns = explode(',', (string)$unique['columns']); + + if (isset($unique['name'])) { + $metadata->table['uniqueConstraints'][(string)$unique['name']] = array( + 'columns' => $columns + ); + } else { + $metadata->table['uniqueConstraints'][] = array( + 'columns' => $columns + ); + } + } + } + + if (isset($xmlRoot->options)) { + $metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children()); + } + + // The mapping assignement 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); + } + } + + 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']; + } + + $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 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 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 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 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'])); + } + } + } + + /** + * 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; + } + + /** + * Parse 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; + } + + /** + * 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($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; + } + } + + return $result; + } + + protected function evaluateBoolean($element) + { + $flag = (string)$element; + + return ($flag === true || $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..f339045 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -0,0 +1,688 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata, + Doctrine\Common\Persistence\Mapping\Driver\FileDriver, + Doctrine\ORM\Mapping\MappingException, + 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 { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate root level properties + $table = array(); + if (isset($element['table'])) { + $table['name'] = $element['table']; + } + $metadata->setPrimaryTable($table); + + // 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 + )); + } + } + + /* not implemented specially anyway. use table = schema.table + if (isset($element['schema'])) { + $metadata->table['schema'] = $element['schema']; + }*/ + + 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 => $index) { + if ( ! isset($index['name'])) { + $index['name'] = $name; + } + + if (is_string($index['columns'])) { + $columns = explode(',', $index['columns']); + $columns = array_map('trim', $columns); + } else { + $columns = $index['columns']; + } + + $metadata->table['indexes'][$index['name']] = array( + 'columns' => $columns + ); + } + } + + // Evaluate uniqueConstraints + if (isset($element['uniqueConstraints'])) { + foreach ($element['uniqueConstraints'] as $name => $unique) { + if ( ! isset($unique['name'])) { + $unique['name'] = $name; + } + + if (is_string($unique['columns'])) { + $columns = explode(',', $unique['columns']); + $columns = array_map('trim', $columns); + } else { + $columns = $unique['columns']; + } + + $metadata->table['uniqueConstraints'][$unique['name']] = array( + 'columns' => $columns + ); + } + } + + 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']; + } + + $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); + } + } + + // 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 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 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 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']; + } + + foreach ($joinTableElement['joinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + 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 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)); + } + } + } + } + + /** + * 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; + } + + /** + * Parse 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; + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + return Yaml::parse($file); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ElementCollection.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ElementCollection.php new file mode 100644 index 0000000..f3f4f4c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ElementCollection.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ALL") + * @todo check available targets + */ +final class ElementCollection implements Annotation +{ + /** @var string */ + public $tableName; +} 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..d1b4eb2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php @@ -0,0 +1,32 @@ +. + */ + +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/EntityResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php new file mode 100644 index 0000000..af26a11 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php @@ -0,0 +1,58 @@ +. + */ + +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..ee330b2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/FieldResult.php @@ -0,0 +1,48 @@ +. + */ + +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..d128d16 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class GeneratedValue implements Annotation +{ + /** @var string */ + 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..51f037a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class Index implements Annotation +{ + /** @var string */ + public $name; + /** @var array */ + public $columns; +} 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..d9a2471 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class InheritanceType implements Annotation +{ + /** @var string */ + 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..8c9bc10 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php @@ -0,0 +1,42 @@ +. + */ + +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; + /** @var string */ + public $fieldName; // field name used in non-object hydration (array/scalar) +} 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..8d4c045 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php @@ -0,0 +1,30 @@ +. + */ + +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..7fb69cb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php @@ -0,0 +1,36 @@ +. + */ + +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..9c79689 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php @@ -0,0 +1,42 @@ +. + */ + +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; + /** @var string */ + 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..eb09755 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class ManyToOne implements Annotation +{ + /** @var string */ + public $targetEntity; + /** @var array */ + public $cascade; + /** @var string */ + 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..b551610 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php @@ -0,0 +1,30 @@ +. + */ + +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..9b97a1f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php @@ -0,0 +1,441 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * A MappingException indicates that something is wrong with the mapping setup. + * + * @since 2.0 + */ +class MappingException extends \Doctrine\ORM\ORMException +{ + public static function pathRequired() + { + return new self("Specifying the paths to your entities is required ". + "in the AnnotationDriver to retrieve all class names."); + } + + 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 + )); + + } + + public static function invalidInheritanceType($entityName, $type) + { + return new self("The inheritance type '$type' specified for '$entityName' does not exist."); + } + + public static function generatorNotAllowedWithCompositeId() + { + return new self("Id generators can't be used with a composite id."); + } + + public static function missingFieldName($entity) + { + return new self("The field or association mapping misses the 'fieldName' attribute in entity '$entity'."); + } + + public static function missingTargetEntity($fieldName) + { + return new self("The association mapping '$fieldName' misses the 'targetEntity' attribute."); + } + + public static function missingSourceEntity($fieldName) + { + return new self("The association mapping '$fieldName' misses the 'sourceEntity' attribute."); + } + + 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 + */ + 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 + */ + public static function invalidOverrideFieldType($className, $fieldName) + { + return new self("The column type of attribute '$fieldName' on class '$className' could not be changed."); + } + + public static function mappingNotFound($className, $fieldName) + { + return new self("No mapping found for field '$fieldName' on class '$className'."); + } + + public static function queryNotFound($className, $queryName) + { + return new self("No query found named '$queryName' on class '$className'."); + } + + public static function resultMappingNotFound($className, $resultName) + { + return new self("No result set mapping found named '$resultName' on class '$className'."); + } + + public static function emptyQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.'" could not be empty.'); + } + + public static function nameIsMandatoryForQueryMapping($className) + { + return new self("Query name on entity class '$className' is not defined."); + } + + public static function missingQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.' requires a result class or result set mapping.'); + } + + public static function missingResultSetMappingEntity($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a entity class name.'); + } + + public static function missingResultSetMappingFieldName($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a field name.'); + } + + public static function nameIsMandatoryForSqlResultSetMapping($className) + { + return new self("Result set mapping name on entity class '$className' is not defined."); + } + + public static function oneToManyRequiresMappedBy($fieldName) + { + return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute."); + } + + 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 + */ + 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 + */ + public static function reflectionFailure($entity, \ReflectionException $previousException) + { + return new self('An error occurred in ' . $entity, 0, $previousException); + } + + 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.'); + } + + 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 + )); + } + + public static function propertyTypeIsRequired($className, $propertyName) + { + return new self("The attribute 'type' is required for the column description of property ".$className."::\$".$propertyName."."); + } + + 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 + */ + public static function duplicateFieldMapping($entity, $fieldName) + { + return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + public static function duplicateAssociationMapping($entity, $fieldName) + { + return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + public static function duplicateQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + 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'); + } + + public static function singleIdNotAllowedOnCompositePrimaryKey($entity) + { + return new self('Single id is not allowed on composite primary key in entity '.$entity); + } + + public static function unsupportedOptimisticLockingType($entity, $fieldName, $unsupportedType) + { + return new self('Locking type "'.$unsupportedType.'" (specified in "'.$entity.'", field "'.$fieldName.'") ' + .'is not supported by Doctrine.' + ); + } + + 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!' + ); + } + + /** + * Throws 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 self + */ + public static function invalidClassInDiscriminatorMap($className, $owningClass) + { + return new self( + "Entity class '$className' used in the discriminator map of class '$owningClass' ". + "does not exist." + ); + } + + 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) + )) . "})" + ); + } + + public static function missingDiscriminatorMap($className) + { + return new self("Entity class '$className' is using inheritance but no discriminator map was defined."); + } + + public static function missingDiscriminatorColumn($className) + { + return new self("Entity class '$className' is using inheritance but no discriminator column was defined."); + } + + 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!"); + } + + public static function nameIsMandatoryForDiscriminatorColumns($className) + { + return new self("Discriminator column name on entity class '$className' is not defined."); + } + + public static function cannotVersionIdField($className, $fieldName) + { + return new self("Setting Id field '$fieldName' as versionale in entity class '$className' is not supported."); + } + + 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 self + */ + public static function duplicateColumnName($className, $columnName) + { + return new self("Duplicate definition of column '".$columnName."' on entity '".$className."' in a field or discriminator column mapping."); + } + + public static function illegalToManyAssocationOnMappedSuperclass($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 self + */ + 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."'."); + } + + public static function noSingleAssociationJoinColumnFound($className, $field) + { + return new self("'$className#$field' is not an association with a single join column."); + } + + 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."); + } + + 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'."); + } + + 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."); + } + + public static function illegalInverseIdentifierAssocation($className, $field) + { + return new self("An inverse association is not allowed to be identifier in '$className#$field'."); + } + + public static function illegalToManyIdentifierAssoaction($className, $field) + { + return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'."); + } + + public static function noInheritanceOnMappedSuperClass($className) + { + return new self("Its not supported to define inheritance information on a mapped superclass '" . $className . "'."); + } + + 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 hierachy. Alternatively you can make '".$className."' an abstract class " . + "to avoid this exception from occuring." + ); + } + + public static function lifecycleCallbackMethodNotFound($className, $methodName) + { + return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback."); + } + + public static function invalidFetchMode($className, $annotation) + { + return new self("Entity '" . $className . "' has a mapping with invalid fetch mode '" . $annotation . "'"); + } + + 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."); + } + + public static function invalidTargetEntityClass($targetEntity, $sourceEntity, $associationName) + { + return new self("The target-entity " . $targetEntity . " cannot be found in '" . $sourceEntity."#".$associationName."'."); + } + + 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 + )); + } +} 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..f0192ae --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php @@ -0,0 +1,63 @@ +. + */ + +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..14bb479 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php @@ -0,0 +1,30 @@ +. + */ + +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..2826820 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php @@ -0,0 +1,32 @@ +. + */ + +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..a6acac2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamingStrategy.php @@ -0,0 +1,82 @@ +. + */ + +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 +{ + /** + * Return a table name for an entity class + * + * @param string $className The fully-qualified class name + * @return string A table name + */ + function classToTableName($className); + + /** + * Return a column name for a property + * + * @param string $propertyName A property + * @return string A column name + */ + function propertyToColumnName($propertyName); + + /** + * Return the default reference column name + * + * @return string A column name + */ + function referenceColumnName(); + + /** + * Return a join column name for a property + * + * @param string $propertyName A property + * @return string A join column name + */ + function joinColumnName($propertyName); + + /** + * Return a join table name + * + * @param string $sourceEntity The source entity + * @param string $targetEntity The target entity + * @param string $propertyName A property + * @return string A join table name + */ + function joinTableName($sourceEntity, $targetEntity, $propertyName = null); + + /** + * Return the foreign key column name for the given parameters + * + * @param string $entityName A entity + * @param string $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..233dc90 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class OneToMany implements Annotation +{ + /** @var string */ + public $mappedBy; + /** @var string */ + public $targetEntity; + /** @var array */ + public $cascade; + /** @var string */ + 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..68689ef --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php @@ -0,0 +1,40 @@ +. + */ + +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; + /** @var string */ + 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..4041973 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php @@ -0,0 +1,30 @@ +. + */ + +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..52e846d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/QuoteStrategy.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\ORM\Mapping\ClassMetadata; +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 $class + * @return string + */ + function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null); + +} \ No newline at end of file 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..1acb498 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php @@ -0,0 +1,34 @@ +. + */ + +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..881e873 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php @@ -0,0 +1,56 @@ +. + */ + +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..8f94f0c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php @@ -0,0 +1,38 @@ +. + */ + +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..236ab5c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php @@ -0,0 +1,135 @@ +. + */ + +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 + */ + public function getCase() + { + return $this->case; + } + + /** + * Sets string case CASE_LOWER | CASE_UPPER + * Alphabetic characters converted to lowercase or uppercase + * + * @param integer $case + */ + 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) + { + return $this->underscore($propertyName); + } + + /** + * {@inheritdoc} + */ + public function referenceColumnName() + { + return $this->case === CASE_UPPER ? 'ID' : 'id'; + } + + /** + * {@inheritdoc} + */ + public function joinColumnName($propertyName) + { + 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..7df2a2c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class UniqueConstraint implements Annotation +{ + /** @var string */ + public $name; + /** @var array */ + public $columns; +} 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..1b9b022 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php @@ -0,0 +1,87 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Represents a native SQL query. + * + * @author Roman Borschel + * @since 2.0 + */ +final class NativeQuery extends AbstractQuery +{ + 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..682cb3a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php @@ -0,0 +1,34 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when an ORM query unexpectedly does not return any results. + * + * @author robo + * @since 2.0 + */ +class NoResultException extends UnexpectedResultException +{ + 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..4523af2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php @@ -0,0 +1,31 @@ +. + */ + +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..2776753 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php @@ -0,0 +1,167 @@ +. + */ + +namespace Doctrine\ORM; + +use Exception; + +/** + * Base exception class for all ORM exceptions. + * + * @author Roman Borschel + * @since 2.0 + */ +class ORMException extends Exception +{ + public static function missingMappingDriverImpl() + { + return new self("It's a requirement to specify a Metadata Driver and pass it ". + "to Doctrine\\ORM\\Configuration::setMetadataDriverImpl()."); + } + + public static function namedQueryNotFound($queryName) + { + return new self('Could not find a named query by the name "' . $queryName . '"'); + } + + public static function namedNativeQueryNotFound($nativeQueryName) + { + return new self('Could not find a named native query by the name "' . $nativeQueryName . '"'); + } + + 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 or PostgreSQL SERIAL) this means you have to call " . + "EntityManager#flush() between both persist operations." + ); + } + + 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." + ); + } + + public static function unrecognizedField($field) + { + return new self("Unrecognized field: $field"); + } + + /** + * @param string $className + * @param string $field + */ + public static function invalidOrientation($className, $field) + { + return new self("Invalid order by orientation specified for " . $className . "#" . $field); + } + + public static function invalidFlushMode($mode) + { + return new self("'$mode' is an invalid flush mode."); + } + + public static function entityManagerClosed() + { + return new self("The EntityManager is closed."); + } + + public static function invalidHydrationMode($mode) + { + return new self("'$mode' is an invalid hydration mode."); + } + + public static function mismatchedEventManager() + { + return new self("Cannot use different EventManager instances for EntityManager and Connection."); + } + + public static function findByRequiresParameter($methodName) + { + return new self("You need to pass a parameter to '".$methodName."'"); + } + + 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" + ); + } + + 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." + ); + } + + public static function invalidResultCacheDriver() { + return new self("Invalid result cache driver; it must implement Doctrine\\Common\\Cache\\Cache."); + } + + public static function notSupported() { + return new self("This behaviour is (currently) not supported by Doctrine 2"); + } + + public static function queryCacheNotConfigured() + { + return new self('Query Cache is not configured.'); + } + + public static function metadataCacheNotConfigured() + { + return new self('Class Metadata Cache is not configured.'); + } + + public static function proxyClassesAlwaysRegenerating() + { + return new self('Proxy Classes are always regenerating.'); + } + + public static function unknownEntityNamespace($entityNamespaceAlias) + { + return new self( + "Unknown Entity namespace alias '$entityNamespaceAlias'." + ); + } + + public static function invalidEntityRepository($className) + { + return new self("Invalid repository class '".$className."'. It must be a Doctrine\Common\Persistence\ObjectRepository."); + } + + public static function missingIdentifierField($className, $fieldName) + { + return new self("The identifier $fieldName is missing for a query of " . $className); + } + + 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."); + } +} 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..d889173 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php @@ -0,0 +1,115 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Contains exception messages for all invalid lifecycle state exceptions inside UnitOfWork + * + * @author Benjamin Eberlei + */ +class ORMInvalidArgumentException extends \InvalidArgumentException +{ + static public function scheduleInsertForManagedEntity($entity) + { + return new self("A managed+dirty entity " . self::objToStr($entity) . " can not be scheduled for insertion."); + } + + static public function scheduleInsertForRemovedEntity($entity) + { + return new self("Removed entity " . self::objToStr($entity) . " can not be scheduled for insertion."); + } + + static public function scheduleInsertTwice($entity) + { + return new self("Entity " . self::objToStr($entity) . " can not be scheduled for insertion twice."); + } + + 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." + ); + } + + 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"); + } + + 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.")); + } + + 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."); + } + + 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"); + } + + static public function entityHasNoIdentity($entity, $operation) + { + return new self("Entity has no identity, therefore " . $operation ." cannot be performed. " . self::objToStr($entity)); + } + + static public function entityIsRemoved($entity, $operation) + { + return new self("Entity is removed, therefore " . $operation ." cannot be performed. " . self::objToStr($entity)); + } + + static public function detachedEntityCannot($entity, $operation) + { + return new self("A detached entity was found during " . $operation . " " . self::objToStr($entity)); + } + + public static function invalidObject($context, $given, $parameterIndex = 1) + { + return new self($context .' expects parameter ' . $parameterIndex . + ' to be an entity object, '. gettype($given) . ' given.'); + } + + /** + * 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..b425ac3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php @@ -0,0 +1,64 @@ +. + */ + +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 +{ + private $entity; + + public function __construct($msg, $entity) + { + parent::__construct($msg); + $this->entity = $entity; + } + + /** + * Gets the entity that caused the exception. + * + * @return object + */ + public function getEntity() + { + return $this->entity; + } + + public static function lockFailed($entity) + { + return new self("The optimistic lock on an entity failed.", $entity); + } + + public static function lockFailedVersionMissmatch($entity, $expectedLockVersion, $actualLockVersion) + { + return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity); + } + + 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..ed5d7df --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php @@ -0,0 +1,836 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\ORM\Mapping\ClassMetadata; + +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Selectable; +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\ExpressionBuilder; + +use Closure; + +/** + * 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 + * @todo Design for inheritance to allow custom implementations? + */ +final class PersistentCollection implements Collection, 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\EntityManager + */ + 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. + */ + 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; + + /** + * Whether the collection has already been initialized. + * + * @var boolean + */ + private $initialized = true; + + /** + * The wrapped Collection instance. + * + * @var Collection + */ + private $coll; + + /** + * Creates a new persistent collection. + * + * @param EntityManager $em The EntityManager the collection will be associated with. + * @param ClassMetadata $class The class descriptor of the entity type of this collection. + * @param array The collection elements. + */ + public function __construct(EntityManager $em, $class, $coll) + { + $this->coll = $coll; + $this->em = $em; + $this->typeClass = $class; + } + + /** + * 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 AssociationMapping $assoc + */ + 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; + } + + 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. + */ + public function hydrateAdd($element) + { + $this->coll->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 $value The element to set. + */ + public function hydrateSet($key, $element) + { + $this->coll->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. + */ + public function initialize() + { + if ($this->initialized || ! $this->association) { + return; + } + + // Has NEW objects added through add(). Remember them. + $newObjects = array(); + + if ($this->isDirty) { + $newObjects = $this->coll->toArray(); + } + + $this->coll->clear(); + $this->em->getUnitOfWork()->loadCollection($this); + $this->takeSnapshot(); + + // Reattach NEW objects added through add(), if any. + if ($newObjects) { + foreach ($newObjects as $obj) { + $this->coll->add($obj); + } + + $this->isDirty = true; + } + + $this->initialized = true; + } + + /** + * INTERNAL: + * Tells this collection to take a snapshot of its current state. + */ + public function takeSnapshot() + { + $this->snapshot = $this->coll->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->coll->toArray(), + function($a, $b) { return $a === $b ? 0 : 1; } + ); + } + + /** + * INTERNAL: + * getInsertDiff + * + * @return array + */ + public function getInsertDiff() + { + return array_udiff_assoc( + $this->coll->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. + */ + 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. + */ + public function setDirty($dirty) + { + $this->isDirty = $dirty; + } + + /** + * Sets the initialized flag of the collection, forcing it into that state. + * + * @param boolean $bool + */ + public function setInitialized($bool) + { + $this->initialized = $bool; + } + + /** + * Checks whether this collection has been initialized. + * + * @return boolean + */ + public function isInitialized() + { + return $this->initialized; + } + + /** {@inheritdoc} */ + public function first() + { + $this->initialize(); + + return $this->coll->first(); + } + + /** {@inheritdoc} */ + public function last() + { + $this->initialize(); + + return $this->coll->last(); + } + + /** + * {@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. + $this->initialize(); + + $removed = $this->coll->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->coll->contains($element)) { + return $this->coll->removeElement($element); + } + + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + if ($persister->removeElement($this, $element)) { + return $element; + } + + return null; + } + + $this->initialize(); + + $removed = $this->coll->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) + { + $this->initialize(); + + return $this->coll->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->coll->contains($element) || $persister->contains($this, $element); + } + + $this->initialize(); + + return $this->coll->contains($element); + } + + /** + * {@inheritdoc} + */ + public function exists(Closure $p) + { + $this->initialize(); + + return $this->coll->exists($p); + } + + /** + * {@inheritdoc} + */ + public function indexOf($element) + { + $this->initialize(); + + return $this->coll->indexOf($element); + } + + /** + * {@inheritdoc} + */ + public function get($key) + { + $this->initialize(); + + return $this->coll->get($key); + } + + /** + * {@inheritdoc} + */ + public function getKeys() + { + $this->initialize(); + + return $this->coll->getKeys(); + } + + /** + * {@inheritdoc} + */ + public function getValues() + { + $this->initialize(); + + return $this->coll->getValues(); + } + + /** + * {@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->coll->count() : 0); + } + + $this->initialize(); + + return $this->coll->count(); + } + + /** + * {@inheritdoc} + */ + public function set($key, $value) + { + $this->initialize(); + + $this->coll->set($key, $value); + + $this->changed(); + } + + /** + * {@inheritdoc} + */ + public function add($value) + { + $this->coll->add($value); + + $this->changed(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function isEmpty() + { + $this->initialize(); + + return $this->coll->isEmpty(); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $this->initialize(); + + return $this->coll->getIterator(); + } + + /** + * {@inheritdoc} + */ + public function map(Closure $func) + { + $this->initialize(); + + return $this->coll->map($func); + } + + /** + * {@inheritdoc} + */ + public function filter(Closure $p) + { + $this->initialize(); + + return $this->coll->filter($p); + } + + /** + * {@inheritdoc} + */ + public function forAll(Closure $p) + { + $this->initialize(); + + return $this->coll->forAll($p); + } + + /** + * {@inheritdoc} + */ + public function partition(Closure $p) + { + $this->initialize(); + + return $this->coll->partition($p); + } + + /** + * {@inheritdoc} + */ + public function toArray() + { + $this->initialize(); + + return $this->coll->toArray(); + } + + /** + * {@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->coll as $element) { + $uow->scheduleOrphanRemoval($element); + } + } + + $this->coll->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 Tried to implement Serializable first but that did not work well + * with circular references. This solution seems simpler and works well. + */ + public function __sleep() + { + return array('coll', 'initialized'); + } + + /* ArrayAccess implementation */ + + /** + * @see containsKey() + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * @see get() + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * @see add() + * @see set() + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + return $this->add($value); + } + + return $this->set($offset, $value); + } + + /** + * @see remove() + */ + public function offsetUnset($offset) + { + return $this->remove($offset); + } + + public function key() + { + return $this->coll->key(); + } + + /** + * Gets the element of the collection at the current iterator position. + */ + public function current() + { + return $this->coll->current(); + } + + /** + * Moves the internal iterator position to the next element. + */ + public function next() + { + return $this->coll->next(); + } + + /** + * Retrieves the wrapped Collection instance. + * + * @return \Doctrine\Common\Collections\Collection + */ + public function unwrap() + { + return $this->coll; + } + + /** + * Extract 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 $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); + } + + $this->initialize(); + + return $this->coll->slice($offset, $length); + } + + /** + * Cleanup 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. + */ + public function __clone() + { + if (is_object($this->coll)) { + $this->coll = clone $this->coll; + } + + $this->initialize(); + + $this->owner = null; + $this->snapshot = array(); + + $this->changed(); + } + + /** + * 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 Collection + */ + public function matching(Criteria $criteria) + { + if ($this->isDirty) { + $this->initialize(); + } + + if ($this->initialized) { + return $this->coll->matching($criteria); + } + + if ($this->association['type'] !== ClassMetadata::ONE_TO_MANY) { + throw new \RuntimeException("Matching Criteria on PersistentCollection only works on OneToMany assocations at the moment."); + } + + $id = $this->em + ->getClassMetadata(get_class($this->owner)) + ->getSingleIdReflectionProperty() + ->getValue($this->owner); + $builder = Criteria::expr(); + $ownerExpression = $builder->eq($this->backRefFieldName, $id); + $expression = $criteria->getWhereExpression(); + $expression = $expression ? $builder->andX($expression, $ownerExpression) : $ownerExpression; + + $criteria->where($expression); + + $persister = $this->em->getUnitOfWork()->getEntityPersister($this->association['targetEntity']); + + return new ArrayCollection($persister->loadCriteria($criteria)); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php new file mode 100644 index 0000000..2728918 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php @@ -0,0 +1,223 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\EntityManager, + Doctrine\ORM\PersistentCollection; + +/** + * Base class for all collection persisters. + * + * @since 2.0 + * @author Roman Borschel + */ +abstract class AbstractCollectionPersister +{ + /** + * @var EntityManager + */ + protected $_em; + + /** + * @var \Doctrine\DBAL\Connection + */ + protected $_conn; + + /** + * @var \Doctrine\ORM\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 \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + $this->_uow = $em->getUnitOfWork(); + $this->_conn = $em->getConnection(); + $this->platform = $this->_conn->getDatabasePlatform(); + $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + } + + /** + * Deletes the persistent state represented by the given collection. + * + * @param PersistentCollection $coll + */ + public function delete(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side + } + + $sql = $this->_getDeleteSQL($coll); + $this->_conn->executeUpdate($sql, $this->_getDeleteSQLParameters($coll)); + } + + /** + * Gets the SQL statement for deleting the given collection. + * + * @param PersistentCollection $coll + */ + abstract protected function _getDeleteSQL(PersistentCollection $coll); + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete + * the given collection. + * + * @param PersistentCollection $coll + */ + abstract protected function _getDeleteSQLParameters(PersistentCollection $coll); + + /** + * Updates the given collection, synchronizing it's state with the database + * by inserting, updating and deleting individual elements. + * + * @param PersistentCollection $coll + */ + public function update(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side + } + + $this->deleteRows($coll); + //$this->updateRows($coll); + $this->insertRows($coll); + } + + public function deleteRows(PersistentCollection $coll) + { + $deleteDiff = $coll->getDeleteDiff(); + $sql = $this->_getDeleteRowSQL($coll); + + foreach ($deleteDiff as $element) { + $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element)); + } + } + + //public function updateRows(PersistentCollection $coll) + //{} + + public function insertRows(PersistentCollection $coll) + { + $insertDiff = $coll->getInsertDiff(); + $sql = $this->_getInsertRowSQL($coll); + + foreach ($insertDiff as $element) { + $this->_conn->executeUpdate($sql, $this->_getInsertRowSQLParameters($coll, $element)); + } + } + + public function count(PersistentCollection $coll) + { + throw new \BadMethodCallException("Counting the size of this persistent collection is not supported by this CollectionPersister."); + } + + public function slice(PersistentCollection $coll, $offset, $length = null) + { + throw new \BadMethodCallException("Slicing elements is not supported by this CollectionPersister."); + } + + public function contains(PersistentCollection $coll, $element) + { + throw new \BadMethodCallException("Checking for existance of an element is not supported by this CollectionPersister."); + } + + public function containsKey(PersistentCollection $coll, $key) + { + throw new \BadMethodCallException("Checking for existance of a key is not supported by this CollectionPersister."); + } + + public function removeElement(PersistentCollection $coll, $element) + { + throw new \BadMethodCallException("Removing an element is not supported by this CollectionPersister."); + } + + public function removeKey(PersistentCollection $coll, $key) + { + throw new \BadMethodCallException("Removing a key is not supported by this CollectionPersister."); + } + + public function get(PersistentCollection $coll, $index) + { + throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister."); + } + + /** + * Gets the SQL statement used for deleting a row from the collection. + * + * @param PersistentCollection $coll + */ + abstract protected function _getDeleteRowSQL(PersistentCollection $coll); + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete the given + * element from the given collection. + * + * @param PersistentCollection $coll + * @param mixed $element + */ + abstract protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element); + + /** + * Gets the SQL statement used for updating a row in the collection. + * + * @param PersistentCollection $coll + */ + abstract protected function _getUpdateRowSQL(PersistentCollection $coll); + + /** + * Gets the SQL statement used for inserting a row in the collection. + * + * @param PersistentCollection $coll + */ + abstract protected function _getInsertRowSQL(PersistentCollection $coll); + + /** + * Gets the SQL parameters for the corresponding SQL statement to insert the given + * element of the given collection into the database. + * + * @param PersistentCollection $coll + * @param mixed $element + */ + abstract protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php new file mode 100644 index 0000000..19e77a4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata, + 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') + { + $columnName = $class->columnNames[$field]; + $sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $this->quoteStrategy->getColumnName($field, $class, $this->_platform); + $columnAlias = $this->getSQLColumnAlias($columnName); + $this->_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; + } + + protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className) + { + $columnAlias = $this->getSQLColumnAlias($joinColumnName); + $this->_rsm->addMetaResult('r', $columnAlias, $joinColumnName); + + return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php new file mode 100644 index 0000000..6620fa9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -0,0 +1,1796 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use PDO; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Connection; + +use Doctrine\ORM\ORMException; +use Doctrine\ORM\OptimisticLockException; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\UnitOfWork; +use Doctrine\ORM\Query; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Events; +use Doctrine\ORM\Event\LifecycleEventArgs; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\Expr\Comparison; + +/** + * A BasicEntityPersiter 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 + * @since 2.0 + */ +class BasicEntityPersister +{ + /** + * @var array + */ + static private $comparisonMap = array( + Comparison::EQ => '= %s', + Comparison::IS => '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)', + ); + + /** + * 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 \Doctrine\ORM\EntityManager + */ + protected $_em; + + /** + * Queued inserts. + * + * @var array + */ + protected $_queuedInserts = array(); + + /** + * ResultSetMapping that is used for all queries. Is generated lazily once per request. + * + * TODO: Evaluate Caching in combination with the other cached SQL snippets. + * + * @var Query\ResultSetMapping + */ + protected $_rsm; + + /** + * 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 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 + */ + protected $_selectColumnListSql; + + /** + * The JOIN SQL fragement used to eagerly load all many-to-one and one-to-one + * associations configured as FETCH_EAGER, aswell as all inverse one-to-one associations. + * + * @var string + */ + protected $_selectJoinSql; + + /** + * Counter for creating unique SQL table and column aliases. + * + * @var integer + */ + protected $_sqlAliasCounter = 0; + + /** + * Map from class names (FQCN) to the corresponding generated SQL table aliases. + * + * @var array + */ + protected $_sqlTableAliases = array(); + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + protected $quoteStrategy; + + /** + * Initializes a new BasicEntityPersister that uses the given EntityManager + * and persists instances of the class described by the given ClassMetadata descriptor. + * + * @param \Doctrine\ORM\EntityManager $em + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + */ + public function __construct(EntityManager $em, ClassMetadata $class) + { + $this->_em = $em; + $this->_class = $class; + $this->_conn = $em->getConnection(); + $this->_platform = $this->_conn->getDatabasePlatform(); + $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + } + + /** + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getClassMetadata() + { + return $this->_class; + } + + /** + * 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. + */ + public function addInsert($entity) + { + $this->_queuedInserts[spl_object_hash($entity)] = $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() + { + if ( ! $this->_queuedInserts) { + return; + } + + $postInsertIds = array(); + $idGen = $this->_class->idGenerator; + $isPostInsertId = $idGen->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) { + $id = $idGen->generate($this->_em, $entity); + $postInsertIds[$id] = $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 mixed $id + */ + protected function assignDefaultVersionValue($entity, $id) + { + $value = $this->fetchVersionValue($this->_class, $id); + $this->_class->setFieldValue($entity, $this->_class->versionField, $value); + } + + /** + * Fetch the current version value of a versioned entity. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $versionedClass + * @param mixed $id + * @return mixed + */ + protected function fetchVersionValue($versionedClass, $id) + { + $versionField = $versionedClass->versionField; + $identifier = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->_platform); + + $versionFieldColumnName = $this->quoteStrategy->getColumnName($versionField, $versionedClass, $this->_platform); + + //FIXME: Order with composite keys might not be correct + $sql = 'SELECT ' . $versionFieldColumnName + . ' FROM ' . $this->quoteStrategy->getTableName($versionedClass, $this->_platform) + . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?'; + $value = $this->_conn->fetchColumn($sql, array_values((array)$id)); + + return Type::getType($versionedClass->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->_platform); + } + + /** + * 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. + * + * The data to update is retrieved through {@link _prepareUpdateData}. + * Subclasses that override this method are supposed to obtain the update data + * in the same way, through {@link _prepareUpdateData}. + * + * Subclasses are also supposed to take care of versioning when overriding this method, + * if necessary. The {@link _updateTable} method can be used to apply the data retrieved + * from {@_prepareUpdateData} on the target tables, thereby optionally applying versioning. + * + * @param object $entity The entity to update. + */ + public function update($entity) + { + $updateData = $this->_prepareUpdateData($entity); + $tableName = $this->_class->getTableName(); + + if (isset($updateData[$tableName]) && $updateData[$tableName]) { + $this->_updateTable( + $entity, $this->quoteStrategy->getTableName($this->_class, $this->_platform), + $updateData[$tableName], $this->_class->isVersioned + ); + + if ($this->_class->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. + */ + protected final function _updateTable($entity, $quotedTableName, array $updateData, $versioned = false) + { + $set = $params = $types = array(); + + foreach ($updateData as $columnName => $value) { + $column = $columnName; + $placeholder = '?'; + + if (isset($this->_class->fieldNames[$columnName])) { + $column = $this->quoteStrategy->getColumnName($this->_class->fieldNames[$columnName], $this->_class, $this->_platform); + + if (isset($this->_class->fieldMappings[$this->_class->fieldNames[$columnName]]['requireSQLConversion'])) { + $type = Type::getType($this->_columnTypes[$columnName]); + $placeholder = $type->convertToDatabaseValueSQL('?', $this->_platform); + } + } else if (isset($this->quotedColumns[$columnName])) { + $column = $this->quotedColumns[$columnName]; + } + + $set[] = $column . ' = ' . $placeholder; + $params[] = $value; + $types[] = $this->_columnTypes[$columnName]; + } + + $where = array(); + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + + foreach ($this->_class->identifier as $idField) { + if (isset($this->_class->associationMappings[$idField])) { + $targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']); + $where[] = $this->_class->associationMappings[$idField]['joinColumns'][0]['name']; + $params[] = $id[$idField]; + + 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]); + } + } else { + $where[] = $this->quoteStrategy->getColumnName($idField, $this->_class, $this->_platform); + $params[] = $id[$idField]; + $types[] = $this->_class->fieldMappings[$idField]['type']; + } + } + + if ($versioned) { + $versionField = $this->_class->versionField; + $versionFieldType = $this->_class->fieldMappings[$versionField]['type']; + $versionColumn = $this->quoteStrategy->getColumnName($versionField, $this->_class, $this->_platform); + + if ($versionFieldType == Type::INTEGER) { + $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1'; + } else if ($versionFieldType == Type::DATETIME) { + $set[] = $versionColumn . ' = CURRENT_TIMESTAMP'; + } + + $where[] = $versionColumn; + $params[] = $this->_class->reflFields[$versionField]->getValue($entity); + $types[] = $this->_class->fieldMappings[$versionField]['type']; + } + + $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) { + // @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 hierachy? I hope not! + $selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']); + $otherKeys = array(); + $keys = array(); + + if ( ! $mapping['isOwningSide']) { + $relatedClass = $this->_em->getClassMetadata($mapping['targetEntity']); + $mapping = $relatedClass->associationMappings[$mapping['mappedBy']]; + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $keys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $relatedClass, $this->_platform); + } + + if ($selfReferential) { + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $otherKeys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $relatedClass, $this->_platform); + } + } + } else { + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $keys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->_class, $this->_platform); + } + + if ($selfReferential) { + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $otherKeys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->_class, $this->_platform); + } + } + } + + if ( ! isset($mapping['isOnDeleteCascade'])) { + + $joinTableName = $this->quoteStrategy->getJoinTableName($mapping, $this->_class, $this->_platform); + + $this->_conn->delete($joinTableName, array_combine($keys, $identifier)); + + if ($selfReferential) { + $this->_conn->delete($joinTableName, array_combine($otherKeys, $identifier)); + } + } + } + } + } + + /** + * 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. + */ + public function delete($entity) + { + $identifier = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + + $this->deleteJoinTableRecords($identifier); + + $id = array_combine($this->quoteStrategy->getIdentifierColumnNames($this->_class, $this->_platform), $identifier); + + $this->_conn->delete($this->quoteStrategy->getTableName($this->_class, $this->_platform), $id); + } + + /** + * 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) + { + $result = array(); + $uow = $this->_em->getUnitOfWork(); + + if (($versioned = $this->_class->isVersioned) != false) { + $versionField = $this->_class->versionField; + } + + foreach ($uow->getEntityChangeSet($entity) as $field => $change) { + if ($versioned && $versionField == $field) { + continue; + } + + $newVal = $change[1]; + + if (isset($this->_class->associationMappings[$field])) { + $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; + } + } + + 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; + + if ($newVal === null) { + $result[$owningTable][$sourceColumn] = null; + } else if ($targetClass->containsForeignIdentifier) { + $result[$owningTable][$sourceColumn] = $newValId[$targetClass->getFieldForColumn($targetColumn)]; + } else { + $result[$owningTable][$sourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]]; + } + + $this->_columnTypes[$sourceColumn] = $targetClass->getTypeOfColumn($targetColumn); + } + } else { + $columnName = $this->_class->columnNames[$field]; + $this->_columnTypes[$columnName] = $this->_class->fieldMappings[$field]['type']; + $result[$this->getOwningTable($field)][$columnName] = $newVal; + } + } + + 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); + } + + /** + * 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) + { + return $this->_class->getTableName(); + } + + /** + * Loads an entity by a list of field criteria. + * + * @param array $criteria The criteria by which to load the entity. + * @param object $entity The entity to load the data into. If not specified, + * a new entity is created. + * @param $assoc The association that connects the entity to load to another entity, if any. + * @param array $hints Hints for entity creation. + * @param int $lockMode + * @param int $limit Limit number of results + * @param array $orderBy Criteria to order by + * @return object 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 = 0, $limit = null, array $orderBy = null) + { + $sql = $this->_getSelectEntitiesSQL($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->_selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + $entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints); + + return $entities ? $entities[0] : 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. + */ + 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); + } + } else { + $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; + } + + /** + * 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. + */ + public function refresh(array $id, $entity, $lockMode = 0) + { + $sql = $this->_getSelectEntitiesSQL($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->_rsm, array(Query::HINT_REFRESH => true)); + } + + /** + * Load Entities matching the given Criteria object + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array + */ + public function loadCriteria(Criteria $criteria) + { + $orderBy = $criteria->getOrderings(); + $limit = $criteria->getMaxResults(); + $offset = $criteria->getFirstResult(); + + $sql = $this->_getSelectEntitiesSQL($criteria, null, 0, $limit, $offset, $orderBy); + + list($params, $types) = $this->expandCriteriaParameters($criteria); + + $stmt = $this->_conn->executeQuery($sql, $params, $types); + + $hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + + return $hydrator->hydrateAll($stmt, $this->_rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true)); + } + + /** + * Expand Criteria Parameters by walking the expressions and grabbing all + * parameters and types from it. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array(array(), array()) + */ + private function expandCriteriaParameters(Criteria $criteria) + { + $expression = $criteria->getWhereExpression(); + + if ($expression === null) { + return array(array(), array()); + } + + $valueVisitor = new SqlValueVisitor(); + $valueVisitor->dispatch($expression); + + list($values, $types) = $valueVisitor->getParamsAndTypes(); + + $sqlValues = array(); + foreach ($values as $value) { + $sqlValues[] = $this->getValue($value); + } + + $sqlTypes = array(); + foreach ($types as $type) { + list($field, $value) = $type; + $sqlTypes[] = $this->getType($field, $value); + } + + return array($sqlValues, $sqlTypes); + } + + /** + * Loads a list of entities by a list of field criteria. + * + * @param array $criteria + * @param array $orderBy + * @param int $limit + * @param int $offset + * @return array + */ + public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null) + { + $sql = $this->_getSelectEntitiesSQL($criteria, null, 0, $limit, $offset, $orderBy); + list($params, $types) = $this->expandParameters($criteria); + $stmt = $this->_conn->executeQuery($sql, $params, $types); + + $hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + + return $hydrator->hydrateAll($stmt, $this->_rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true)); + } + + /** + * Get (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) + { + $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit); + + return $this->loadArrayFromStatement($assoc, $stmt); + } + + /** + * Load an array of entities from a given dbal statement. + * + * @param array $assoc + * @param \Doctrine\DBAL\Statement $stmt + * + * @return array + */ + private function loadArrayFromStatement($assoc, $stmt) + { + $hints = array(UnitOfWork::HINT_DEFEREAGERLOAD => true); + + if (isset($assoc['indexBy'])) { + $rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed. + $rsm->addIndexBy('r', $assoc['indexBy']); + } else { + $rsm = $this->_rsm; + } + + $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); + + return $hydrator->hydrateAll($stmt, $rsm, $hints); + } + + /** + * Hydrate 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) + { + $hints = array(UnitOfWork::HINT_DEFEREAGERLOAD => true, 'collection' => $coll); + + if (isset($assoc['indexBy'])) { + $rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed. + $rsm->addIndexBy('r', $assoc['indexBy']); + } else { + $rsm = $this->_rsm; + } + + $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); + + return $hydrator->hydrateAll($stmt, $rsm, $hints); + } + + /** + * Loads a collection of entities of a many-to-many association. + * + * @param ManyToManyMapping $assoc The association mapping of the association being loaded. + * @param object $sourceEntity The entity that owns the collection. + * @param PersistentCollection $coll The collection to fill. + * @param int|null $offset + * @param int|null $limit + * @return array + */ + public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $stmt = $this->getManyToManyStatement($assoc, $sourceEntity); + + return $this->loadCollectionFromStatement($assoc, $stmt, $coll); + } + + private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $criteria = array(); + $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); + + if ($assoc['isOwningSide']) { + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($assoc, $sourceClass, $this->_platform); + + foreach ($assoc['joinTable']['joinColumns'] as $joinColumn) { + $relationKeyColumn = $joinColumn['name']; + $sourceKeyColumn = $joinColumn['referencedColumnName']; + $quotedKeyColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->_platform); + + 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[$quotedJoinTable . "." . $quotedKeyColumn] = $value; + } else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { + $criteria[$quotedJoinTable . "." . $quotedKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + } else { + throw MappingException::joinColumnMustPointToMappedField( + $sourceClass->name, $sourceKeyColumn + ); + } + } + } else { + $owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']]; + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($owningAssoc, $sourceClass, $this->_platform); + + // TRICKY: since the association is inverted source and target are flipped + foreach ($owningAssoc['joinTable']['inverseJoinColumns'] as $joinColumn) { + $relationKeyColumn = $joinColumn['name']; + $sourceKeyColumn = $joinColumn['referencedColumnName']; + $quotedKeyColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->_platform); + + 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[$quotedJoinTable . "." . $quotedKeyColumn] = $value; + } else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { + $criteria[$quotedJoinTable . "." . $quotedKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + } else { + throw MappingException::joinColumnMustPointToMappedField( + $sourceClass->name, $sourceKeyColumn + ); + } + } + } + + $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset); + list($params, $types) = $this->expandParameters($criteria); + + return $this->_conn->executeQuery($sql, $params, $types); + } + + /** + * Gets the SELECT SQL to select one or more entities by a set of field criteria. + * + * @param array|\Doctrine\Common\Collections\Criteria $criteria + * @param AssociationMapping $assoc + * @param string $orderBy + * @param int $lockMode + * @param int $limit + * @param int $offset + * @param array $orderBy + * @return string + * @todo Refactor: _getSelectSQL(...) + */ + protected function _getSelectEntitiesSQL($criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null) + { + $joinSql = ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $this->_getSelectManyToManyJoinSQL($assoc) : ''; + $conditionSql = ($criteria instanceof Criteria) + ? $this->_getSelectConditionCriteriaSQL($criteria) + : $this->_getSelectConditionSQL($criteria, $assoc); + + $orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy; + $orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : ''; + + $lockSql = ''; + + if ($lockMode == LockMode::PESSIMISTIC_READ) { + $lockSql = ' ' . $this->_platform->getReadLockSql(); + } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { + $lockSql = ' ' . $this->_platform->getWriteLockSql(); + } + + $alias = $this->_getSQLTableAlias($this->_class->name); + + if ($filterSql = $this->generateFilterConditionSQL($this->_class, $alias)) { + if ($conditionSql) { + $conditionSql .= ' AND '; + } + + $conditionSql .= $filterSql; + } + + return $this->_platform->modifyLimitQuery('SELECT ' . $this->_getSelectColumnListSQL() + . $this->_platform->appendLockHint(' FROM ' . $this->quoteStrategy->getTableName($this->_class, $this->_platform) . ' ' + . $alias, $lockMode) + . $this->_selectJoinSql . $joinSql + . ($conditionSql ? ' WHERE ' . $conditionSql : '') + . $orderBySql, $limit, $offset) + . $lockSql; + } + + /** + * Gets the ORDER BY SQL snippet for ordered collections. + * + * @param array $orderBy + * @param string $baseTableAlias + * @return string + */ + protected final function _getOrderBySQL(array $orderBy, $baseTableAlias) + { + $orderBySql = ''; + + foreach ($orderBy as $fieldName => $orientation) { + if ( ! isset($this->_class->fieldMappings[$fieldName])) { + throw ORMException::unrecognizedField($fieldName); + } + + $orientation = strtoupper(trim($orientation)); + if ($orientation != 'ASC' && $orientation != 'DESC') { + throw ORMException::invalidOrientation($this->_class->name, $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); + + $orderBySql .= $orderBySql ? ', ' : ' ORDER BY '; + $orderBySql .= $tableAlias . '.' . $columnName . ' ' . $orientation; + } + + return $orderBySql; + } + + /** + * 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. + * @todo Rename: _getSelectColumnsSQL() + */ + protected function _getSelectColumnListSQL() + { + if ($this->_selectColumnListSql !== null) { + return $this->_selectColumnListSql; + } + + $columnList = ''; + $this->_rsm = new Query\ResultSetMapping(); + $this->_rsm->addEntityResult($this->_class->name, 'r'); // r for root + + // Add regular columns to select list + foreach ($this->_class->fieldNames as $field) { + if ($columnList) $columnList .= ', '; + + $columnList .= $this->_getSelectColumnSQL($field, $this->_class); + } + + $this->_selectJoinSql = ''; + $eagerAliasCounter = 0; + + foreach ($this->_class->associationMappings as $assocField => $assoc) { + $assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class); + + if ($assocColumnSQL) { + if ($columnList) $columnList .= ', '; + + $columnList .= $assocColumnSQL; + } + + if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) { + $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->_rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField); + + foreach ($eagerEntity->fieldNames as $field) { + if ($columnList) $columnList .= ', '; + + $columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias); + } + + foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) { + $assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias); + + if ($assoc2ColumnSQL) { + if ($columnList) $columnList .= ', '; + $columnList .= $assoc2ColumnSQL; + } + } + $first = true; + + if ($assoc['isOwningSide']) { + $this->_selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($assoc['joinColumns']); + $this->_selectJoinSql .= ' ' . $this->quoteStrategy->getTableName($eagerEntity, $this->_platform) . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON '; + + $tableAlias = $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias); + foreach ($assoc['joinColumns'] as $joinColumn) { + $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->_class, $this->_platform); + $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->_class, $this->_platform); + + if ( ! $first) { + $this->_selectJoinSql .= ' AND '; + } + $this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = ' + . $tableAlias . '.' . $targetCol; + $first = false; + } + + // Add filter SQL + if ($filterSql = $this->generateFilterConditionSQL($eagerEntity, $tableAlias)) { + $this->_selectJoinSql .= ' AND ' . $filterSql; + } + } else { + $eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']); + $owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']); + + $this->_selectJoinSql .= ' LEFT JOIN'; + $this->_selectJoinSql .= ' ' . $this->quoteStrategy->getTableName($eagerEntity, $this->_platform) . ' ' + . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) . ' ON '; + + foreach ($owningAssoc['sourceToTargetKeyColumns'] as $sourceCol => $targetCol) { + if ( ! $first) { + $this->_selectJoinSql .= ' AND '; + } + + $this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = ' + . $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol; + $first = false; + } + } + } + } + + $this->_selectColumnListSql = $columnList; + + return $this->_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') + { + $columnList = array(); + + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + + foreach ($assoc['joinColumns'] as $joinColumn) { + + $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; + + $this->_rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, isset($assoc['id']) && $assoc['id'] === true); + } + } + + return implode(', ', $columnList); + } + + /** + * Gets the SQL join fragment used when selecting entities from a + * many-to-many association. + * + * @param ManyToManyMapping $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); + } + + /** + * Gets the INSERT SQL used by the persister to persist a new entity. + * + * @return string + */ + protected function _getInsertSQL() + { + if ($this->_insertSql === null) { + $insertSql = ''; + $columns = $this->_getInsertColumnList(); + + if (empty($columns)) { + $insertSql = $this->_platform->getEmptyIdentityInsertSQL( + $this->quoteStrategy->getTableName($this->_class, $this->_platform), + $this->quoteStrategy->getColumnName($this->_class->identifier[0], $this->_class, $this->_platform) + ); + } else { + $columns = array_unique($columns); + + $values = array(); + 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; + } + + $insertSql = 'INSERT INTO ' . $this->quoteStrategy->getTableName($this->_class, $this->_platform) + . ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $values) . ')'; + } + + $this->_insertSql = $insertSql; + } + + 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->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); + } + } + } else 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 + */ + protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') + { + $sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) + . '.' . $this->quoteStrategy->getColumnName($field, $class, $this->_platform); + $columnAlias = $this->getSQLColumnAlias($class->columnNames[$field]); + + $this->_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 + * @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->_sqlTableAliases[$className])) { + return $this->_sqlTableAliases[$className]; + } + + $tableAlias = 't' . $this->_sqlAliasCounter++; + + $this->_sqlTableAliases[$className] = $tableAlias; + + return $tableAlias; + } + + /** + * Lock all rows of this entity matching the given criteria with the specified pessimistic lock mode + * + * @param array $criteria + * @param int $lockMode + * @return void + */ + public function lock(array $criteria, $lockMode) + { + $conditionSql = $this->_getSelectConditionSQL($criteria); + + if ($lockMode == LockMode::PESSIMISTIC_READ) { + $lockSql = $this->_platform->getReadLockSql(); + } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { + $lockSql = $this->_platform->getWriteLockSql(); + } + + $sql = 'SELECT 1 ' + . $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode) + . ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql; + + list($params, $types) = $this->expandParameters($criteria); + + $this->_conn->executeQuery($sql, $params, $types); + } + + /** + * Get the FROM and optionally JOIN conditions to lock the entity managed by this persister. + * + * @return string + */ + protected function getLockTablesSql() + { + return 'FROM ' . $this->quoteStrategy->getTableName($this->_class, $this->_platform) . ' ' + . $this->_getSQLTableAlias($this->_class->name); + } + + /** + * Get 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); + + return $visitor->dispatch($expression); + } + + /** + * Get the SQL WHERE condition for matching a field with a given value. + * + * @param string $field + * @param mixed $value + * @param array|null $assoc + * @param string $comparison + * + * @return string + */ + public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null) + { + $conditionSql = $this->getSelectConditionStatementColumnSQL($field, $assoc); + $placeholder = '?'; + + if (isset($this->_class->fieldMappings[$field]['requireSQLConversion'])) { + $type = Type::getType($this->_class->getTypeOfField($field)); + $placeholder = $type->convertToDatabaseValueSQL($placeholder, $this->_platform); + } + + $conditionSql .= ($comparison === null) + ? ((is_array($value)) ? ' IN (?)' : (($value === null) ? ' IS NULL' : ' = ' . $placeholder)) + : ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder); + + + return $conditionSql; + } + + /** + * Build the left-hand-side of a where condition statement. + * + * @param string $field + * @param array $assoc + * + * @return string + */ + protected function getSelectConditionStatementColumnSQL($field, $assoc = null) + { + switch (true) { + case (isset($this->_class->columnNames[$field])): + $className = (isset($this->_class->fieldMappings[$field]['inherited'])) + ? $this->_class->fieldMappings[$field]['inherited'] + : $this->_class->name; + + return $this->_getSQLTableAlias($className) . '.' . $this->quoteStrategy->getColumnName($field, $this->_class, $this->_platform); + + case (isset($this->_class->associationMappings[$field])): + if ( ! $this->_class->associationMappings[$field]['isOwningSide']) { + throw ORMException::invalidFindByInverseAssociation($this->_class->name, $field); + } + + $className = (isset($this->_class->associationMappings[$field]['inherited'])) + ? $this->_class->associationMappings[$field]['inherited'] + : $this->_class->name; + + return $this->_getSQLTableAlias($className) . '.' . $this->_class->associationMappings[$field]['joinColumns'][0]['name']; + + case ($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 $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 AssociationMapping $assoc + * @return string + */ + protected function _getSelectConditionSQL(array $criteria, $assoc = null) + { + $conditionSql = ''; + + foreach ($criteria as $field => $value) { + $conditionSql .= $conditionSql ? ' AND ' : ''; + $conditionSql .= $this->getSelectConditionStatementSQL($field, $value, $assoc); + } + + return $conditionSql; + } + + /** + * Return an array with (sliced or full list) of elements in the specified collection. + * + * @param array $assoc + * @param object $sourceEntity + * @param int $offset + * @param int $limit + * @return array + */ + public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit); + + return $this->loadArrayFromStatement($assoc, $stmt); + } + + /** + * Loads a collection of entities in a one-to-many association. + * + * @param array $assoc + * @param object $sourceEntity + * @param PersistentCollection $coll The collection to load/fill. + * @param int|null $offset + * @param int|null $limit + */ + public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $stmt = $this->getOneToManyStatement($assoc, $sourceEntity); + + return $this->loadCollectionFromStatement($assoc, $stmt, $coll); + } + + /** + * Build 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) + { + $criteria = 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; + } else { + $criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + } + } + + $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset); + list($params, $types) = $this->expandParameters($criteria); + + return $this->_conn->executeQuery($sql, $params, $types); + } + + /** + * Expand the parameters from the given criteria and use the correct binding types if found. + * + * @param array $criteria + * @return array + */ + private function expandParameters($criteria) + { + $params = $types = array(); + + foreach ($criteria as $field => $value) { + if ($value === null) { + continue; // skip null values. + } + + $types[] = $this->getType($field, $value); + $params[] = $this->getValue($value); + } + + return array($params, $types); + } + + /** + * Infer field type to be used by parameter type casting. + * + * @param string $field + * @param mixed $value + * @return integer + */ + private function getType($field, $value) + { + switch (true) { + case (isset($this->_class->fieldMappings[$field])): + $type = $this->_class->fieldMappings[$field]['type']; + break; + + case (isset($this->_class->associationMappings[$field])): + $assoc = $this->_class->associationMappings[$field]; + + if (count($assoc['sourceToTargetKeyColumns']) > 1) { + throw Query\QueryException::associationPathCompositeKeyNotSupported(); + } + + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); + $targetColumn = $assoc['joinColumns'][0]['referencedColumnName']; + $type = null; + + if (isset($targetClass->fieldNames[$targetColumn])) { + $type = $targetClass->fieldMappings[$targetClass->fieldNames[$targetColumn]]['type']; + } + + break; + + default: + $type = null; + } + if (is_array($value)) { + $type = Type::getType( $type )->getBindingType(); + $type += Connection::ARRAY_PARAM_OFFSET; + } + + return $type; + } + + /** + * Retrieve parameter value + * + * @param mixed $value + * @return mixed + */ + private function getValue($value) + { + if (is_array($value)) { + $newValue = array(); + + foreach ($value as $itemValue) { + $newValue[] = $this->getIndividualValue($itemValue); + } + + return $newValue; + } + + return $this->getIndividualValue($value); + } + + /** + * Retrieve an invidiual parameter value + * + * @param mixed $value + * @return mixed + */ + private function getIndividualValue($value) + { + if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) { + if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) { + $idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value); + } else { + $class = $this->_em->getClassMetadata(get_class($value)); + $idValues = $class->getIdentifierValues($value); + } + + $key = key($idValues); + + if (null !== $key){ + $value = $idValues[$key]; + } + } + + return $value; + } + + /** + * Checks whether the given managed entity exists in the database. + * + * @param object $entity + * @return boolean TRUE if the entity exists in the database, FALSE otherwise. + */ + public function exists($entity, array $extraConditions = array()) + { + $criteria = $this->_class->getIdentifierValues($entity); + + if ( ! $criteria) { + return false; + } + + if ($extraConditions) { + $criteria = array_merge($criteria, $extraConditions); + } + + $alias = $this->_getSQLTableAlias($this->_class->name); + + $sql = 'SELECT 1 ' + . $this->getLockTablesSql() + . ' WHERE ' . $this->_getSelectConditionSQL($criteria); + + if ($filterSql = $this->generateFilterConditionSQL($this->_class, $alias)) { + $sql .= ' AND ' . $filterSql; + } + + list($params) = $this->expandParameters($criteria); + + return (bool) $this->_conn->fetchColumn($sql, $params); + } + + /** + * 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'; + } + + /** + * Gets an SQL column alias for a column name. + * + * @param string $columnName + * @return string + */ + public function getSQLColumnAlias($columnName) + { + return $this->quoteStrategy->getColumnAlias($columnName, $this->_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" + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php new file mode 100644 index 0000000..0b0354a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php @@ -0,0 +1,30 @@ +. + */ +namespace Doctrine\ORM\Persisters; + +/** + * Persister for collections of basic elements / value types. + * + * @author robo + * @todo Implementation once support for collections of basic elements (i.e. strings) is added. + */ +abstract class ElementCollectionPersister extends AbstractCollectionPersister +{ + //put your code here +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php new file mode 100644 index 0000000..2d8967c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -0,0 +1,495 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\ORMException; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query\ResultSetMapping; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Types\Type; + +use Doctrine\Common\Collections\Criteria; + +/** + * 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]; + } + + if (isset($this->_class->associationMappings[$fieldName]['inherited'])) { + $cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']); + } else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) { + $cm = $this->_em->getClassMetadata($this->_class->fieldMappings[$fieldName]['inherited']); + } else { + $cm = $this->_class; + } + + $tableName = $cm->getTableName(); + + $this->_owningTableMap[$fieldName] = $tableName; + $this->_quotedTableMap[$tableName] = $this->quoteStrategy->getTableName($cm, $this->_platform); + + return $tableName; + } + + /** + * {@inheritdoc} + */ + public function executeInserts() + { + if ( ! $this->_queuedInserts) { + return; + } + + $postInsertIds = array(); + $idGen = $this->_class->idGenerator; + $isPostInsertId = $idGen->isPostInsertGenerator(); + + // Prepare statement for the root table + $rootClass = ($this->_class->name !== $this->_class->rootEntityName) ? $this->_em->getClassMetadata($this->_class->rootEntityName) : $this->_class; + $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) { + $id = $idGen->generate($this->_em, $entity); + $postInsertIds[$id] = $entity; + } else { + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + } + + // 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) { + $data = isset($insertData[$tableName]) ? $insertData[$tableName] : array(); + $paramIndex = 1; + + 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) { + $stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]); + } + + $stmt->execute(); + } + } + + $rootTableStmt->closeCursor(); + + foreach ($subTableStmts as $stmt) { + $stmt->closeCursor(); + } + + if ($this->_class->isVersioned) { + $this->assignDefaultVersionValue($entity, $id); + } + + $this->_queuedInserts = array(); + + return $postInsertIds; + } + + /** + * {@inheritdoc} + */ + public function update($entity) + { + $updateData = $this->_prepareUpdateData($entity); + + if (($isVersioned = $this->_class->isVersioned) != false) { + $versionedClass = $this->_getVersionedClassMetadata(); + $versionedTable = $versionedClass->getTableName(); + } + + if ($updateData) { + foreach ($updateData as $tableName => $data) { + $this->_updateTable( + $entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName + ); + } + + // 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])) { + $this->_updateTable($entity, $this->quoteStrategy->getTableName($versionedClass, $this->_platform), array(), true); + } + + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + $this->assignDefaultVersionValue($entity, $id); + } + } + } + + /** + * {@inheritdoc} + */ + public function delete($entity) + { + $identifier = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + $this->deleteJoinTableRecords($identifier); + + $id = array_combine($this->_class->getIdentifierColumnNames(), $identifier); + + // If the database platform supports FKs, just + // delete the row from the root table. Cascades do the rest. + if ($this->_platform->supportsForeignKeyConstraints()) { + $this->_conn->delete( + $this->quoteStrategy->getTableName($this->_em->getClassMetadata($this->_class->rootEntityName), $this->_platform), $id + ); + } else { + // Delete from all tables individually, starting from this class' table up to the root table. + $this->_conn->delete($this->quoteStrategy->getTableName($this->_class, $this->_platform), $id); + + foreach ($this->_class->parentClasses as $parentClass) { + $this->_conn->delete( + $this->quoteStrategy->getTableName($this->_em->getClassMetadata($parentClass), $this->_platform), $id + ); + } + } + } + + /** + * {@inheritdoc} + */ + protected function _getSelectEntitiesSQL($criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null) + { + $idColumns = $this->_class->getIdentifierColumnNames(); + $baseTableAlias = $this->_getSQLTableAlias($this->_class->name); + + // Create the column list fragment only once + if ($this->_selectColumnListSql === null) { + + $this->_rsm = new ResultSetMapping(); + $this->_rsm->addEntityResult($this->_class->name, 'r'); + + // Add regular columns + $columnList = ''; + + foreach ($this->_class->fieldMappings as $fieldName => $mapping) { + if ($columnList != '') $columnList .= ', '; + + $columnList .= $this->_getSelectColumnSQL( + $fieldName, + isset($mapping['inherited']) ? $this->_em->getClassMetadata($mapping['inherited']) : $this->_class + ); + } + + // Add foreign key columns + foreach ($this->_class->associationMappings as $assoc2) { + if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE) { + $tableAlias = isset($assoc2['inherited']) ? $this->_getSQLTableAlias($assoc2['inherited']) : $baseTableAlias; + + foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { + if ($columnList != '') $columnList .= ', '; + + $columnList .= $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + isset($assoc2['inherited']) ? $assoc2['inherited'] : $this->_class->name + ); + } + } + } + + // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#_processSQLResult). + $discrColumn = $this->_class->discriminatorColumn['name']; + $tableAlias = ($this->_class->rootEntityName == $this->_class->name) ? $baseTableAlias : $this->_getSQLTableAlias($this->_class->rootEntityName); + $columnList .= ', ' . $tableAlias . '.' . $discrColumn; + + $resultColumnName = $this->_platform->getSQLResultCasing($discrColumn); + + $this->_rsm->setDiscriminatorColumn('r', $resultColumnName); + $this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn); + } + + // INNER JOIN parent tables + $joinSql = ''; + + foreach ($this->_class->parentClasses as $parentClassName) { + $parentClass = $this->_em->getClassMetadata($parentClassName); + $tableAlias = $this->_getSQLTableAlias($parentClassName); + $joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->_platform) . ' ' . $tableAlias . ' ON '; + $first = true; + + foreach ($idColumns as $idColumn) { + if ($first) $first = false; else $joinSql .= ' AND '; + + $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + } + + // OUTER JOIN sub tables + foreach ($this->_class->subClasses as $subClassName) { + $subClass = $this->_em->getClassMetadata($subClassName); + $tableAlias = $this->_getSQLTableAlias($subClassName); + + if ($this->_selectColumnListSql === null) { + // 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 $assoc2) { + if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE && ! isset($assoc2['inherited'])) { + foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { + if ($columnList != '') $columnList .= ', '; + + $columnList .= $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name + ); + } + } + } + } + + // Add LEFT JOIN + $joinSql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->_platform) . ' ' . $tableAlias . ' ON '; + $first = true; + + foreach ($idColumns as $idColumn) { + if ($first) $first = false; else $joinSql .= ' AND '; + + $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + } + + $joinSql .= ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $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))) { + if ($conditionSql) { + $conditionSql .= ' AND '; + } + + $conditionSql .= $filterSql; + } + + $orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy; + $orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $baseTableAlias) : ''; + + if ($this->_selectColumnListSql === null) { + $this->_selectColumnListSql = $columnList; + } + + $lockSql = ''; + + if ($lockMode == LockMode::PESSIMISTIC_READ) { + $lockSql = ' ' . $this->_platform->getReadLockSql(); + } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { + $lockSql = ' ' . $this->_platform->getWriteLockSql(); + } + + return $this->_platform->modifyLimitQuery('SELECT ' . $this->_selectColumnListSql + . ' FROM ' . $this->quoteStrategy->getTableName($this->_class, $this->_platform) . ' ' . $baseTableAlias + . $joinSql + . ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql, $limit, $offset) + . $lockSql; + } + + /** + * Get the FROM and optionally JOIN conditions to lock the entity managed by this persister. + * + * @return string + */ + public function getLockTablesSql() + { + $idColumns = $this->_class->getIdentifierColumnNames(); + $baseTableAlias = $this->_getSQLTableAlias($this->_class->name); + + // INNER JOIN parent tables + $joinSql = ''; + + foreach ($this->_class->parentClasses as $parentClassName) { + $parentClass = $this->_em->getClassMetadata($parentClassName); + $tableAlias = $this->_getSQLTableAlias($parentClassName); + $joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->_platform) . ' ' . $tableAlias . ' ON '; + $first = true; + + foreach ($idColumns as $idColumn) { + if ($first) $first = false; else $joinSql .= ' AND '; + + $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + } + + return 'FROM ' .$this->quoteStrategy->getTableName($this->_class, $this->_platform) . ' ' . $baseTableAlias . $joinSql; + } + + /* Ensure this method is never called. This persister overrides _getSelectEntitiesSQL directly. */ + protected function _getSelectColumnListSQL() + { + throw new \BadMethodCallException("Illegal invocation of ".__METHOD__."."); + } + + /** {@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)) { + 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, $id) + { + $value = $this->fetchVersionValue($this->_getVersionedClassMetadata(), $id); + $this->_class->setFieldValue($entity, $this->_class->versionField, $value); + } + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php new file mode 100644 index 0000000..9ecafae --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -0,0 +1,446 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\PersistentCollection, + Doctrine\ORM\UnitOfWork; + +/** + * Persister for many-to-many collections. + * + * @author Roman Borschel + * @author Guilherme Blanco + * @author Alexander + * @since 2.0 + */ +class ManyToManyPersister extends AbstractCollectionPersister +{ + /** + * {@inheritdoc} + * + * @override + */ + protected function _getDeleteRowSQL(PersistentCollection $coll) + { + $columns = array(); + $mapping = $coll->getMapping(); + $class = $this->_em->getClassMetadata(get_class($coll->getOwner())); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + return 'DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform) + . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; + } + + /** + * {@inheritdoc} + * + * @override + * @internal Order of the parameters must be the same as the order of the columns in + * _getDeleteRowSql. + */ + protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element) + { + return $this->_collectJoinTableColumnParameters($coll, $element); + } + + /** + * {@inheritdoc} + * + * @override + */ + protected function _getUpdateRowSQL(PersistentCollection $coll) + {} + + /** + * {@inheritdoc} + * + * @override + * @internal Order of the parameters must be the same as the order of the columns in + * _getInsertRowSql. + */ + protected function _getInsertRowSQL(PersistentCollection $coll) + { + $columns = array(); + $mapping = $coll->getMapping(); + $class = $this->_em->getClassMetadata(get_class($coll->getOwner())); + $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + return 'INSERT INTO ' . $joinTable . ' (' . implode(', ', $columns) . ')' + . ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; + } + + /** + * {@inheritdoc} + * + * @override + * @internal Order of the parameters must be the same as the order of the columns in + * _getInsertRowSql. + */ + protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element) + { + return $this->_collectJoinTableColumnParameters($coll, $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 $coll + * @param $element + * @return array + */ + private function _collectJoinTableColumnParameters(PersistentCollection $coll, $element) + { + $params = array(); + $mapping = $coll->getMapping(); + $isComposite = count($mapping['joinTableColumns']) > 2; + + $identifier1 = $this->_uow->getEntityIdentifier($coll->getOwner()); + $identifier2 = $this->_uow->getEntityIdentifier($element); + + if ($isComposite) { + $class1 = $this->_em->getClassMetadata(get_class($coll->getOwner())); + $class2 = $coll->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; + } + + /** + * {@inheritdoc} + * + * @override + */ + protected function _getDeleteSQL(PersistentCollection $coll) + { + $columns = array(); + $mapping = $coll->getMapping(); + $class = $this->_em->getClassMetadata(get_class($coll->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} + * + * @override + * @internal Order of the parameters must be the same as the order of the columns in + * _getDeleteSql. + */ + protected function _getDeleteSQLParameters(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $identifier = $this->_uow->getEntityIdentifier($coll->getOwner()); + + // Optimization for single column identifier + if (count($mapping['relationToSourceKeyColumns']) === 1) { + return array(reset($identifier)); + } + + // Composite identifier + $sourceClass = $this->_em->getClassMetadata(get_class($coll->getOwner())); + $params = array(); + + foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) { + $params[] = isset($sourceClass->fieldNames[$refColumnName]) + ? $identifier[$sourceClass->fieldNames[$refColumnName]] + : $identifier[$sourceClass->getFieldForColumn($columnName)]; + } + + return $params; + } + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $coll) + { + $conditions = array(); + $params = array(); + $mapping = $coll->getMapping(); + $association = $mapping; + $class = $this->_em->getClassMetadata($mapping['sourceEntity']); + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner()); + + if ( ! $mapping['isOwningSide']) { + $targetEntity = $this->_em->getClassMetadata($mapping['targetEntity']); + $association = $targetEntity->associationMappings[$mapping['mappedBy']]; + } + + $joinColumns = ( ! $mapping['isOwningSide']) + ? $association['joinTable']['inverseJoinColumns'] + : $association['joinTable']['joinColumns']; + + foreach ($joinColumns as $joinColumn) { + $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + $referencedName = $joinColumn['referencedColumnName']; + $conditions[] = $columnName . ' = ?'; + $params[] = ($class->containsForeignIdentifier) + ? $id[$class->getFieldForColumn($referencedName)] + : $id[$class->fieldNames[$referencedName]]; + } + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform); + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping); + + if ($filterSql) { + $conditions[] = $filterSql; + } + + $sql = 'SELECT COUNT(*)' + . ' FROM ' . $joinTableName . ' t' + . $joinTargetEntitySQL + . ' WHERE ' . implode(' AND ', $conditions); + + return $this->_conn->fetchColumn($sql, $params); + } + + /** + * @param PersistentCollection $coll + * @param int $offset + * @param int $length + * @return array + */ + public function slice(PersistentCollection $coll, $offset, $length = null) + { + $mapping = $coll->getMapping(); + + return $this->_em->getUnitOfWork()->getEntityPersister($mapping['targetEntity'])->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length); + } + + /** + * @param PersistentCollection $coll + * @param object $element + * @return boolean + */ + public function contains(PersistentCollection $coll, $element) + { + $uow = $this->_em->getUnitOfWork(); + + // Shortcut for new entities + $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); + + if ($entityState === UnitOfWork::STATE_NEW) { + return false; + } + + // Entity is scheduled for inclusion + if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { + return false; + } + + list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element, true); + + $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->_conn->fetchColumn($sql, $params); + } + + /** + * @param PersistentCollection $coll + * @param object $element + * @return boolean + */ + public function removeElement(PersistentCollection $coll, $element) + { + $uow = $this->_em->getUnitOfWork(); + + // shortcut for new entities + $entityState = $uow->getEntityState($element, 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 && $uow->isScheduledForInsert($element)) { + return false; + } + + list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element, false); + + $sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->_conn->executeUpdate($sql, $params); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * @param boolean $addFilters Whether the filter SQL should be included or not. + * @return array + */ + private function getJoinTableRestrictions(PersistentCollection $coll, $element, $addFilters) + { + $uow = $this->_em->getUnitOfWork(); + $mapping = $filterMapping = $coll->getMapping(); + + if ( ! $mapping['isOwningSide']) { + $sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']); + $targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']); + $sourceId = $uow->getEntityIdentifier($element); + $targetId = $uow->getEntityIdentifier($coll->getOwner()); + + $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; + } else { + $sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->_em->getClassMetadata($mapping['targetEntity']); + $sourceId = $uow->getEntityIdentifier($coll->getOwner()); + $targetId = $uow->getEntityIdentifier($element); + } + + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform); + $whereClauses = array(); + $params = array(); + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + $whereClauses[] = $joinTableColumn . ' = ?'; + + if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { + $params[] = ($targetClass->containsForeignIdentifier) + ? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])] + : $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; + continue; + } + + // relationToSourceKeyColumns + $params[] = ($sourceClass->containsForeignIdentifier) + ? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])] + : $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]]; + } + + if ($addFilters) { + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + if ($filterSql) { + $quotedJoinTable .= ' t ' . $joinTargetEntitySQL; + $whereClauses[] = $filterSql; + } + } + + return array($quotedJoinTable, $whereClauses, $params); + } + + /** + * 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 The SQL query part to add to a query. + */ + public function getFilterSql($mapping) + { + $targetClass = $this->_em->getClassMetadata($mapping['targetEntity']); + + if ($mapping['isOwningSide']) { + $joinColumns = $mapping['relationToTargetKeyColumns']; + } else { + $mapping = $targetClass->associationMappings[$mapping['mappedBy']]; + $joinColumns = $mapping['relationToSourceKeyColumns']; + } + + $targetClass = $this->_em->getClassMetadata($targetClass->rootEntityName); + + // A join is needed if there is filtering on the target entity + $joinTargetEntitySQL = ''; + if ($filterSql = $this->generateFilterConditionSQL($targetClass, 'te')) { + $joinTargetEntitySQL = ' JOIN ' + . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' te' + . ' ON'; + + $joinTargetEntitySQLClauses = array(); + foreach ($joinColumns as $joinTableColumn => $targetTableColumn) { + $joinTargetEntitySQLClauses[] = ' t.' . $joinTableColumn . ' = ' . 'te.' . $targetTableColumn; + } + + $joinTargetEntitySQL .= implode(' AND ', $joinTargetEntitySQLClauses); + } + + return array($joinTargetEntitySQL, $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 . ')'; + } + } + + $sql = implode(' AND ', $filterClauses); + return $sql ? "(" . $sql . ")" : ""; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/OneToManyPersister.php new file mode 100644 index 0000000..aa0208f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -0,0 +1,212 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\PersistentCollection, + Doctrine\ORM\UnitOfWork; + +/** + * Persister for one-to-many collections. + * + * @author Roman Borschel + * @author Guilherme Blanco + * @author Alexander + * @since 2.0 + */ +class OneToManyPersister extends AbstractCollectionPersister +{ + /** + * Generates the SQL UPDATE that updates a particular row's foreign + * key to null. + * + * @param PersistentCollection $coll + * @return string + * @override + */ + protected function _getDeleteRowSQL(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $class = $this->_em->getClassMetadata($mapping['targetEntity']); + + return 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform) + . ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?'; + } + + /** + * {@inheritdoc} + * + */ + protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element) + { + return array_values($this->_uow->getEntityIdentifier($element)); + } + + protected function _getInsertRowSQL(PersistentCollection $coll) + { + return "UPDATE xxx SET foreign_key = yyy WHERE foreign_key = zzz"; + } + + /** + * Gets the SQL parameters for the corresponding SQL statement to insert the given + * element of the given collection into the database. + * + * @param PersistentCollection $coll + * @param mixed $element + */ + protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element) + {} + + /* Not used for OneToManyPersister */ + protected function _getUpdateRowSQL(PersistentCollection $coll) + { + return; + } + + /** + * Generates the SQL UPDATE that updates all the foreign keys to null. + * + * @param PersistentCollection $coll + */ + protected function _getDeleteSQL(PersistentCollection $coll) + { + + } + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete + * the given collection. + * + * @param PersistentCollection $coll + */ + protected function _getDeleteSQLParameters(PersistentCollection $coll) + {} + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $targetClass = $this->_em->getClassMetadata($mapping['targetEntity']); + $sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']); + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner()); + + $whereClauses = array(); + $params = array(); + + foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] as $joinColumn) { + $whereClauses[] = $joinColumn['name'] . ' = ?'; + + $params[] = ($targetClass->containsForeignIdentifier) + ? $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])] + : $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]]; + } + + $filterTargetClass = $this->_em->getClassMetadata($targetClass->rootEntityName); + foreach ($this->_em->getFilters()->getEnabledFilters() as $filter) { + if ($filterExpr = $filter->addFilterConstraint($filterTargetClass, 't')) { + $whereClauses[] = '(' . $filterExpr . ')'; + } + } + + $sql = 'SELECT count(*)' + . ' FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' t' + . ' WHERE ' . implode(' AND ', $whereClauses); + + return $this->_conn->fetchColumn($sql, $params); + } + + /** + * @param PersistentCollection $coll + * @param int $offset + * @param int $length + * @return \Doctrine\Common\Collections\ArrayCollection + */ + public function slice(PersistentCollection $coll, $offset, $length = null) + { + $mapping = $coll->getMapping(); + $uow = $this->_em->getUnitOfWork(); + $persister = $uow->getEntityPersister($mapping['targetEntity']); + + return $persister->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length); + } + + /** + * @param PersistentCollection $coll + * @param object $element + * @return boolean + */ + public function contains(PersistentCollection $coll, $element) + { + $mapping = $coll->getMapping(); + $uow = $this->_em->getUnitOfWork(); + + // shortcut for new entities + $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); + + if ($entityState === UnitOfWork::STATE_NEW) { + return false; + } + + // Entity is scheduled for inclusion + if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { + return false; + } + + $persister = $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. + $id = current( $uow->getEntityIdentifier($coll->getOwner())); + + return $persister->exists($element, array($mapping['mappedBy'] => $id)); + } + + /** + * @param PersistentCollection $coll + * @param object $element + * @return boolean + */ + public function removeElement(PersistentCollection $coll, $element) + { + $uow = $this->_em->getUnitOfWork(); + + // shortcut for new entities + $entityState = $uow->getEntityState($element, 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 && $uow->isScheduledForInsert($element)) { + return false; + } + + $mapping = $coll->getMapping(); + $class = $this->_em->getClassMetadata($mapping['targetEntity']); + $sql = 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform) + . ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?'; + + return (bool) $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SingleTablePersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SingleTablePersister.php new file mode 100644 index 0000000..9fedcaa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SingleTablePersister.php @@ -0,0 +1,163 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\Common\Collections\Criteria; + +/** + * 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 _getSelectColumnListSQL() + { + if ($this->_selectColumnListSql !== null) { + return $this->_selectColumnListSql; + } + + $columnList = parent::_getSelectColumnListSQL(); + + $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->_rsm->setDiscriminatorColumn('r', $resultColumnName); + $this->_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'])) { + $columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass); + } + } + + // Foreign key columns + foreach ($subClass->associationMappings as $assoc) { + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) { + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + if ($columnList != '') $columnList .= ', '; + + $columnList .= $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + isset($assoc['inherited']) ? $assoc['inherited'] : $this->_class->name + ); + } + } + } + } + + $this->_selectColumnListSql = $columnList; + return $this->_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(); + } + + 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]); + } + + return $this->_getSQLTableAlias($this->_class->name) . '.' . $this->_class->discriminatorColumn['name'] + . ' IN (' . implode(', ', $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 dont 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/SqlExpressionVisitor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php new file mode 100644 index 0000000..2fb685f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php @@ -0,0 +1,102 @@ +. + */ + +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; + +/** + * Visit Expressions and generate SQL WHERE conditions from them. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class SqlExpressionVisitor extends ExpressionVisitor +{ + /** + * @var \Doctrine\ORM\Persisters\BasicEntityPersister + */ + private $persister; + + /** + * @param \Doctrine\ORM\Persisters\BasicEntityPersister $persister + */ + public function __construct(BasicEntityPersister $persister) + { + $this->persister = $persister; + } + + /** + * Convert 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() + + return $this->persister->getSelectConditionStatementSQL($field, $value, null, $comparison->getOperator()); + } + + /** + * Convert a composite expression into the target query language output + * + * @param \Doctrine\Common\Collections\Expr\CompositeExpression $expr + * + * @return mixed + */ + 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()); + } + } + + /** + * Convert 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..aa4d68d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php @@ -0,0 +1,100 @@ +. + */ + +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; + +use Doctrine\ORM\Mapping\ClassMetadata; + +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Connection; + +/** + * Extract the values from a criteria/expression + * + * @author Benjamin Eberlei + */ +class SqlValueVisitor extends ExpressionVisitor +{ + /** + * @var array + */ + private $values = array(); + + /** + * @var array + */ + private $types = array(); + + /** + * Convert a comparison expression into the target query language output + * + * @param \Doctrine\Common\Collections\Expr\Comparison $comparison + * + * @return mixed + */ + public function walkComparison(Comparison $comparison) + { + $value = $comparison->getValue()->getValue(); + $field = $comparison->getField(); + + $this->values[] = $value; + $this->types[] = array($field, $value); + } + + /** + * Convert 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); + } + } + + /** + * Convert a value expression into the target query language part. + * + * @param \Doctrine\Common\Collections\Expr\Value $value + * + * @return mixed + */ + public function walkValue(Value $value) + { + return; + } + + /** + * Return the Parameters and Types necessary for matching the last visited expression. + * + * @return array + */ + public function getParamsAndTypes() + { + return array($this->values, $this->types); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php new file mode 100644 index 0000000..ef844a7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php @@ -0,0 +1,8 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Pessimistic Lock Exception + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class PessimisticLockException extends ORMException +{ + 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..0b4d9a0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +/** + * Special Autoloader for Proxy classes because them not being PSR-0 compatible. + * + * @author Benjamin Eberlei + */ +class Autoloader +{ + /** + * Resolve proxy class name to a filename based on the following pattern. + * + * 1. Remove Proxy namespace from class name + * 2. Remove namespace seperators 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 + */ + static public function resolveFile($proxyDir, $proxyNamespace, $className) + { + if (0 !== strpos($className, $proxyNamespace)) { + throw ProxyException::notProxyClass($className, $proxyNamespace); + } + + $className = str_replace('\\', '', substr($className, strlen($proxyNamespace) +1)); + return $proxyDir . DIRECTORY_SEPARATOR . $className.'.php'; + } + + /** + * Register and return autoloader callback for the given proxy dir and + * namespace. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param Closure $notFoundCallback Invoked when the proxy file is not found. + * @return Closure + */ + static public function register($proxyDir, $proxyNamespace, \Closure $notFoundCallback = null) + { + $proxyNamespace = ltrim($proxyNamespace, "\\"); + $autoloader = function($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) { + if (0 === strpos($className, $proxyNamespace)) { + $file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + + if ($notFoundCallback && ! file_exists($file)) { + $notFoundCallback($proxyDir, $proxyNamespace, $className); + } + + require $file; + } + }; + spl_autoload_register($autoloader); + return $autoloader; + } +} + 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..47cda90 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\Common\Persistence\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/ProxyException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyException.php new file mode 100644 index 0000000..4e26d86 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyException.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +/** + * ORM Proxy Exception + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +class ProxyException extends \Doctrine\ORM\ORMException { + + public static function proxyDirectoryRequired() { + return new self("You must configure a proxy directory. See docs for details"); + } + + public static function proxyDirectoryNotWritable() { + return new self("Your proxy directory must be writable."); + } + + public static function proxyNamespaceRequired() { + return new self("You must configure a proxy namespace. See docs for details"); + } + + public static function notProxyClass($className, $proxyNamespace) + { + return new self(sprintf( + "The class %s is not part of the proxy namespace %s", + $className, $proxyNamespace + )); + } + +} 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..df74207 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -0,0 +1,410 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\ORM\EntityManager, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\Common\Util\ClassUtils; + +/** + * This factory is used to create proxy objects for entities at runtime. + * + * @author Roman Borschel + * @author Giorgio Sironi + * @since 2.0 + */ +class ProxyFactory +{ + /** The EntityManager this factory is bound to. */ + private $_em; + /** Whether to automatically (re)generate proxy classes. */ + private $_autoGenerate; + /** The namespace that contains all proxy classes. */ + private $_proxyNamespace; + /** The directory that contains all proxy classes. */ + private $_proxyDir; + + /** + * Used to match very simple id methods that don't need + * to be proxied since the identifier is known. + * + * @var string + */ + const PATTERN_MATCH_ID_METHOD = '((public\s)?(function\s{1,}%s\s?\(\)\s{1,})\s{0,}{\s{0,}return\s{0,}\$this->%s;\s{0,}})i'; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given EntityManager. + * + * @param EntityManager $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 $autoGenerate Whether to automatically generate proxy classes. + */ + public function __construct(EntityManager $em, $proxyDir, $proxyNs, $autoGenerate = false) + { + if ( ! $proxyDir) { + throw ProxyException::proxyDirectoryRequired(); + } + if ( ! $proxyNs) { + throw ProxyException::proxyNamespaceRequired(); + } + $this->_em = $em; + $this->_proxyDir = $proxyDir; + $this->_autoGenerate = $autoGenerate; + $this->_proxyNamespace = $proxyNs; + } + + /** + * Gets a reference proxy instance for the entity of the given type and identified by + * the given identifier. + * + * @param string $className + * @param mixed $identifier + * @return object + */ + public function getProxy($className, $identifier) + { + $fqn = ClassUtils::generateProxyClassName($className, $this->_proxyNamespace); + + if (! class_exists($fqn, false)) { + $fileName = $this->getProxyFileName($className); + if ($this->_autoGenerate) { + $this->_generateProxyClass($this->_em->getClassMetadata($className), $fileName, self::$_proxyClassTemplate); + } + require $fileName; + } + + $entityPersister = $this->_em->getUnitOfWork()->getEntityPersister($className); + + return new $fqn($entityPersister, $identifier); + } + + /** + * Generate the Proxy file name + * + * @param string $className + * @param string $baseDir 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 + */ + private function getProxyFileName($className, $baseDir = null) + { + $proxyDir = $baseDir ?: $this->_proxyDir; + + return $proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php'; + } + + /** + * Generates proxy classes for all given classes. + * + * @param array $classes The classes (ClassMetadata instances) for which to generate proxies. + * @param string $toDir 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, $toDir = null) + { + $proxyDir = $toDir ?: $this->_proxyDir; + $proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR); + $num = 0; + + foreach ($classes as $class) { + /* @var $class ClassMetadata */ + if ($class->isMappedSuperclass || $class->reflClass->isAbstract()) { + continue; + } + + $proxyFileName = $this->getProxyFileName($class->name, $proxyDir); + + $this->_generateProxyClass($class, $proxyFileName, self::$_proxyClassTemplate); + $num++; + } + + return $num; + } + + /** + * Generates a proxy class file. + * + * @param ClassMetadata $class Metadata for the original class + * @param string $fileName Filename (full path) for the generated class + * @param string $file The proxy class template data + */ + private function _generateProxyClass(ClassMetadata $class, $fileName, $file) + { + $methods = $this->_generateMethods($class); + $sleepImpl = $this->_generateSleep($class); + $cloneImpl = $class->reflClass->hasMethod('__clone') ? 'parent::__clone();' : ''; // hasMethod() checks case-insensitive + + $placeholders = array( + '', + '', '', + '', '', '' + ); + + $className = ltrim($class->name, '\\'); + $proxyClassName = ClassUtils::generateProxyClassName($class->name, $this->_proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + $proxyClassNamespace = strrev($parts[1]); + $proxyClassName = strrev($parts[0]); + + $replacements = array( + $proxyClassNamespace, + $proxyClassName, + $className, + $methods, + $sleepImpl, + $cloneImpl + ); + + $file = str_replace($placeholders, $replacements, $file); + + $parentDirectory = dirname($fileName); + + if ( ! is_dir($parentDirectory)) { + if (false === @mkdir($parentDirectory, 0775, true)) { + throw ProxyException::proxyDirectoryNotWritable(); + } + } else if ( ! is_writable($parentDirectory)) { + throw ProxyException::proxyDirectoryNotWritable(); + } + + $tmpFileName = $fileName . '.' . uniqid("", true); + file_put_contents($tmpFileName, $file); + rename($tmpFileName, $fileName); + } + + /** + * Generates the methods of a proxy class. + * + * @param ClassMetadata $class + * @return string The code of the generated methods. + */ + private function _generateMethods(ClassMetadata $class) + { + $methods = ''; + + $methodNames = array(); + foreach ($class->reflClass->getMethods() as $method) { + /* @var $method ReflectionMethod */ + if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone")) || isset($methodNames[$method->getName()])) { + continue; + } + $methodNames[$method->getName()] = true; + + if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) { + $methods .= "\n" . ' public function '; + if ($method->returnsReference()) { + $methods .= '&'; + } + $methods .= $method->getName() . '('; + $firstParam = true; + $parameterString = $argumentString = ''; + + foreach ($method->getParameters() as $param) { + if ($firstParam) { + $firstParam = false; + } else { + $parameterString .= ', '; + $argumentString .= ', '; + } + + // We need to pick the type hint class too + if (($paramClass = $param->getClass()) !== null) { + $parameterString .= '\\' . $paramClass->getName() . ' '; + } else if ($param->isArray()) { + $parameterString .= 'array '; + } + + if ($param->isPassedByReference()) { + $parameterString .= '&'; + } + + $parameterString .= '$' . $param->getName(); + $argumentString .= '$' . $param->getName(); + + if ($param->isDefaultValueAvailable()) { + $parameterString .= ' = ' . var_export($param->getDefaultValue(), true); + } + } + + $methods .= $parameterString . ')'; + $methods .= "\n" . ' {' . "\n"; + if ($this->isShortIdentifierGetter($method, $class)) { + $identifier = lcfirst(substr($method->getName(), 3)); + + $cast = in_array($class->fieldMappings[$identifier]['type'], array('integer', 'smallint')) ? '(int) ' : ''; + + $methods .= ' if ($this->__isInitialized__ === false) {' . "\n"; + $methods .= ' return ' . $cast . '$this->_identifier["' . $identifier . '"];' . "\n"; + $methods .= ' }' . "\n"; + } + $methods .= ' $this->__load();' . "\n"; + $methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');'; + $methods .= "\n" . ' }' . "\n"; + } + } + + return $methods; + } + + /** + * Check 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 ClassMetadata $class + * @return bool + */ + private function isShortIdentifierGetter($method, ClassMetadata $class) + { + $identifier = lcfirst(substr($method->getName(), 3)); + $cheapCheck = ( + $method->getNumberOfParameters() == 0 && + substr($method->getName(), 0, 3) == "get" && + in_array($identifier, $class->identifier, true) && + $class->hasField($identifier) && + (($method->getEndLine() - $method->getStartLine()) <= 4) + && in_array($class->fieldMappings[$identifier]['type'], array('integer', 'bigint', 'smallint', 'string')) + ); + + if ($cheapCheck) { + $code = file($method->getDeclaringClass()->getFileName()); + $code = trim(implode(" ", array_slice($code, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1))); + + $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier); + + if (preg_match($pattern, $code)) { + return true; + } + } + return false; + } + + /** + * Generates the code for the __sleep method for a proxy class. + * + * @param $class + * @return string + */ + private function _generateSleep(ClassMetadata $class) + { + $sleepImpl = ''; + + if ($class->reflClass->hasMethod('__sleep')) { + $sleepImpl .= "return array_merge(array('__isInitialized__'), parent::__sleep());"; + } else { + $sleepImpl .= "return array('__isInitialized__', "; + $first = true; + + foreach ($class->getReflectionProperties() as $name => $prop) { + if ($first) { + $first = false; + } else { + $sleepImpl .= ', '; + } + + $sleepImpl .= "'" . $name . "'"; + } + + $sleepImpl .= ');'; + } + + return $sleepImpl; + } + + /** Proxy class code template */ + private static $_proxyClassTemplate = +'; + +/** + * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. + */ +class extends \ implements \Doctrine\ORM\Proxy\Proxy +{ + private $_entityPersister; + private $_identifier; + public $__isInitialized__ = false; + public function __construct($entityPersister, $identifier) + { + $this->_entityPersister = $entityPersister; + $this->_identifier = $identifier; + } + /** @private */ + public function __load() + { + if (!$this->__isInitialized__ && $this->_entityPersister) { + $this->__isInitialized__ = true; + + if (method_exists($this, "__wakeup")) { + // call this after __isInitialized__to avoid infinite recursion + // but before loading to emulate what ClassMetadata::newInstance() + // provides. + $this->__wakeup(); + } + + if ($this->_entityPersister->load($this->_identifier, $this) === null) { + throw new \Doctrine\ORM\EntityNotFoundException(); + } + unset($this->_entityPersister, $this->_identifier); + } + } + + /** @private */ + public function __isInitialized() + { + return $this->__isInitialized__; + } + + + + public function __sleep() + { + + } + + public function __clone() + { + if (!$this->__isInitialized__ && $this->_entityPersister) { + $this->__isInitialized__ = true; + $class = $this->_entityPersister->getClassMetadata(); + $original = $this->_entityPersister->load($this->_identifier); + if ($original === null) { + throw new \Doctrine\ORM\EntityNotFoundException(); + } + foreach ($class->reflFields as $field => $reflProperty) { + $reflProperty->setValue($this, $reflProperty->getValue($original)); + } + unset($this->_entityPersister, $this->_identifier); + } + + } +}'; +} 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..2e1b817 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php @@ -0,0 +1,626 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Collections\ArrayCollection; + +use Doctrine\DBAL\LockMode; + +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\ParserResult; +use Doctrine\ORM\Query\QueryException; + +/** + * 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'; + + + /** + * 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'; + + + /** + * @var integer $_state The current state of this query. + */ + private $_state = self::STATE_CLEAN; + + /** + * @var string $_dql Cached DQL query. + */ + private $_dql = null; + + /** + * @var \Doctrine\ORM\Query\ParserResult The parser result that holds DQL => SQL information. + */ + private $_parserResult; + + /** + * @var integer The first result to return (the "offset"). + */ + private $_firstResult = null; + + /** + * @var integer The maximum number of results to return (the "limit"). + */ + private $_maxResults = null; + + /** + * @var CacheDriver The cache driver used for caching queries. + */ + private $_queryCache; + + /** + * @var boolean Boolean value that indicates whether or not expire the query cache. + */ + private $_expireQueryCache = false; + + /** + * @var int Query Cache lifetime. + */ + private $_queryCacheTTL; + + /** + * @var boolean Whether to use a query cache, if available. Defaults to TRUE. + */ + private $_useQueryCache = true; + + /** + * Initializes a new Query instance. + * + * @param \Doctrine\ORM\EntityManager $entityManager + */ + /*public function __construct(EntityManager $entityManager) + { + parent::__construct($entityManager); + }*/ + + /** + * 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(); + } + + /** + * 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() + { + // Return previous parser result if the query and the filter collection are both clean + if ($this->_state === self::STATE_CLEAN && $this->_em->isFiltersStateClean()) { + return $this->_parserResult; + } + + $this->_state = self::STATE_CLEAN; + + // 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); + } + + // Prepare parameters + $paramMappings = $this->_parserResult->getParameterMappings(); + + if (count($paramMappings) != count($this->parameters)) { + throw QueryException::invalidParameterNumber(); + } + + list($sqlParams, $types) = $this->processParameterMappings($paramMappings); + + if ($this->_resultSetMapping === null) { + $this->_resultSetMapping = $this->_parserResult->getResultSetMapping(); + } + + return $executor->execute($this->_em->getConnection(), $sqlParams, $types); + } + + /** + * Processes query parameter mappings + * + * @param array $paramMappings + * @return array + */ + private function processParameterMappings($paramMappings) + { + $sqlParams = array(); + $types = array(); + + foreach ($this->parameters as $parameter) { + $key = $parameter->getName(); + + if ( ! isset($paramMappings[$key])) { + throw QueryException::unknownParameter($key); + } + + $value = $this->processParameterValue($parameter->getValue()); + $type = ($parameter->getValue() === $value) + ? $parameter->getType() + : Query\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::parameterTypeMissmatch(); + } + + 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_Cache_Interface|null $driver 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 @return Query This query instance. + */ + public function useQueryCache($bool) + { + $this->_useQueryCache = $bool; + + return $this; + } + + /** + * Returns the cache driver used for query caching. + * + * @return CacheDriver 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 Return 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 \Doctrine\Common\Collections\ArrayCollection|array $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 + */ + public function setLockMode($lockMode) + { + if (in_array($lockMode, array(LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE))) { + 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 + */ + public function getLockMode() + { + $lockMode = $this->getHint(self::HINT_LOCK_MODE); + + if ( ! $lockMode) { + return LockMode::NONE; + } + + return $lockMode; + } + + /** + * Generate a cache id for the query cache - reusing the Result-Cache-Id generator. + * + * The query cache + * + * @return string + */ + protected function _getQueryCacheId() + { + ksort($this->_hints); + + return md5( + $this->getDql() . var_export($this->_hints, true) . + ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') . + '&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults . + '&hydrationMode='.$this->_hydrationMode.'DOCTRINE_QUERY_CACHE_SALT' + ); + } + + /** + * 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..4633322 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php @@ -0,0 +1,33 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +use Doctrine\ORM\Query\QueryException; + +/** + * Base exception class for AST exceptions. + */ +class ASTException extends QueryException +{ + public static function noDispatchForNode($node) + { + return new self("Double-dispatch for node " . get_class($node) . " is not supported."); + } +} \ No newline at end of file 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..ec91ada --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/AggregateExpression.php @@ -0,0 +1,49 @@ +. + */ + +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 +{ + public $functionName; + public $pathExpression; + public $isDistinct = false; // Some aggregate expressions support distinct, eg COUNT + + public function __construct($functionName, $pathExpression, $isDistinct) + { + $this->functionName = $functionName; + $this->pathExpression = $pathExpression; + $this->isDistinct = $isDistinct; + } + + 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..55a6b06 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php @@ -0,0 +1,51 @@ +. + */ + +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 +{ + public $simpleArithmeticExpression; + public $subselect; + + public function isSimpleArithmeticExpression() + { + return (bool) $this->simpleArithmeticExpression; + } + + public function isSubselect() + { + return (bool) $this->subselect; + } + + 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..8d595ef --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php @@ -0,0 +1,64 @@ +. + */ + +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 ArithmeticPrimary + */ + public $arithmeticPrimary; + + /** + * @var null|boolean NULL represents no sign, TRUE means positive and FALSE means negative sign + */ + public $sign; + + public function __construct($arithmeticPrimary, $sign = null) + { + $this->arithmeticPrimary = $arithmeticPrimary; + $this->sign = $sign; + } + + public function isPositiveSigned() + { + return $this->sign === true; + } + + public function isNegativeSigned() + { + return $this->sign === false; + } + + 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..ced25e9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php @@ -0,0 +1,45 @@ +. + */ + +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 +{ + public $arithmeticFactors; + + public function __construct(array $arithmeticFactors) + { + $this->arithmeticFactors = $arithmeticFactors; + } + + 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..83ba6ca --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php @@ -0,0 +1,51 @@ +. + */ + +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 +{ + public $expression; + public $leftBetweenExpression; + public $rightBetweenExpression; + public $not; + + public function __construct($expr, $leftExpr, $rightExpr) + { + $this->expression = $expr; + $this->leftBetweenExpression = $leftExpr; + $this->rightBetweenExpression = $rightExpr; + } + + 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..731d45e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php @@ -0,0 +1,47 @@ +. + */ + +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 +{ + public $scalarExpressions = array(); + + + public function __construct(array $scalarExpressions) + { + $this->scalarExpressions = $scalarExpressions; + } + + 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..b8c7b53 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php @@ -0,0 +1,49 @@ +. + */ + +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; + public $collectionValuedPathExpression; + public $not; + + public function __construct($entityExpr, $collValuedPathExpr) + { + $this->entityExpression = $entityExpr; + $this->collectionValuedPathExpression = $collValuedPathExpr; + } + + 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..d3105a1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php @@ -0,0 +1,54 @@ +. + */ + +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 +{ + public $leftExpression; + public $rightExpression; + public $operator; + + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->leftExpression = $leftExpr; + $this->rightExpression = $rightExpr; + $this->operator = $operator; + } + + 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..c302aa2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php @@ -0,0 +1,45 @@ +. + */ + +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 +{ + public $conditionalTerms = array(); + + public function __construct(array $conditionalTerms) + { + $this->conditionalTerms = $conditionalTerms; + } + + 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..b17089b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php @@ -0,0 +1,46 @@ +. + */ + +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 +{ + public $not = false; + public $conditionalPrimary; + + public function __construct($conditionalPrimary) + { + $this->conditionalPrimary = $conditionalPrimary; + } + + 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..8a7c0b7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php @@ -0,0 +1,51 @@ +. + */ + +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 +{ + public $simpleConditionalExpression; + public $conditionalExpression; + + public function isSimpleConditionalExpression() + { + return (bool) $this->simpleConditionalExpression; + } + + public function isConditionalExpression() + { + return (bool) $this->conditionalExpression; + } + + 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..d24defc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php @@ -0,0 +1,44 @@ +. + */ +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 +{ + public $conditionalFactors = array(); + + public function __construct(array $conditionalFactors) + { + $this->conditionalFactors = $conditionalFactors; + } + + 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..a05e52f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php @@ -0,0 +1,47 @@ +. + */ + +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 +{ + public $abstractSchemaName; + public $aliasIdentificationVariable; + + public function __construct($abstractSchemaName) + { + $this->abstractSchemaName = $abstractSchemaName; + } + + 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..f6e8cb3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php @@ -0,0 +1,46 @@ +. + */ + +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 +{ + public $deleteClause; + public $whereClause; + + public function __construct($deleteClause) + { + $this->deleteClause = $deleteClause; + } + + 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..fbe504c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php @@ -0,0 +1,47 @@ +. + */ + +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 +{ + public $expression; + public $not; + + public function __construct($expression) + { + $this->expression = $expression; + } + + 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..94ee55d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php @@ -0,0 +1,47 @@ +. + */ + +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 +{ + public $not; + public $subselect; + + public function __construct($subselect) + { + $this->subselect = $subselect; + } + + 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..32c7a73 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php @@ -0,0 +1,45 @@ +. + */ + +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 +{ + public $identificationVariableDeclarations = array(); + + public function __construct(array $identificationVariableDeclarations) + { + $this->identificationVariableDeclarations = $identificationVariableDeclarations; + } + + 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..d771f05 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php @@ -0,0 +1,62 @@ +. + */ + +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 +{ + 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..39ecd2d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CONCAT" "(" 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 $secondStringPriamry; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + return $platform->getConcatExpression( + $sqlWalker->walkStringPrimary($this->firstStringPrimary), + $sqlWalker->walkStringPrimary($this->secondStringPrimary) + ); + } + + /** + * @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(); + + $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..bf56785 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php @@ -0,0 +1,77 @@ +. + */ + +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; + + public function getSql(SqlWalker $sqlWalker) + { + switch (strtolower($this->unit->value)) { + 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 day and month.' + ); + } + } + + 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..f21b9a0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php @@ -0,0 +1,58 @@ +. + */ + +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; + + public function getSql(SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateDiffExpression( + $this->date1->dispatch($sqlWalker), + $this->date2->dispatch($sqlWalker) + ); + } + + 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..0cc0730 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php @@ -0,0 +1,57 @@ +. + */ + +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 +{ + public function getSql(SqlWalker $sqlWalker) + { + switch (strtolower($this->unit->value)) { + 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 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..bd8b607 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\AST\Node; + +/** + * Abtract 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 +{ + public $name; + + public function __construct($name) + { + $this->name = $name; + } + + abstract public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker); + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkFunction($this); + } + + 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..2733399 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "IDENTITY" "(" SingleValuedAssociationPathExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class IdentityFunction extends FunctionNode +{ + public $pathExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $dqlAlias = $this->pathExpression->identificationVariable; + $assocField = $this->pathExpression->field; + + $qComp = $sqlWalker->getQueryComponent($dqlAlias); + $class = $qComp['metadata']; + $assoc = $class->associationMappings[$assocField]; + + $tableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); + + return $tableAlias . '.' . reset($assoc['targetToSourceKeyColumns']);; + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->pathExpression = $parser->SingleValuedAssociationPathExpression(); + + $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..9c6f770 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php @@ -0,0 +1,62 @@ +. + */ + +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..a189082 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php @@ -0,0 +1,81 @@ +. + */ + +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; + 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..12c6745 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php @@ -0,0 +1,62 @@ +. + */ + +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..27d252e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php @@ -0,0 +1,68 @@ +. + */ + +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 +{ + public $firstSimpleArithmeticExpression; + 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..bffff29 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php @@ -0,0 +1,121 @@ +. + */ + +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 +{ + 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..cf8c27b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php @@ -0,0 +1,62 @@ +. + */ + +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 +{ + 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..6f3dfbe --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php @@ -0,0 +1,82 @@ +. + */ + +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; + public $firstSimpleArithmeticExpression; + 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..0c60ab0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +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 +{ + public $leading; + public $trailing; + public $both; + public $trimChar = false; + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $pos = AbstractPlatform::TRIM_UNSPECIFIED; + if ($this->leading) { + $pos = AbstractPlatform::TRIM_LEADING; + } else if ($this->trailing) { + $pos = AbstractPlatform::TRIM_TRAILING; + } else if ($this->both) { + $pos = AbstractPlatform::TRIM_BOTH; + } + + return $sqlWalker->getConnection()->getDatabasePlatform()->getTrimExpression( + $sqlWalker->walkStringPrimary($this->stringPrimary), + $pos, + ($this->trimChar != false) ? $sqlWalker->getConnection()->quote($this->trimChar) : false + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $lexer = $parser->getLexer(); + + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + if (strcasecmp('leading', $lexer->lookahead['value']) === 0) { + $parser->match(Lexer::T_LEADING); + $this->leading = true; + } else if (strcasecmp('trailing', $lexer->lookahead['value']) === 0) { + $parser->match(Lexer::T_TRAILING); + $this->trailing = true; + } else if (strcasecmp('both', $lexer->lookahead['value']) === 0) { + $parser->match(Lexer::T_BOTH); + $this->both = true; + } + + 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); + } + +} 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..26c8f0d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php @@ -0,0 +1,62 @@ +. + */ + +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..ee4b418 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php @@ -0,0 +1,48 @@ +. + */ + +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 +{ + public $whenClauses = array(); + public $elseScalarExpression = null; + + public function __construct(array $whenClauses, $elseScalarExpression) + { + $this->whenClauses = $whenClauses; + $this->elseScalarExpression = $elseScalarExpression; + } + + 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..06350f0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php @@ -0,0 +1,45 @@ +. + */ + +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 +{ + public $groupByItems = array(); + + public function __construct(array $groupByItems) + { + $this->groupByItems = $groupByItems; + } + + 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..971e919 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php @@ -0,0 +1,45 @@ +. + */ + +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 +{ + public $conditionalExpression; + + public function __construct($conditionalExpression) + { + $this->conditionalExpression = $conditionalExpression; + } + + 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..6bbdaaa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php @@ -0,0 +1,49 @@ +. + */ + +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 +{ + public $rangeVariableDeclaration = null; + public $indexBy = null; + public $joins = array(); + + public function __construct($rangeVariableDecl, $indexBy, array $joins) + { + $this->rangeVariableDeclaration = $rangeVariableDecl; + $this->indexBy = $indexBy; + $this->joins = $joins; + } + + 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..39501a4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php @@ -0,0 +1,48 @@ +. + */ +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 +{ + public $not; + public $expression; + public $literals = array(); + public $subselect; + + public function __construct($expression) + { + $this->expression = $expression; + } + + 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..08e9e6a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php @@ -0,0 +1,45 @@ +. + */ + +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 +{ + public $simpleStateFieldPathExpression = null; + + public function __construct($simpleStateFieldPathExpression) + { + $this->simpleStateFieldPathExpression = $simpleStateFieldPathExpression; + } + + 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..d575df7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php @@ -0,0 +1,55 @@ +. + */ + +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 +{ + public $isNamed; + public $name; + + /** + * @param string $value + */ + 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; + } + + 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..c022cd6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php @@ -0,0 +1,49 @@ +. + */ + +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 +{ + public $not; + public $identificationVariable; + public $value; + + public function __construct($identVariable) + { + $this->identificationVariable = $identVariable; + } + + 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..6724f9e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php @@ -0,0 +1,52 @@ +. + */ + +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; + + public $joinType = self::JOIN_TYPE_INNER; + public $joinAssociationDeclaration = null; + public $conditionalExpression = null; + + public function __construct($joinType, $joinAssociationDeclaration) + { + $this->joinType = $joinType; + $this->joinAssociationDeclaration = $joinAssociationDeclaration; + } + + 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..8e97353 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Guilherme Blanco + */ +class JoinAssociationDeclaration extends Node +{ + public $joinAssociationPathExpression; + public $aliasIdentificationVariable; + public $indexBy; + + public function __construct($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy) + { + $this->joinAssociationPathExpression = $joinAssociationPathExpression; + $this->aliasIdentificationVariable = $aliasIdentificationVariable; + $this->indexBy = $indexBy; + } + + 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..57bd052 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php @@ -0,0 +1,46 @@ +. + */ + +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 +{ + public $identificationVariable; + public $associationField; + + public function __construct($identificationVariable, $associationField) + { + $this->identificationVariable = $identificationVariable; + $this->associationField = $associationField; + } + + 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..a6bafea --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinClassPathExpression ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.3 + * @author Alexander + */ +class JoinClassPathExpression extends Node +{ + public $abstractSchemaName; + public $aliasIdentificationVariable; + + public function __construct($abstractSchemaName, $aliasIdentificationVar) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->aliasIdentificationVariable = $aliasIdentificationVar; + } + + public function dispatch($walker) + { + return $walker->walkJoinPathExpression($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..68f8635 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php @@ -0,0 +1,49 @@ +. + */ +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 +{ + public $not; + public $stringExpression; + public $stringPattern; + public $escapeChar; + + public function __construct($stringExpression, $stringPattern, $escapeChar = null) + { + $this->stringExpression = $stringExpression; + $this->stringPattern = $stringPattern; + $this->escapeChar = $escapeChar; + } + + 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..26337cb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +class Literal extends Node +{ + const STRING = 1; + const BOOLEAN = 2; + const NUMERIC = 3; + + public $type; + public $value; + + public function __construct($type, $value) + { + $this->type = $type; + $this->value = $value; + } + + public function dispatch($walker) + { + return $walker->walkLiteral($this); + } +} \ No newline at end of file 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..d50e0d0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php @@ -0,0 +1,95 @@ +. + */ + +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 $walker + */ + 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); + } + + 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..d0fb8bf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php @@ -0,0 +1,47 @@ +. + */ + +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 +{ + public $not; + public $expression; + + public function __construct($expression) + { + $this->expression = $expression; + } + + 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..2c85ddd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php @@ -0,0 +1,49 @@ +. + */ + +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 +{ + public $firstExpression; + + public $secondExpression; + + public function __construct($firstExpression, $secondExpression) + { + $this->firstExpression = $firstExpression; + $this->secondExpression = $secondExpression; + } + + 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..311a9ed --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php @@ -0,0 +1,45 @@ +. + */ + +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 +{ + public $orderByItems = array(); + + public function __construct(array $orderByItems) + { + $this->orderByItems = $orderByItems; + } + + 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..5d48077 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php @@ -0,0 +1,56 @@ +. + */ + +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 +{ + public $expression; + public $type; + + public function __construct($expression) + { + $this->expression = $expression; + } + + public function isAsc() + { + return strtoupper($this->type) == 'ASC'; + } + + public function isDesc() + { + return strtoupper($this->type) == 'DESC'; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkOrderByItem($this); + } +} 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..8a0e0d0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +class PartialObjectExpression extends Node +{ + public $identificationVariable; + public $partialFieldSet; + + public function __construct($identificationVariable, array $partialFieldSet) + { + $this->identificationVariable = $identificationVariable; + $this->partialFieldSet = $partialFieldSet; + } +} \ No newline at end of file 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..1b27742 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php @@ -0,0 +1,58 @@ +. + */ + +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; + + public $type; + public $expectedType; + public $identificationVariable; + public $field; + + public function __construct($expectedType, $identificationVariable, $field = null) + { + $this->expectedType = $expectedType; + $this->identificationVariable = $identificationVariable; + $this->field = $field; + } + + 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..64a764e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php @@ -0,0 +1,64 @@ +. + */ +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 +{ + public $type; + public $subselect; + + public function __construct($subselect) + { + $this->subselect = $subselect; + } + + public function isAll() + { + return strtoupper($this->type) == 'ALL'; + } + + public function isAny() + { + return strtoupper($this->type) == 'ANY'; + } + + public function isSome() + { + return strtoupper($this->type) == 'SOME'; + } + + /** + * @override + */ + 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..facd0bb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php @@ -0,0 +1,47 @@ +. + */ + +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 +{ + public $abstractSchemaName; + public $aliasIdentificationVariable; + + public function __construct($abstractSchemaName, $aliasIdentificationVar) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->aliasIdentificationVariable = $aliasIdentificationVar; + } + + 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..be76a4f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php @@ -0,0 +1,47 @@ +. + */ + +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 +{ + public $isDistinct; + public $selectExpressions = array(); + + public function __construct(array $selectExpressions, $isDistinct) + { + $this->isDistinct = $isDistinct; + $this->selectExpressions = $selectExpressions; + } + + 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..14ca850 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php @@ -0,0 +1,50 @@ +. + */ + +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 +{ + public $expression; + public $fieldIdentificationVariable; + public $hiddenAliasResultVariable; + + public function __construct($expression, $fieldIdentificationVariable, $hiddenAliasResultVariable = false) + { + $this->expression = $expression; + $this->fieldIdentificationVariable = $fieldIdentificationVariable; + $this->hiddenAliasResultVariable = $hiddenAliasResultVariable; + } + + 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..d91d26f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php @@ -0,0 +1,50 @@ +. + */ + +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 +{ + public $selectClause; + public $fromClause; + public $whereClause; + public $groupByClause; + public $havingClause; + public $orderByClause; + + public function __construct($selectClause, $fromClause) { + $this->selectClause = $selectClause; + $this->fromClause = $fromClause; + } + + 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..8d91c9b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php @@ -0,0 +1,45 @@ +. + */ + +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 +{ + public $arithmeticTerms = array(); + + public function __construct(array $arithmeticTerms) + { + $this->arithmeticTerms = $arithmeticTerms; + } + + 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..bd593ee --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php @@ -0,0 +1,50 @@ +. + */ + +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 +{ + public $caseOperand = null; + public $simpleWhenClauses = array(); + public $elseScalarExpression = null; + + public function __construct($caseOperand, array $simpleWhenClauses, $elseScalarExpression) + { + $this->caseOperand = $caseOperand; + $this->simpleWhenClauses = $simpleWhenClauses; + $this->elseScalarExpression = $elseScalarExpression; + } + + 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..95a6198 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php @@ -0,0 +1,47 @@ +. + */ + +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 +{ + public $isDistinct = false; + public $simpleSelectExpression; + + public function __construct($simpleSelectExpression, $isDistinct) + { + $this->simpleSelectExpression = $simpleSelectExpression; + $this->isDistinct = $isDistinct; + } + + 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..25e927e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php @@ -0,0 +1,47 @@ +. + */ + +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 +{ + public $expression; + public $fieldIdentificationVariable; + + public function __construct($expression) + { + $this->expression = $expression; + } + + 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..fabe449 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php @@ -0,0 +1,48 @@ +. + */ + +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 +{ + public $caseScalarExpression = null; + public $thenScalarExpression = null; + + public function __construct($caseScalarExpression, $thenScalarExpression) + { + $this->caseScalarExpression = $caseScalarExpression; + $this->thenScalarExpression = $thenScalarExpression; + } + + 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..46c0e96 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php @@ -0,0 +1,51 @@ +. + */ + +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 +{ + public $simpleSelectClause; + public $subselectFromClause; + public $whereClause; + public $groupByClause; + public $havingClause; + public $orderByClause; + + public function __construct($simpleSelectClause, $subselectFromClause) + { + $this->simpleSelectClause = $simpleSelectClause; + $this->subselectFromClause = $subselectFromClause; + } + + 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..3a6b547 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php @@ -0,0 +1,45 @@ +. + */ + +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 +{ + public $identificationVariableDeclarations = array(); + + public function __construct(array $identificationVariableDeclarations) + { + $this->identificationVariableDeclarations = $identificationVariableDeclarations; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSubselectFromClause($this); + } +} 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..4188cf5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php @@ -0,0 +1,49 @@ +. + */ + +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 +{ + public $abstractSchemaName; + public $aliasIdentificationVariable; + public $updateItems = array(); + + public function __construct($abstractSchemaName, array $updateItems) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->updateItems = $updateItems; + } + + 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..2a13391 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php @@ -0,0 +1,50 @@ +. + */ + +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 +{ + public $pathExpression; + public $newValue; + + public function __construct($pathExpression, $newValue) + { + $this->pathExpression = $pathExpression; + $this->newValue = $newValue; + } + + 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..9e25d83 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php @@ -0,0 +1,45 @@ +. + */ +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 +{ + public $updateClause; + public $whereClause; + + public function __construct($updateClause) + { + $this->updateClause = $updateClause; + } + + 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..ca91f57 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php @@ -0,0 +1,48 @@ +. + */ + +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 +{ + public $caseConditionExpression = null; + public $thenScalarExpression = null; + + public function __construct($caseConditionExpression, $thenScalarExpression) + { + $this->caseConditionExpression = $caseConditionExpression; + $this->thenScalarExpression = $thenScalarExpression; + } + + 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..0b9f160 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php @@ -0,0 +1,45 @@ +. + */ + +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 +{ + public $conditionalExpression; + + public function __construct($conditionalExpression) + { + $this->conditionalExpression = $conditionalExpression; + } + + 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..d20905a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php @@ -0,0 +1,70 @@ +. + */ + +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/lgpl-license.php LGPL + * @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; + } + + public function setQueryCacheProfile(QueryCacheProfile $qcp) + { + $this->queryCacheProfile = $qcp; + } + + /** + * Executes all sql statements. + * + * @param \Doctrine\DBAL\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..d40b37e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php @@ -0,0 +1,133 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection, + 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/lgpl-license.php LGPL + * @link http://www.doctrine-project.org + * @since 2.0 + */ +class MultiTableDeleteExecutor extends AbstractSqlExecutor +{ + private $_createTempTableSql; + private $_dropTempTableSql; + private $_insertSql; + + /** + * Initializes a new MultiTableDeleteExecutor. + * + * @param Node $AST The root AST node of the DQL query. + * @param SqlWalker $sqlWalker The walker used for SQL generation from the AST. + * @internal Any SQL construction and preparation takes place in the constructor for + * best performance. With a query cache the executor will be cached. + */ + 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..60951ee --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -0,0 +1,186 @@ +. + */ + +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 +{ + private $_createTempTableSql; + private $_dropTempTableSql; + private $_insertSql; + private $_sqlParameters = array(); + private $_numParametersInUpdateClause = 0; + + /** + * Initializes a new MultiTableUpdateExecutor. + * + * @param Node $AST The root AST node of the DQL query. + * @param SqlWalker $sqlWalker The walker used for SQL generation from the AST. + * @internal Any SQL construction and preparation takes place in the constructor for + * best performance. With a query cache the executor will be cached. + */ + 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..61ae2fa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection, + Doctrine\ORM\Query\AST\SelectStatement, + Doctrine\ORM\Query\SqlWalker; + +/** + * Executor that executes the SQL statement for simple DQL SELECT statements. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Roman Borschel + * @link www.doctrine-project.org + * @since 2.0 + */ +class SingleSelectExecutor extends AbstractSqlExecutor +{ + 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..b9bfd6f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection, + 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/lgpl-license.php LGPL + * @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 +{ + 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..5ffedba --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php @@ -0,0 +1,595 @@ +. + */ + +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 = 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 = 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 $sort + * @return Expr\OrderBy + */ + public function asc($expr) + { + return new Expr\OrderBy($expr, 'ASC'); + } + + /** + * Creates a DESCending order expression. + * + * @param $sort + * @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.percentAnualSalaryIncrease + * $q->expr()->prod('u.salary', 'u.percentAnualSalaryIncrease') + * + * @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 CONCAT() function expression with the given arguments. + * + * @param mixed $x First argument to be used in CONCAT() function. + * @param mixed $x 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 integer $from Initial offset to start cropping string. May accept negative values. + * @param integer $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); + } +} 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..10b5e8e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php @@ -0,0 +1,56 @@ +. + */ + +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..0fee7b2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php @@ -0,0 +1,121 @@ +. + */ + +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 + */ + 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..52070df --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php @@ -0,0 +1,101 @@ +. + */ + +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..cc0e0e8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php @@ -0,0 +1,72 @@ +. + */ + +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..3a37703 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php @@ -0,0 +1,93 @@ +. + */ + +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..6917dd2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php @@ -0,0 +1,79 @@ +. + */ + +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..40bb838 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php @@ -0,0 +1,51 @@ +. + */ + +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..3e6546e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php @@ -0,0 +1,147 @@ +. + */ + +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 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 $alias The alias of the join + * @param string $conditionType The condition type constant. Either ON or WITH. + * @param string $condition The condition for the join + * @param string $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..991b044 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php @@ -0,0 +1,52 @@ +. + */ + +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..a41c848 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Math.php @@ -0,0 +1,108 @@ +. + */ + +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..e0e7ffc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php @@ -0,0 +1,103 @@ +. + */ + +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 $sort + * @param string $order + */ + public function __construct($sort = null, $order = null) + { + if ($sort) { + $this->add($sort, $order); + } + } + + /** + * @param string $sort + * @param string $order + */ + 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..caecba6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php @@ -0,0 +1,56 @@ +. + */ + +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..804d797 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php @@ -0,0 +1,58 @@ +. + */ + +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..ce2096e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php @@ -0,0 +1,122 @@ +. + */ + +namespace Doctrine\ORM\Query\Filter; + +use Doctrine\ORM\EntityManager; +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 EntityManager + */ + private $em; + + /** + * Parameters for the filter. + * @var array + */ + private $parameters; + + /** + * Constructs the SQLFilter object. + * + * @param EntityManager $em The EM + */ + final public function __construct(EntityManager $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 $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. + */ + 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']); + } + + /** + * 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); + } + + /** + * Gets the SQL query part to add to a query. + * + * @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..fc47eb1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php @@ -0,0 +1,198 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\Configuration, + Doctrine\ORM\EntityManager; + +/** + * 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 array + */ + private $enabledFilters = array(); + + /** + * @var string The filter hash from the last time the query was parsed. + */ + private $filterHash; + + /** + * @var integer $state The current state of this filter + */ + private $filtersState = self::FILTERS_STATE_CLEAN; + + /** + * Constructor. + * + * @param EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + $this->config = $em->getConfiguration(); + } + + /** + * Get all the enabled filters. + * + * @return array The enabled filters. + */ + public function getEnabledFilters() + { + return $this->enabledFilters; + } + + /** + * Enables a filter from the collection. + * + * @param string $name Name of the filter. + * + * @throws \InvalidArgumentException If the filter does not exist. + * + * @return SQLFilter The enabled filter. + */ + public function enable($name) + { + if (null === $filterClass = $this->config->getFilterClassName($name)) { + throw new \InvalidArgumentException("Filter '" . $name . "' does not exist."); + } + + if (!isset($this->enabledFilters[$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 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; + } + + /** + * Get an enabled filter from the collection. + * + * @param string $name Name of the filter. + * + * @return SQLFilter The filter. + * + * @throws \InvalidArgumentException If the filter is not enabled. + */ + public function getFilter($name) + { + if (!isset($this->enabledFilters[$name])) { + throw new \InvalidArgumentException("Filter '" . $name . "' is not enabled."); + } + + return $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; + } + + /** + * Set 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..beafa7d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php @@ -0,0 +1,206 @@ +. + */ + +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; + + /** + * 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]{1}[a-z0-9_]{0,}' + ); + } + + /** + * @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..57813cb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parameter.php @@ -0,0 +1,101 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Define a Query Parameter + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Guilherme Blanco + */ +class Parameter +{ + /** + * @var string Parameter name + */ + private $name; + + /** + * @var mixed Parameter value + */ + private $value; + + /** + * @var mixed Parameter type + */ + 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); + } + + /** + * Retrieve the Parameter name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Retrieve the Parameter value. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Retrieve the Parameter type. + * + * @return mixed + */ + public function getType() + { + return $this->type; + } + + /** + * Define 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..45a0aab --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +/** + * Provides an enclosed support for parameter infering. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ParameterTypeInferer +{ + /** + * Infer 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 ($value instanceof \DateTime) { + return Type::DATETIME; + } + + if (is_array($value)) { + return is_integer(current($value)) + ? Connection::PARAM_INT_ARRAY + : Connection::PARAM_STR_ARRAY; + } + + return \PDO::PARAM_STR; + } +} \ No newline at end of file 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..1e1919d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php @@ -0,0 +1,3132 @@ +. + */ + +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 + */ +class Parser +{ + /** READ-ONLY: Maps BUILT-IN string function names to AST class names. */ + 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. */ + 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. */ + 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. + */ + private $_deferredIdentificationVariables = array(); + private $_deferredPartialObjectExpressions = array(); + private $_deferredPathExpressions = array(); + private $_deferredResultVariables = array(); + + /** + * The lexer. + * + * @var \Doctrine\ORM\Query\Lexer + */ + private $_lexer; + + /** + * The parser result. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $_parserResult; + + /** + * The EntityManager. + * + * @var EnityManager + */ + 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(); + + /** + * Check 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 + */ + public function setCustomOutputTreeWalker($className) + { + $this->_customOutputWalker = $className; + } + + /** + * Adds a custom tree walker for modifying the AST. + * + * @param string $className + */ + 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 EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * Parse and build 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(); + } + + $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 type + * @return void + * @throws QueryException If the tokens dont 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(); + } + + /** + * Free this parser enabling it to be reused + * + * @param boolean $deep Whether to clean peek and reset errors + * @param integer $position Position to reset + */ + 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); + } + } + + $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; + } + + /** + * Fix 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 $token Got token. + * + * @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 $token Optional token. + * + * @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())); + } + + /** + * Peek beyond the matched closing parenthesis and return 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. + * + * @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() + { + $peek = $this->_lexer->peek(); + $nextpeek = $this->_lexer->peek(); + + $this->_lexer->resetPeek(); + + // We deny the COUNT(SELECT * FROM User u) here. COUNT won't be considered a function + return ($peek['type'] === Lexer::T_OPEN_PARENTHESIS && $nextpeek['type'] !== Lexer::T_SELECT); + } + + /** + * Checks whether the given token type indicates an aggregate function. + * + * @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 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; + } + + $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 array $deferredItem + * @param mixed $AST + */ + 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; + } + } + + 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); + + 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']; + } + + // Creating AST node + $pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field); + + // Defer PathExpression validation if requested to be defered + $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 + * ) ["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->_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 + */ + 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->SimpleArithmeticExpression(); + } + + /** + * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}* + * + * @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration + */ + public function IdentificationVariableDeclaration() + { + $rangeVariableDeclaration = $this->RangeVariableDeclaration(); + $indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; + $joins = array(); + + 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 | (AssociationPathExpression ["AS"] AliasIdentificationVariable) + * + * @return \Doctrine\ORM\Query\AST\SubselectIdentificationVariableDeclaration | + * \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration + */ + public function SubselectIdentificationVariableDeclaration() + { + $this->_lexer->glimpse(); + + /* NOT YET IMPLEMENTED! + + if ($glimpse['type'] == Lexer::T_DOT) { + $subselectIdVarDecl = new AST\SubselectIdentificationVariableDeclaration(); + $subselectIdVarDecl->associationPathExpression = $this->AssociationPathExpression(); + $this->match(Lexer::T_AS); + $subselectIdVarDecl->aliasIdentificationVariable = $this->AliasIdentificationVariable(); + + return $subselectIdVarDecl; + } + */ + + 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(); + + // Create AST node + $join = new AST\Join($joinType, $joinDeclaration); + + // Check for ad-hoc Join conditions + if ($this->_lexer->isNextToken(Lexer::T_WITH) || $joinDeclaration instanceof AST\RangeVariableDeclaration) { + $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); + + $partialFieldSet[] = $this->_lexer->token['value']; + } + + $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; + } + + /** + * 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']; + + switch ($lookahead) { + case 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(); + + case Lexer::T_INTEGER: + case Lexer::T_FLOAT: + return $this->SimpleArithmeticExpression(); + + case Lexer::T_STRING: + return $this->StringPrimary(); + + case Lexer::T_TRUE: + case Lexer::T_FALSE: + $this->match($lookahead); + + return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']); + + case Lexer::T_INPUT_PARAMETER: + return $this->InputParameter(); + + case Lexer::T_CASE: + case Lexer::T_COALESCE: + case Lexer::T_NULLIF: + // Since NULLIF and COALESCE can be identified as a function, + // we need to check if before check for FunctionDeclaration + return $this->CaseExpression(); + + default: + if ( ! ($this->_isFunction() || $this->_isAggregateFunction($lookahead))) { + $this->syntaxError(); + } + + // We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator) + $this->_lexer->peek(); // "(" + $peek = $this->_peekBeyondClosingParenthesis(); + + if ($this->_isMathOperator($peek)) { + return $this->SimpleArithmeticExpression(); + } + + if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + return $this->AggregateExpression(); + } + + return $this->FunctionDeclaration(); + } + } + + /** + * 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\GeneralExpression + */ + 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 + */ + 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\WhenExpression + */ + 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\SimpleWhenExpression + */ + 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 + * ) [["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; + + 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 paranthesis ')' + $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() + { + $token = $this->_lexer->lookahead; + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $token = $this->_lexer->glimpse(); + } + + if ($token['type'] === Lexer::T_EXISTS) { + return $this->ExistsExpression(); + } + + $peek = $this->_lexer->glimpse(); + + if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER) { + if ($peek['value'] == '(') { + // Peek beyond the matching closing paranthesis ')' + $this->_lexer->peek(); + $token = $this->_peekBeyondClosingParenthesis(false); + + if ($token['type'] === Lexer::T_NOT) { + $token = $this->_lexer->peek(); + } + + $this->_lexer->resetPeek(); + } else { + // Peek beyond the PathExpression (or InputParameter) + $peek = $this->_lexer->peek(); + + while ($peek['value'] === '.') { + $this->_lexer->peek(); + $peek = $this->_lexer->peek(); + } + + // Also peek beyond a NOT if there is one + if ($peek['type'] === Lexer::T_NOT) { + $peek = $this->_lexer->peek(); + } + + $token = $peek; + + // We need to go even further in case of IS (differenciate 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(); + } + } + + switch ($token['type']) { + case Lexer::T_BETWEEN: + return $this->BetweenExpression(); + case Lexer::T_LIKE: + return $this->LikeExpression(); + case Lexer::T_IN: + return $this->InExpression(); + case Lexer::T_INSTANCE: + return $this->InstanceOfExpression(); + case Lexer::T_IS: + if ($lookahead['type'] == Lexer::T_NULL) { + return $this->NullComparisonExpression(); + } + return $this->EmptyCollectionComparisonExpression(); + case Lexer::T_MEMBER: + return $this->CollectionMemberExpression(); + default: + return $this->ComparisonExpression(); + } + } + + /** + * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + * + * @return \Doctrine\ORM\Query\AST\EmptyCollectionComparisonExpression + */ + public function EmptyCollectionComparisonExpression() + { + $emptyColletionCompExpr = new AST\EmptyCollectionComparisonExpression( + $this->CollectionValuedPathExpression() + ); + $this->match(Lexer::T_IS); + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $emptyColletionCompExpr->not = true; + } + + $this->match(Lexer::T_EMPTY); + + return $emptyColletionCompExpr; + } + + /** + * 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 string + */ + 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 | "(" SimpleArithmeticExpression ")" + * | 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 $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 | "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\StringPrimary | + * \Doctrine]ORM\Query\AST\Subselect + */ + public function StringExpression() + { + 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 = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $expr; + } + } + + 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 doesnt 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") "(" ["DISTINCT"] StateFieldPathExpression ")" | + * "COUNT" "(" ["DISTINCT"] (IdentificationVariable | SingleValuedPathExpression) ")" + * + * @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 = ($lookaheadType === Lexer::T_COUNT) + ? $this->SingleValuedPathExpression() + : $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 ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL" + * + * @return \Doctrine\ORM\Query\AST\NullComparisonExpression + */ + public function NullComparisonExpression() + { + if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + $this->match(Lexer::T_INPUT_PARAMETER); + $expr = new AST\InputParameter($this->_lexer->token['value']); + } else { + $expr = $this->SingleValuedPathExpression(); + } + + $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 + */ + 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 + */ + 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 ")" + */ + public function FunctionsReturningNumerics() + { + $funcNameLower = strtolower($this->_lexer->lookahead['value']); + $funcClass = self::$_NUMERIC_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + public function CustomFunctionsReturningNumerics() + { + // getCustomNumericFunction is case-insensitive + $funcName = strtolower($this->_lexer->lookahead['value']); + $funcClass = $this->_em->getConfiguration()->getCustomNumericFunction($funcName); + + $function = new $funcClass($funcName); + $function->parse($this); + + return $function; + } + + /** + * FunctionsReturningDateTime ::= "CURRENT_DATE" | "CURRENT_TIME" | "CURRENT_TIMESTAMP" + */ + public function FunctionsReturningDatetime() + { + $funcNameLower = strtolower($this->_lexer->lookahead['value']); + $funcClass = self::$_DATETIME_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + public function CustomFunctionsReturningDatetime() + { + // getCustomDatetimeFunction is case-insensitive + $funcName = $this->_lexer->lookahead['value']; + $funcClass = $this->_em->getConfiguration()->getCustomDatetimeFunction($funcName); + + $function = new $funcClass($funcName); + $function->parse($this); + + return $function; + } + + /** + * FunctionsReturningStrings ::= + * "CONCAT" "(" StringPrimary "," StringPrimary ")" | + * "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + * "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" | + * "LOWER" "(" StringPrimary ")" | + * "UPPER" "(" StringPrimary ")" + */ + public function FunctionsReturningStrings() + { + $funcNameLower = strtolower($this->_lexer->lookahead['value']); + $funcClass = self::$_STRING_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + public function CustomFunctionsReturningStrings() + { + // getCustomStringFunction is case-insensitive + $funcName = $this->_lexer->lookahead['value']; + $funcClass = $this->_em->getConfiguration()->getCustomStringFunction($funcName); + + $function = new $funcClass($funcName); + $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..e068622 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php @@ -0,0 +1,138 @@ +. + */ + +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/lgpl-license.php LGPL + * @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 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 + */ + 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 + */ + 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 + */ + 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..83640c5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * A parse tree printer for Doctrine Query Language parser. + * + * @author Janne Vanhala + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 + */ + 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. + */ + public function endProduction() + { + $this->_indent--; + $this->println(')'); + } + + /** + * Prints text indented with spaces depending on current indentation level. + * + * @param string $str text + */ + 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..6a03c8c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php @@ -0,0 +1,156 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\Query\AST\PathExpression; + +/** + * 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 +{ + public static function dqlError($dql) + { + return new self($dql); + } + + public static function syntaxError($message, $previous = null) + { + return new self('[Syntax Error] ' . $message, 0, $previous); + } + + public static function semanticalError($message, $previous = null) + { + return new self('[Semantical Error] ' . $message, 0, $previous); + } + + public static function invalidLockMode() + { + return new self('Invalid lock mode hint provided.'); + } + + public static function invalidParameterType($expected, $received) + { + return new self('Invalid parameter type, ' . $received . ' given, but ' . $expected . ' expected.'); + } + + public static function invalidParameterPosition($pos) + { + return new self('Invalid parameter position: ' . $pos); + } + + public static function invalidParameterNumber() + { + return new self("Invalid parameter number: number of bound variables does not match number of tokens"); + } + + public static function invalidParameterFormat($value) + { + return new self('Invalid parameter format, '.$value.' given, but : or ? expected.'); + } + + public static function unknownParameter($key) + { + return new self("Invalid parameter: token ".$key." is not defined in the query."); + } + + public static function parameterTypeMissmatch() + { + return new self("DQL Query parameter and type numbers missmatch, but have to be exactly equal."); + } + + public static function invalidPathExpression($pathExpr) + { + return new self( + "Invalid PathExpression '" . $pathExpr->identificationVariable . "." . $pathExpr->field . "'." + ); + } + + public static function invalidLiteral($literal) { + return new self("Invalid literal '$literal'"); + } + + /** + * @param array $assoc + */ + public static function iterateWithFetchJoinCollectionNotAllowed($assoc) + { + return new self( + "Invalid query operation: Not allowed to iterate over fetch join collections ". + "in class ".$assoc['sourceEntity']." assocation ".$assoc['fieldName'] + ); + } + + 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." + ); + } + + 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']." assocation ".$assoc['fieldName'].". ". + "Use WITH to append additional join conditions to the association." + ); + } + + 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." + ); + } + + public static function iterateWithFetchJoinNotAllowed($assoc) { + return new self( + "Iterate with fetch join in class " . $assoc['sourceEntity'] . + " using association " . $assoc['fieldName'] . " not allowed." + ); + } + + 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." + ); + } + + public static function instanceOfUnrelatedClass($className, $rootClass) + { + return new self("Cannot check if a child of '" . $rootClass . "' is instanceof '" . $className . "', " . + "inheritance hierachy exists between these two classes."); + } +} 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..b8cbd32 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -0,0 +1,485 @@ +. + */ + +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 +{ + /** + * @ignore + * @var boolean Whether the result is mixed (contains scalar values together with field values). + */ + public $isMixed = false; + + /** + * @ignore + * @var array Maps alias names to class names. + */ + public $aliasMap = array(); + + /** + * @ignore + * @var array Maps alias names to related association field names. + */ + public $relationMap = array(); + + /** + * @ignore + * @var array Maps alias names to parent alias names. + */ + public $parentAliasMap = array(); + + /** + * @ignore + * @var array Maps column names in the result set to field names for each class. + */ + public $fieldMappings = array(); + + /** + * @ignore + * @var array Maps column names in the result set to the alias/field name to use in the mapped result. + */ + public $scalarMappings = array(); + + /** + * @ignore + * @var array Maps column names in the result set to the alias/field type to use in the mapped result. + */ + public $typeMappings = array(); + + /** + * @ignore + * @var array Maps entities in the result set to the alias name to use in the mapped result. + */ + public $entityMappings = array(); + + /** + * @ignore + * @var array Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names. + */ + public $metaMappings = array(); + + /** + * @ignore + * @var array Maps column names in the result set to the alias they belong to. + */ + public $columnOwnerMap = array(); + + /** + * @ignore + * @var array List of columns in the result set that are used as discriminator columns. + */ + public $discriminatorColumns = array(); + + /** + * @ignore + * @var array Maps alias names to field names that should be used for indexing. + */ + public $indexByMap = array(); + + /** + * @ignore + * @var array Map from column names to class names that declare the field the column is mapped to. + */ + public $declaringClasses = array(); + + /** + * @var array This is necessary to hydrate derivate foreign keys correctly. + */ + public $isIdentifierColumn = 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 $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 ($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; + } + + /** + * Set to index by a scalar result column name + * + * @param $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 $alias + * @param $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 $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 object $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; + } + + /** + * Checks whether a column with a given name is mapped as a scalar result. + * + * @param string $columName 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 + * @param string $columnName + * @param string $fieldName + * @param bool + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false) + { + $this->metaMappings[$columnName] = $fieldName; + $this->columnOwnerMap[$columnName] = $alias; + + if ($isIdentifierColumn) { + $this->isIdentifierColumn[$alias][$columnName] = true; + } + + 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..8e31bcf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php @@ -0,0 +1,264 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\EntityManager; +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 +{ + /** + * @var EntityManager + */ + private $em; + + /** + * @param EntityManager + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * 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) + */ + public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array()) + { + $this->addEntityResult($class, $alias); + $this->addAllClassFields($class, $alias, $renamedColumns); + } + + /** + * 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) + */ + public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = array()) + { + $this->addJoinedEntityResult($class, $alias, $parentAlias, $relation); + $this->addAllClassFields($class, $alias, $renamedColumns); + } + + /** + * Adds all fields of the given class to the result set mapping (columns and meta fields) + */ + protected function addAllClassFields($class, $alias, $renamedColumns = array()) + { + $classMetadata = $this->em->getClassMetadata($class); + if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) { + throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.'); + } + $platform = $this->em->getConnection()->getDatabasePlatform(); + foreach ($classMetadata->getColumnNames() as $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + if (isset($renamedColumns[$columnName])) { + $columnName = $renamedColumns[$columnName]; + } + $columnName = $platform->getSQLResultCasing($columnName); + if (isset($this->fieldMappings[$columnName])) { + throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper."); + } + $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']; + $renamedColumnName = isset($renamedColumns[$columnName]) ? $renamedColumns[$columnName] : $columnName; + $renamedColumnName = $platform->getSQLResultCasing($renamedColumnName); + if (isset($this->metaMappings[$renamedColumnName])) { + throw new \InvalidArgumentException("The column '$renamedColumnName' conflicts with another column in the mapper."); + } + $this->addMetaResult($alias, $renamedColumnName, $columnName); + } + } + } + } + + + /** + * 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 + */ + 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; + } +} 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..d3608e3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php @@ -0,0 +1,2202 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\DBAL\LockMode, + Doctrine\DBAL\Types\Type, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Query, + Doctrine\ORM\Query\QueryException, + 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 + * @since 2.0 + * @todo Rename: SQLWalker + */ +class SqlWalker implements TreeWalker +{ + /** + * @var string + */ + const HINT_DISTINCT = 'doctrine.distinct'; + + /** + * @var ResultSetMapping + */ + private $rsm; + + /** + * Counters for generating unique column aliases. + * + * @var integer + */ + private $aliasCounter = 0; + + /** + * Counters for generating unique table aliases. + * + * @var integer + */ + private $tableAliasCounter = 0; + + /** + * Counters for generating unique scalar result. + * + * @var integer + */ + private $scalarResultCounter = 1; + + /** + * Counters for generating unique parameter indexes. + * + * @var integer + */ + private $sqlParamIndex = 0; + + /** + * @var ParserResult + */ + private $parserResult; + + /** + * @var EntityManager + */ + private $em; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * @var 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 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 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 Connection + */ + public function getConnection() + { + return $this->conn; + } + + /** + * Gets the EntityManager used by the walker. + * + * @return 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]; + } + + /** + * Gets an executor that can be used to execute the result of this walker. + * + * @return AbstractExecutor + */ + 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] = strtolower(substr($tableName, 0, 1)) . $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; + } + + private function _generateOrderedCollectionOrderByItems() + { + $sqlParts = array(); + + foreach ($this->selectedClasses as $selectedClass) { + $dqlAlias = $selectedClass['dqlAlias']; + $qComp = $this->queryComponents[$dqlAlias]; + + if ( ! isset($qComp['relation']['orderBy'])) continue; + + foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) { + $columnName = $this->quoteStrategy->getColumnName($fieldName, $qComp['metadata'], $this->platform); + $tableName = ($qComp['metadata']->isInheritanceTypeJoined()) + ? $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name)->getOwningTable($fieldName) + : $qComp['metadata']->getTableName(); + + $sqlParts[] = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName . ' ' . $orientation; + } + } + + return implode(', ', $sqlParts); + } + + /** + * 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) { // discrimnators 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); + } + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + $sql = $this->walkSelectClause($AST->selectClause); + $sql .= $this->walkFromClause($AST->fromClause); + $sql .= $this->walkWhereClause($AST->whereClause); + $sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : ''; + $sql .= $AST->havingClause ? $this->walkHavingClause($AST->havingClause) : ''; + + if (($orderByClause = $AST->orderByClause) !== null) { + $sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : ''; + } else if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') { + $sql .= ' ORDER BY ' . $orderBySql; + } + + $sql = $this->platform->modifyLimitQuery( + $sql, $this->query->getMaxResults(), $this->query->getFirstResult() + ); + + if (($lockMode = $this->query->getHint(Query::HINT_LOCK_MODE)) !== false) { + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + $sql .= ' ' . $this->platform->getReadLockSQL(); + break; + + case LockMode::PESSIMISTIC_WRITE: + $sql .= ' ' . $this->platform->getWriteLockSQL(); + break; + + case LockMode::OPTIMISTIC: + foreach ($this->selectedClasses as $selectedClass) { + if ( ! $selectedClass['class']->isVersioned) { + throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name); + } + } + break; + case LockMode::NONE: + break; + + default: + throw \Doctrine\ORM\Query\QueryException::invalidLockMode(); + } + } + + return $sql; + } + + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param UpdateStatement + * @return string The SQL. + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + $this->useSqlTableAliases = false; + + return $this->walkUpdateClause($AST->updateClause) + . $this->walkWhereClause($AST->whereClause); + } + + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param DeleteStatement + * @return string The SQL. + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + $this->useSqlTableAliases = 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); + } + + /** + * Walks down a PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + 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; + } + + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @param $selectClause + * @return string The SQL. + */ + 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); + + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + $columnAlias = $this->getSQLColumnAlias($srcColumn); + + $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; + + $this->rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn, (isset($assoc['id']) && $assoc['id'] === true)); + } + } + + // 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; + } + + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFromClause($fromClause) + { + $identificationVarDecls = $fromClause->identificationVariableDeclarations; + $sqlParts = array(); + + foreach ($identificationVarDecls as $identificationVariableDecl) { + $sql = $this->platform->appendLockHint( + $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration), + $this->query->getHint(Query::HINT_LOCK_MODE) + ); + + foreach ($identificationVariableDecl->joins as $join) { + $sql .= $this->walkJoin($join); + } + + if ($identificationVariableDecl->indexBy) { + $alias = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable; + $field = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field; + + if (isset($this->scalarFields[$alias][$field])) { + $this->rsm->addIndexByScalar($this->scalarFields[$alias][$field]); + } else { + $this->rsm->addIndexBy( + $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable, + $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field + ); + } + } + + $sqlParts[] = $sql; + } + + return ' FROM ' . implode(', ', $sqlParts); + } + + /** + * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL. + * + * @return string + */ + public function walkRangeVariableDeclaration($rangeVariableDeclaration) + { + $class = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName); + $dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable; + + $this->rootAliases[] = $dqlAlias; + + $sql = $class->getQuotedTableName($this->platform) . ' ' + . $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + if ($class->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); + } + + return $sql; + } + + /** + * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL. + * + * @return string + */ + public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER) + { + $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 = $targetClass->getQuotedTableName($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); + } + } + + // 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; + } + + $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions); + break; + + case ($assoc['type'] == ClassMetadata::MANY_TO_MANY): + // Join relation table + $joinTable = $assoc['joinTable']; + $joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias); + $joinTableName = $sourceClass->getQuotedJoinTableName($assoc, $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; + } + + $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions); + break; + } + + // FIXME: these should either be nested or all forced to be left joins (DDC-XXX) + if ($targetClass->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias); + } + + // Apply the indexes + if ($indexBy) { + // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently. + $this->rsm->addIndexBy( + $indexBy->simpleStateFieldPathExpression->identificationVariable, + $indexBy->simpleStateFieldPathExpression->field + ); + } else if (isset($relation['indexBy'])) { + $this->rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']); + } + + return $sql; + } + + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFunction($function) + { + return $function->getSql($this); + } + + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param OrderByClause + * @return string The SQL. + */ + 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); + } + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param OrderByItem + * @return string The SQL. + */ + public function walkOrderByItem($orderByItem) + { + $expr = $orderByItem->expression; + $sql = ($expr instanceof AST\Node) + ? $expr->dispatch($this) + : $this->walkResultVariable($this->queryComponents[$expr]['token']['value']); + + return $sql . ' ' . strtoupper($orderByItem->type); + } + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param HavingClause + * @return string The SQL. + */ + public function walkHavingClause($havingClause) + { + return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression); + } + + /** + * Walks down a Join AST node and creates the corresponding SQL. + * + * @return string The SQL. + */ + 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); + $condExprConjunction = $class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER + ? ' AND ' + : ' ON '; + + $sql .= $this->walkRangeVariableDeclaration($joinDeclaration) + . $condExprConjunction . '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')'; + break; + + case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration): + $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType); + + // Handle WITH clause + if (($condExpr = $join->conditionalExpression) !== null) { + // Phase 2 AST optimization: Skip processment of ConditionalExpression + // if only one ConditionalTerm is defined + $sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')'; + } + break; + } + + return $sql; + } + + /** + * Walks down a CaseExpression AST node and generates the corresponding SQL. + * + * @param CoalesceExpression|NullIfExpression|GeneralCaseExpression|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 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 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 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 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; + } + + /** + * Walks down a SelectExpression AST node and generates the corresponding SQL. + * + * @param SelectExpression $selectExpression + * @return string The SQL. + */ + 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\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; + + 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; + } + + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param QuantifiedExpression + * @return string The SQL. + */ + public function walkQuantifiedExpression($qExpr) + { + return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')'; + } + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param Subselect + * @return string The SQL. + */ + 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; + } + + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param SubselectFromClause + * @return string The SQL. + */ + public function walkSubselectFromClause($subselectFromClause) + { + $identificationVarDecls = $subselectFromClause->identificationVariableDeclarations; + $sqlParts = array (); + + foreach ($identificationVarDecls as $subselectIdVarDecl) { + $sql = $this->platform->appendLockHint( + $this->walkRangeVariableDeclaration($subselectIdVarDecl->rangeVariableDeclaration), + $this->query->getHint(Query::HINT_LOCK_MODE) + ); + + foreach ($subselectIdVarDecl->joins as $join) { + $sql .= $this->walkJoin($join); + } + + $sqlParts[] = $sql; + } + + return ' FROM ' . implode(', ', $sqlParts); + } + + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectClause + * @return string The SQL. + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + return 'SELECT' . ($simpleSelectClause->isDistinct ? ' DISTINCT' : '') + . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression); + } + + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectExpression + * @return string The SQL. + */ + 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; + + default: // IdentificationVariable + $sql .= $this->walkEntityIdentificationVariable($expr); + break; + } + + return $sql; + } + + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AggregateExpression + * @return string The SQL. + */ + public function walkAggregateExpression($aggExpression) + { + return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '') + . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')'; + } + + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param GroupByClause + * @return string The SQL. + */ + public function walkGroupByClause($groupByClause) + { + $sqlParts = array(); + + foreach ($groupByClause->groupByItems as $groupByItem) { + $sqlParts[] = $this->walkGroupByItem($groupByItem); + } + + return ' GROUP BY ' . implode(', ', $sqlParts); + } + + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param GroupByItem + * @return string The SQL. + */ + public function walkGroupByItem($groupByItem) + { + // StateFieldPathExpression + if ( ! is_string($groupByItem)) { + return $this->walkPathExpression($groupByItem); + } + + // ResultVariable + if (isset($this->queryComponents[$groupByItem]['resultVariable'])) { + 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); + } + + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param DeleteClause + * @return string The SQL. + */ + 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; + } + + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param UpdateClause + * @return string The SQL. + */ + 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; + } + + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param UpdateItem + * @return string The SQL. + */ + 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; + } + + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * WhereClause or not, the appropriate discriminator sql is added. + * + * @param WhereClause + * @return string The SQL. + */ + 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 ''; + } + + /** + * Walk down a ConditionalExpression AST node, thereby generating the appropriate SQL. + * + * @param ConditionalExpression + * @return string The SQL. + */ + public function walkConditionalExpression($condExpr) + { + // Phase 2 AST optimization: Skip processment 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)); + } + + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param ConditionalTerm + * @return string The SQL. + */ + public function walkConditionalTerm($condTerm) + { + // Phase 2 AST optimization: Skip processment 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)); + } + + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param ConditionalFactor + * @return string The SQL. + */ + public function walkConditionalFactor($factor) + { + // Phase 2 AST optimization: Skip processment of ConditionalFactor + // if only one ConditionalPrimary is defined + return ( ! ($factor instanceof AST\ConditionalFactor)) + ? $this->walkConditionalPrimary($factor) + : ($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary); + } + + /** + * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. + * + * @param ConditionalPrimary + * @return string The SQL. + */ + public function walkConditionalPrimary($primary) + { + if ($primary->isSimpleConditionalExpression()) { + return $primary->simpleConditionalExpression->dispatch($this); + } + + if ($primary->isConditionalExpression()) { + $condExpr = $primary->conditionalExpression; + + return '(' . $this->walkConditionalExpression($condExpr) . ')'; + } + } + + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param ExistsExpression + * @return string The SQL. + */ + public function walkExistsExpression($existsExpr) + { + $sql = ($existsExpr->not) ? 'NOT ' : ''; + + $sql .= 'EXISTS (' . $this->walkSubselect($existsExpr->subselect) . ')'; + + return $sql; + } + + /** + * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. + * + * @param CollectionMemberExpression + * @return string The SQL. + */ + 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 . ' = ' . $entitySql; + } + + $sql .= implode(' AND ', $sqlParts); + } + + return $sql . ')'; + } + + /** + * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param EmptyCollectionComparisonExpression + * @return string The SQL. + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + $sizeFunc = new AST\Functions\SizeFunction('size'); + $sizeFunc->collectionPathExpression = $emptyCollCompExpr->expression; + + return $sizeFunc->getSql($this) . ($emptyCollCompExpr->not ? ' > 0' : ' = 0'); + } + + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param NullComparisonExpression + * @return string The SQL. + */ + public function walkNullComparisonExpression($nullCompExpr) + { + $sql = ''; + $innerExpr = $nullCompExpr->expression; + + if ($innerExpr instanceof AST\InputParameter) { + $dqlParamKey = $innerExpr->name; + $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++); + $sql .= ' ?'; + } else { + $sql .= $this->walkPathExpression($innerExpr); + } + + $sql .= ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL'; + + return $sql; + } + + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param InExpression + * @return string The SQL. + */ + 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; + } + + /** + * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. + * + * @param InstanceOfExpression + * @return string The SQL. + */ + 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) { + $sqlParameterList[] = $this->walkInputParameter($parameter); + } else { + // Get name from ClassMetadata to resolve aliases. + $entityClassName = $this->em->getClassMetadata($parameter)->name; + + if ($entityClassName == $class->name) { + $sqlParameterList[] = $this->conn->quote($class->discriminatorValue); + } else { + $discrMap = array_flip($class->discriminatorMap); + + if (!isset($discrMap[$entityClassName])) { + throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName); + } + + $sqlParameterList[] = $this->conn->quote($discrMap[$entityClassName]); + } + } + } + + $sql .= '(' . implode(', ', $sqlParameterList) . ')'; + + return $sql; + } + + /** + * Walks down an InParameter AST node, thereby generating the appropriate SQL. + * + * @param InParameter + * @return string The SQL. + */ + public function walkInParameter($inParam) + { + return $inParam instanceof AST\InputParameter + ? $this->walkInputParameter($inParam) + : $this->walkLiteral($inParam); + } + + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + 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); + } + } + + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param BetweenExpression + * @return string The SQL. + */ + 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; + } + + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param LikeExpression + * @return string The SQL. + */ + public function walkLikeExpression($likeExpr) + { + $stringExpr = $likeExpr->stringExpression; + $sql = $stringExpr->dispatch($this) . ($likeExpr->not ? ' NOT' : '') . ' LIKE '; + + if ($likeExpr->stringPattern instanceof AST\InputParameter) { + $inputParam = $likeExpr->stringPattern; + $dqlParamKey = $inputParam->name; + $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++); + $sql .= '?'; + } 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; + } + + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param StateFieldPathExpression + * @return string The SQL. + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + return $this->walkPathExpression($stateFieldPathExpression); + } + + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param ComparisonExpression + * @return string The SQL. + */ + 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; + } + + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param InputParameter + * @return string The SQL. + */ + public function walkInputParameter($inputParam) + { + $this->parserResult->addParameterMapping($inputParam->name, $this->sqlParamIndex++); + + return '?'; + } + + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param ArithmeticExpression + * @return string The SQL. + */ + public function walkArithmeticExpression($arithmeticExpr) + { + return ($arithmeticExpr->isSimpleArithmeticExpression()) + ? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression) + : '(' . $this->walkSubselect($arithmeticExpr->subselect) . ')'; + } + + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleArithmeticExpression + * @return string The SQL. + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) { + return $this->walkArithmeticTerm($simpleArithmeticExpr); + } + + return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms)); + } + + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + 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 processment 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)); + } + + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticFactor($factor) + { + if (is_string($factor)) { + return $factor; + } + + // Phase 2 AST optimization: Skip processment 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 + * @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); + } + + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkStringPrimary($stringPrimary) + { + return (is_string($stringPrimary)) + ? $this->conn->quote($stringPrimary) + : $stringPrimary->dispatch($this); + } + + /** + * Walks down a ResultVriable that represents an AST node, thereby generating the appropriate SQL. + * + * @param string $resultVariable + * @return string The SQL. + */ + 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..c88ca13 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php @@ -0,0 +1,409 @@ +. + */ + +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 Query $query The parsed Query. + * @param ParserResult $parserResult The result of the parsing process. + * @param array $queryComponents Query components (symbol table) + */ + public function __construct($query, $parserResult, array $queryComponents); + + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + function walkSelectStatement(AST\SelectStatement $AST); + + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + function walkSelectClause($selectClause); + + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + function walkFromClause($fromClause); + + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + function walkFunction($function); + + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param OrderByClause + * @return string The SQL. + */ + function walkOrderByClause($orderByClause); + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param OrderByItem + * @return string The SQL. + */ + function walkOrderByItem($orderByItem); + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param HavingClause + * @return string The SQL. + */ + function walkHavingClause($havingClause); + + /** + * Walks down a Join AST node and creates the corresponding SQL. + * + * @param Join $joinVarDecl + * @return string The SQL. + */ + function walkJoin($join); + + /** + * Walks down a SelectExpression AST node and generates the corresponding SQL. + * + * @param SelectExpression $selectExpression + * @return string The SQL. + */ + function walkSelectExpression($selectExpression); + + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param QuantifiedExpression + * @return string The SQL. + */ + function walkQuantifiedExpression($qExpr); + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param Subselect + * @return string The SQL. + */ + function walkSubselect($subselect); + + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param SubselectFromClause + * @return string The SQL. + */ + function walkSubselectFromClause($subselectFromClause); + + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectClause + * @return string The SQL. + */ + function walkSimpleSelectClause($simpleSelectClause); + + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectExpression + * @return string The SQL. + */ + function walkSimpleSelectExpression($simpleSelectExpression); + + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AggregateExpression + * @return string The SQL. + */ + function walkAggregateExpression($aggExpression); + + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param GroupByClause + * @return string The SQL. + */ + function walkGroupByClause($groupByClause); + + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param GroupByItem + * @return string The SQL. + */ + function walkGroupByItem($groupByItem); + + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param UpdateStatement + * @return string The SQL. + */ + function walkUpdateStatement(AST\UpdateStatement $AST); + + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param DeleteStatement + * @return string The SQL. + */ + function walkDeleteStatement(AST\DeleteStatement $AST); + + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param DeleteClause + * @return string The SQL. + */ + function walkDeleteClause(AST\DeleteClause $deleteClause); + + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param UpdateClause + * @return string The SQL. + */ + function walkUpdateClause($updateClause); + + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param UpdateItem + * @return string The SQL. + */ + function walkUpdateItem($updateItem); + + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * + * @param WhereClause + * @return string The SQL. + */ + function walkWhereClause($whereClause); + + /** + * Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL. + * + * @param ConditionalExpression + * @return string The SQL. + */ + function walkConditionalExpression($condExpr); + + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param ConditionalTerm + * @return string The SQL. + */ + function walkConditionalTerm($condTerm); + + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param ConditionalFactor + * @return string The SQL. + */ + function walkConditionalFactor($factor); + + /** + * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. + * + * @param ConditionalPrimary + * @return string The SQL. + */ + function walkConditionalPrimary($primary); + + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param ExistsExpression + * @return string The SQL. + */ + function walkExistsExpression($existsExpr); + + /** + * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. + * + * @param CollectionMemberExpression + * @return string The SQL. + */ + function walkCollectionMemberExpression($collMemberExpr); + + /** + * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param EmptyCollectionComparisonExpression + * @return string The SQL. + */ + function walkEmptyCollectionComparisonExpression($emptyCollCompExpr); + + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param NullComparisonExpression + * @return string The SQL. + */ + function walkNullComparisonExpression($nullCompExpr); + + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param InExpression + * @return string The SQL. + */ + function walkInExpression($inExpr); + + /** + * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. + * + * @param InstanceOfExpression + * @return string The SQL. + */ + function walkInstanceOfExpression($instanceOfExpr); + + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + function walkLiteral($literal); + + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param BetweenExpression + * @return string The SQL. + */ + function walkBetweenExpression($betweenExpr); + + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param LikeExpression + * @return string The SQL. + */ + function walkLikeExpression($likeExpr); + + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param StateFieldPathExpression + * @return string The SQL. + */ + function walkStateFieldPathExpression($stateFieldPathExpression); + + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param ComparisonExpression + * @return string The SQL. + */ + function walkComparisonExpression($compExpr); + + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param InputParameter + * @return string The SQL. + */ + function walkInputParameter($inputParam); + + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param ArithmeticExpression + * @return string The SQL. + */ + function walkArithmeticExpression($arithmeticExpr); + + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + function walkArithmeticTerm($term); + + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + function walkStringPrimary($stringPrimary); + + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + function walkArithmeticFactor($factor); + + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleArithmeticExpression + * @return string The SQL. + */ + function walkSimpleArithmeticExpression($simpleArithmeticExpr); + + /** + * Walks down an PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + function walkPathExpression($pathExpr); + + /** + * Walks down an ResultVariable 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. + * + * @return AbstractExecutor + */ + 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..4446a85 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php @@ -0,0 +1,443 @@ +. + */ + +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 +{ + private $_query; + private $_parserResult; + private $_queryComponents; + + /** + * {@inheritdoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->_query = $query; + $this->_parserResult = $parserResult; + $this->_queryComponents = $queryComponents; + } + + /** + * @return array + */ + protected function _getQueryComponents() + { + return $this->_queryComponents; + } + + /** + * Retrieve Query Instance reponsible for the current walkers execution. + * + * @return \Doctrine\ORM\Query + */ + protected function _getQuery() + { + return $this->_query; + } + + /** + * Retrieve ParserResult + * + * @return \Doctrine\ORM\Query\ParserResult + */ + protected function _getParserResult() + { + return $this->_parserResult; + } + + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkSelectStatement(AST\SelectStatement $AST) {} + + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkSelectClause($selectClause) {} + + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFromClause($fromClause) {} + + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFunction($function) {} + + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param OrderByClause + * @return string The SQL. + */ + public function walkOrderByClause($orderByClause) {} + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param OrderByItem + * @return string The SQL. + */ + public function walkOrderByItem($orderByItem) {} + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param HavingClause + * @return string The SQL. + */ + public function walkHavingClause($havingClause) {} + + /** + * Walks down a Join AST node and creates the corresponding SQL. + * + * @param Join $join + * @return string The SQL. + */ + public function walkJoin($join) {} + + /** + * Walks down a SelectExpression AST node and generates the corresponding SQL. + * + * @param SelectExpression $selectExpression + * @return string The SQL. + */ + public function walkSelectExpression($selectExpression) {} + + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param QuantifiedExpression + * @return string The SQL. + */ + public function walkQuantifiedExpression($qExpr) {} + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param Subselect + * @return string The SQL. + */ + public function walkSubselect($subselect) {} + + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param SubselectFromClause + * @return string The SQL. + */ + public function walkSubselectFromClause($subselectFromClause) {} + + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectClause + * @return string The SQL. + */ + public function walkSimpleSelectClause($simpleSelectClause) {} + + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectExpression + * @return string The SQL. + */ + public function walkSimpleSelectExpression($simpleSelectExpression) {} + + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AggregateExpression + * @return string The SQL. + */ + public function walkAggregateExpression($aggExpression) {} + + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param GroupByClause + * @return string The SQL. + */ + public function walkGroupByClause($groupByClause) {} + + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param GroupByItem + * @return string The SQL. + */ + public function walkGroupByItem($groupByItem) {} + + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param UpdateStatement + * @return string The SQL. + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) {} + + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param DeleteStatement + * @return string The SQL. + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) {} + + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param DeleteClause + * @return string The SQL. + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) {} + + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param UpdateClause + * @return string The SQL. + */ + public function walkUpdateClause($updateClause) {} + + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param UpdateItem + * @return string The SQL. + */ + public function walkUpdateItem($updateItem) {} + + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * + * @param WhereClause + * @return string The SQL. + */ + public function walkWhereClause($whereClause) {} + + /** + * Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL. + * + * @param ConditionalExpression + * @return string The SQL. + */ + public function walkConditionalExpression($condExpr) {} + + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param ConditionalTerm + * @return string The SQL. + */ + public function walkConditionalTerm($condTerm) {} + + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param ConditionalFactor + * @return string The SQL. + */ + public function walkConditionalFactor($factor) {} + + /** + * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. + * + * @param ConditionalPrimary + * @return string The SQL. + */ + public function walkConditionalPrimary($primary) {} + + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param ExistsExpression + * @return string The SQL. + */ + public function walkExistsExpression($existsExpr) {} + + /** + * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. + * + * @param CollectionMemberExpression + * @return string The SQL. + */ + public function walkCollectionMemberExpression($collMemberExpr) {} + + /** + * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param EmptyCollectionComparisonExpression + * @return string The SQL. + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) {} + + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param NullComparisonExpression + * @return string The SQL. + */ + public function walkNullComparisonExpression($nullCompExpr) {} + + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param InExpression + * @return string The SQL. + */ + public function walkInExpression($inExpr) {} + + /** + * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. + * + * @param InstanceOfExpression + * @return string The SQL. + */ + function walkInstanceOfExpression($instanceOfExpr) {} + + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkLiteral($literal) {} + + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param BetweenExpression + * @return string The SQL. + */ + public function walkBetweenExpression($betweenExpr) {} + + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param LikeExpression + * @return string The SQL. + */ + public function walkLikeExpression($likeExpr) {} + + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param StateFieldPathExpression + * @return string The SQL. + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) {} + + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param ComparisonExpression + * @return string The SQL. + */ + public function walkComparisonExpression($compExpr) {} + + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param InputParameter + * @return string The SQL. + */ + public function walkInputParameter($inputParam) {} + + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param ArithmeticExpression + * @return string The SQL. + */ + public function walkArithmeticExpression($arithmeticExpr) {} + + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticTerm($term) {} + + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkStringPrimary($stringPrimary) {} + + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticFactor($factor) {} + + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleArithmeticExpression + * @return string The SQL. + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) {} + + /** + * Walks down an PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkPathExpression($pathExpr) {} + + /** + * Walks down an ResultVariable AST node, thereby generating the appropriate SQL. + * + * @param string $resultVariable + * @return string The SQL. + */ + public function walkResultVariable($resultVariable) {} + + /** + * Gets an executor that can be used to execute the result of this walker. + * + * @return AbstractExecutor + */ + 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..13bbcde --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php @@ -0,0 +1,662 @@ +. + */ + +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. */ + private $_walkers = array(); + /** The original Query. */ + private $_query; + /** The ParserResult of the original query that was produced by the Parser. */ + private $_parserResult; + /** The query components of the original query (the "symbol table") that was produced by the Parser. */ + private $_queryComponents; + + /** + * @inheritdoc + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->_query = $query; + $this->_parserResult = $parserResult; + $this->_queryComponents = $queryComponents; + } + + /** + * Adds a tree walker to the chain. + * + * @param string $walkerClass The class of the walker to instantiate. + */ + public function addTreeWalker($walkerClass) + { + $this->_walkers[] = new $walkerClass($this->_query, $this->_parserResult, $this->_queryComponents); + } + + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectStatement($AST); + } + } + + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkSelectClause($selectClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectClause($selectClause); + } + } + + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFromClause($fromClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkFromClause($fromClause); + } + } + + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFunction($function) + { + foreach ($this->_walkers as $walker) { + $walker->walkFunction($function); + } + } + + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param OrderByClause + * @return string The SQL. + */ + public function walkOrderByClause($orderByClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkOrderByClause($orderByClause); + } + } + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param OrderByItem + * @return string The SQL. + */ + public function walkOrderByItem($orderByItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkOrderByItem($orderByItem); + } + } + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param HavingClause + * @return string The SQL. + */ + public function walkHavingClause($havingClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkHavingClause($havingClause); + } + } + + /** + * Walks down a Join AST node and creates the corresponding SQL. + * + * @param Join $join + * @return string The SQL. + */ + public function walkJoin($join) + { + foreach ($this->_walkers as $walker) { + $walker->walkJoin($join); + } + } + + /** + * Walks down a SelectExpression AST node and generates the corresponding SQL. + * + * @param SelectExpression $selectExpression + * @return string The SQL. + */ + public function walkSelectExpression($selectExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectExpression($selectExpression); + } + } + + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param QuantifiedExpression + * @return string The SQL. + */ + public function walkQuantifiedExpression($qExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkQuantifiedExpression($qExpr); + } + } + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param Subselect + * @return string The SQL. + */ + public function walkSubselect($subselect) + { + foreach ($this->_walkers as $walker) { + $walker->walkSubselect($subselect); + } + } + + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param SubselectFromClause + * @return string The SQL. + */ + public function walkSubselectFromClause($subselectFromClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSubselectFromClause($subselectFromClause); + } + } + + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectClause + * @return string The SQL. + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleSelectClause($simpleSelectClause); + } + } + + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectExpression + * @return string The SQL. + */ + public function walkSimpleSelectExpression($simpleSelectExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleSelectExpression($simpleSelectExpression); + } + } + + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AggregateExpression + * @return string The SQL. + */ + public function walkAggregateExpression($aggExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkAggregateExpression($aggExpression); + } + } + + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param GroupByClause + * @return string The SQL. + */ + public function walkGroupByClause($groupByClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkGroupByClause($groupByClause); + } + } + + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param GroupByItem + * @return string The SQL. + */ + public function walkGroupByItem($groupByItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkGroupByItem($groupByItem); + } + } + + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param UpdateStatement + * @return string The SQL. + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateStatement($AST); + } + } + + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param DeleteStatement + * @return string The SQL. + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkDeleteStatement($AST); + } + } + + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param DeleteClause + * @return string The SQL. + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkDeleteClause($deleteClause); + } + } + + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param UpdateClause + * @return string The SQL. + */ + public function walkUpdateClause($updateClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateClause($updateClause); + } + } + + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param UpdateItem + * @return string The SQL. + */ + public function walkUpdateItem($updateItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateItem($updateItem); + } + } + + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * + * @param WhereClause + * @return string The SQL. + */ + public function walkWhereClause($whereClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkWhereClause($whereClause); + } + } + + /** + * Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL. + * + * @param ConditionalExpression + * @return string The SQL. + */ + public function walkConditionalExpression($condExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalExpression($condExpr); + } + } + + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param ConditionalTerm + * @return string The SQL. + */ + public function walkConditionalTerm($condTerm) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalTerm($condTerm); + } + } + + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param ConditionalFactor + * @return string The SQL. + */ + public function walkConditionalFactor($factor) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalFactor($factor); + } + } + + /** + * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. + * + * @param ConditionalPrimary + * @return string The SQL. + */ + public function walkConditionalPrimary($condPrimary) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalPrimary($condPrimary); + } + } + + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param ExistsExpression + * @return string The SQL. + */ + public function walkExistsExpression($existsExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkExistsExpression($existsExpr); + } + } + + /** + * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. + * + * @param CollectionMemberExpression + * @return string The SQL. + */ + public function walkCollectionMemberExpression($collMemberExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkCollectionMemberExpression($collMemberExpr); + } + } + + /** + * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param EmptyCollectionComparisonExpression + * @return string The SQL. + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkEmptyCollectionComparisonExpression($emptyCollCompExpr); + } + } + + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param NullComparisonExpression + * @return string The SQL. + */ + public function walkNullComparisonExpression($nullCompExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkNullComparisonExpression($nullCompExpr); + } + } + + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param InExpression + * @return string The SQL. + */ + public function walkInExpression($inExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkInExpression($inExpr); + } + } + + /** + * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. + * + * @param InstanceOfExpression + * @return string The SQL. + */ + function walkInstanceOfExpression($instanceOfExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkInstanceOfExpression($instanceOfExpr); + } + } + + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkLiteral($literal) + { + foreach ($this->_walkers as $walker) { + $walker->walkLiteral($literal); + } + } + + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param BetweenExpression + * @return string The SQL. + */ + public function walkBetweenExpression($betweenExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkBetweenExpression($betweenExpr); + } + } + + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param LikeExpression + * @return string The SQL. + */ + public function walkLikeExpression($likeExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkLikeExpression($likeExpr); + } + } + + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param StateFieldPathExpression + * @return string The SQL. + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkStateFieldPathExpression($stateFieldPathExpression); + } + } + + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param ComparisonExpression + * @return string The SQL. + */ + public function walkComparisonExpression($compExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkComparisonExpression($compExpr); + } + } + + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param InputParameter + * @return string The SQL. + */ + public function walkInputParameter($inputParam) + { + foreach ($this->_walkers as $walker) { + $walker->walkInputParameter($inputParam); + } + } + + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param ArithmeticExpression + * @return string The SQL. + */ + public function walkArithmeticExpression($arithmeticExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticExpression($arithmeticExpr); + } + } + + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticTerm($term) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticTerm($term); + } + } + + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkStringPrimary($stringPrimary) + { + foreach ($this->_walkers as $walker) { + $walker->walkStringPrimary($stringPrimary); + } + } + + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticFactor($factor) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticFactor($factor); + } + } + + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleArithmeticExpression + * @return string The SQL. + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleArithmeticExpression($simpleArithmeticExpr); + } + } + + /** + * Walks down an PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkPathExpression($pathExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkPathExpression($pathExpr); + } + } + + /** + * Walks down an ResultVariable AST node, thereby generating the appropriate SQL. + * + * @param string $resultVariable + * @return string The SQL. + */ + public function walkResultVariable($resultVariable) + { + foreach ($this->_walkers as $walker) { + $walker->walkResultVariable($resultVariable); + } + } + + /** + * Gets an executor that can be used to execute the result of this walker. + * + * @return AbstractExecutor + */ + public function getExecutor($AST) + {} +} 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..51ef277 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php @@ -0,0 +1,1181 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Collections\ArrayCollection; + +use Doctrine\ORM\Query\Expr; + +/** + * 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; + + /** + * @var EntityManager The EntityManager used by this QueryBuilder. + */ + private $_em; + + /** + * @var array The array of DQL parts collected. + */ + private $_dqlParts = array( + 'distinct' => false, + 'select' => array(), + 'from' => array(), + 'join' => array(), + 'set' => array(), + 'where' => null, + 'groupBy' => array(), + 'having' => null, + 'orderBy' => array() + ); + + /** + * @var integer The type of query this is. Can be select, update or delete. + */ + private $_type = self::SELECT; + + /** + * @var integer The state of the query object. Can be dirty or clean. + */ + private $_state = self::STATE_CLEAN; + + /** + * @var string The complete DQL string for this query. + */ + private $_dql; + + /** + * @var \Doctrine\Common\Collections\ArrayCollection The query parameters. + */ + private $parameters = array(); + + /** + * @var integer The index of the first result to retrieve. + */ + private $_firstResult = null; + + /** + * @var integer The maximum number of results to retrieve. + */ + private $_maxResults = null; + + /** + * @var array Keeps root entity alias names for join entities. + */ + private $joinRootAliases = array(); + + /** + * Initializes a new QueryBuilder that uses the given EntityManager. + * + * @param EntityManager $em The EntityManager to use. + */ + public function __construct(EntityManager $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() + * ->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(); + } + + /** + * Get the type of the currently built query. + * + * @return integer + */ + public function getType() + { + return $this->_type; + } + + /** + * Get the associated EntityManager for this query builder. + * + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * Get the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() + { + return $this->_state; + } + + /** + * Get 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; + } + + $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; + + return $this->_em->createQuery($this->getDQL()) + ->setParameters($parameters) + ->setFirstResult($this->_firstResult) + ->setMaxResults($this->_maxResults); + } + + /** + * 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. + * @return string $rootAlias + */ + public function getRootAlias() + { + $aliases = $this->getRootAliases(); + 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 $rootAliases + */ + 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 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 $rootEntities + */ + 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 $params 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 string $dqlPart + * @param string $append + * @return QueryBuilder This QueryBuilder instance. + */ + public function add($dqlPartName, $dqlPart, $append = false) + { + $isMultiple = is_array($this->_dqlParts[$dqlPartName]); + + // 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); + } + + /** + * Add a DISTINCT flag to this query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->distinct() + * ->from('User', 'u'); + * + * + * @param bool + * @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)); + } + + /** + * Create and add 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); + } + + /** + * 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 $conditionType The condition type constant. Either ON or WITH. + * @param string $condition The condition for the join + * @param string $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 $conditionType The condition type constant. Either ON or WITH. + * @param string $condition The condition for the join + * @param string $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 $conditionType The condition type constant. Either ON or WITH. + * @param string $condition The condition for the join + * @param string $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($where) + { + $where = $this->getDQLPart('where'); + $args = func_get_args(); + + if ($where instanceof Expr\Andx) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new Expr\Andx($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') + * ->from('User', 'u') + * ->where('u.id = 1') + * ->orWhere('u.id = 2'); + * + * + * @param mixed $where The WHERE statement + * @return QueryBuilder $qb + * @see where() + */ + public function orWhere($where) + { + $where = $this->getDqlPart('where'); + $args = func_get_args(); + + if ($where instanceof Expr\Orx) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new Expr\Orx($args); + } + + return $this->add('where', $where, true); + } + + /** + * 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) + { + $having = $this->getDqlPart('having'); + $args = func_get_args(); + + 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) + { + $having = $this->getDqlPart('having'); + $args = func_get_args(); + + 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 $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 $sort The ordering expression. + * @param string $order The ordering direction. + * @return QueryBuilder This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) + { + return $this->add('orderBy', new Expr\OrderBy($sort, $order), true); + } + + /** + * Get 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]; + } + + /** + * Get all query parts. + * + * @return array $dqlParts + * @todo Rename: getQueryParts (or remove?) + */ + public function getDQLParts() + { + return $this->_dqlParts; + } + + 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' => ', ')); + } + + 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' => ', ')); + } + + 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; + } + + 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'] : ''); + } + + /** + * Reset DQL parts + * + * @param array $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; + } + + /** + * Reset 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 clone of 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/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..c01d964 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\Common\Cache; + +/** + * 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 Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + 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 + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(Console\Input\InputInterface $input, Console\Output\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 Cache\ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->write('Clearing ALL Metadata 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/QueryCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php new file mode 100644 index 0000000..a984b96 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\Common\Cache; + +/** + * 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 Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + 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 + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(Console\Input\InputInterface $input, Console\Output\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 Cache\ApcCache) { + throw new \LogicException("Cannot clear APC 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/ResultCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php new file mode 100644 index 0000000..096aa53 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\Common\Cache; + +/** + * 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 Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + 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 + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(Console\Input\InputInterface $input, Console\Output\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 Cache\ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->write('Clearing ALL Result 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/ConvertDoctrine1SchemaCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php new file mode 100644 index 0000000..a964299 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php @@ -0,0 +1,220 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\ORM\Tools\Export\ClassMetadataExporter, + Doctrine\ORM\Tools\ConvertDoctrine1Schema, + Doctrine\ORM\Tools\EntityGenerator; + +/** + * 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 Console\Command\Command +{ + /** + * @var EntityGenerator + */ + private $entityGenerator = null; + + /** + * @var ClassMetadataExporter + */ + private $metadataExporter = null; + + /** + * @return EntityGenerator + */ + public function getEntityGenerator() + { + if ($this->entityGenerator == null) { + $this->entityGenerator = new EntityGenerator(); + } + + return $this->entityGenerator; + } + + /** + * @param EntityGenerator $entityGenerator + */ + 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 + */ + public function setMetadataExporter(ClassMetadataExporter $metadataExporter) + { + $this->metadataExporter = $metadataExporter; + } + + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('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(<<getHelper('em')->getEntityManager(); + + // Process source directories + $fromPaths = array_merge(array($input->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($em, $fromPaths, $destPath, $toType, $numSpaces, $extend, $output); + } + + /** + * @param \Doctrine\ORM\EntityManager $em + * @param array $fromPaths + * @param string $destPath + * @param string $toType + * @param int $numSpaces + * @param string|null $extend + * @param Console\Output\OutputInterface $output + */ + public function convertDoctrine1Schema($em, $fromPaths, $destPath, $toType, $numSpaces, $extend, $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) + ); + } else 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) + ); + } else 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->write(PHP_EOL); + + foreach ($metadata as $class) { + $output->write(sprintf('Processing entity "%s"', $class->name) . PHP_EOL); + } + + $exporter->setMetadata($metadata); + $exporter->export(); + + $output->write(PHP_EOL . sprintf( + 'Converting Doctrine 1.X schema to "%s" mapping type in "%s"', $toType, $destPath + )); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} 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..84070fb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -0,0 +1,183 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\ORM\Tools\Console\MetadataFilter, + Doctrine\ORM\Tools\Export\ClassMetadataExporter, + Doctrine\ORM\Tools\EntityGenerator, + Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; + +/** + * 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 Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('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', null, 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, escpecially when using the --from-database +flag. + +Converting an existing databsae 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. +EOT + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + + if ($input->getOption('from-database') === true) { + $databaseDriver = new \Doctrine\ORM\Mapping\Driver\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, 0777, true); + } + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Mapping destination directory '%s' does not exist.", $input->getArgument('dest-path')) + ); + } else 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') !== false) ); + + 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->write(sprintf('Processing entity "%s"', $class->name) . PHP_EOL); + } + + $exporter->setMetadata($metadata); + $exporter->export(); + + $output->write(PHP_EOL . sprintf( + 'Exporting "%s" mapping information to "%s"' . PHP_EOL, $toType, $destPath + )); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } + + 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..53f90b2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console; + +/** + * 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 Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + 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 existance.' + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + $error = false; + try { + $em->getConfiguration()->ensureProductionSettings(); + + if ($input->getOption('complete') !== null) { + $em->getConnection()->connect(); + } + } catch (\Exception $e) { + $error = true; + $output->writeln('' . $e->getMessage() . ''); + } + + if ($error === false) { + $output->write('Environment is correctly configured for production.' . PHP_EOL); + } + } +} 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..7210b6b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php @@ -0,0 +1,160 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\ORM\Tools\Console\MetadataFilter, + Doctrine\ORM\Tools\EntityGenerator, + Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; + +/** + * 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 Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('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_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(<<--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 dont 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 + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(Console\Input\InputInterface $input, Console\Output\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')) + ); + } else 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')); + + if (($extend = $input->getOption('extend')) !== null) { + $entityGenerator->setClassToExtend($extend); + } + + foreach ($metadatas as $metadata) { + $output->write( + sprintf('Processing entity "%s"', $metadata->name) . PHP_EOL + ); + } + + // Generating Entities + $entityGenerator->generate($metadatas, $destPath); + + // Outputting information message + $output->write(PHP_EOL . sprintf('Entity classes generated to "%s"', $destPath) . PHP_EOL); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} 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..2a7de9d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\ORM\Tools\Console\MetadataFilter; + +/** + * 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 Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('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, 0777, true); + } + + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Proxies destination directory '%s' does not exist.", $em->getConfiguration()->getProxyDir()) + ); + } else 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->write( + sprintf('Processing entity "%s"', $metadata->name) . PHP_EOL + ); + } + + // Generating Proxies + $em->getProxyFactory()->generateProxyClasses($metadatas, $destPath); + + // Outputting information message + $output->write(PHP_EOL . sprintf('Proxy classes generated to "%s"', $destPath) . PHP_EOL); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + + } +} 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..b716aee --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\ORM\Tools\Console\MetadataFilter, + Doctrine\ORM\Tools\EntityRepositoryGenerator; + +/** + * 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 Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('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')); + + // 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')) + ); + } else 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(); + + foreach ($metadatas as $metadata) { + if ($metadata->customRepositoryClassName) { + $output->write( + sprintf('Processing repository "%s"', $metadata->customRepositoryClassName) . PHP_EOL + ); + + $generator->writeEntityRepositoryClass($metadata->customRepositoryClassName, $destPath); + + $numRepositories++; + } + } + + if ($numRepositories) { + // Outputting information message + $output->write(PHP_EOL . sprintf('Repository classes generated to "%s"', $destPath) . PHP_EOL); + } else { + $output->write('No Repository classes were found to be processed.' . PHP_EOL); + } + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} 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..b0902ba --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php @@ -0,0 +1,79 @@ +. + */ + +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 +{ + 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 + ); + } + + 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))); + + 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(''); + } + } + } +} 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..c9af93f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console; + +/** + * 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 Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + 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 + ) + )) + ->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); + } + + $resultSet = $query->execute(array(), constant($hydrationMode)); + + \Doctrine\Common\Util\Debug::dump($resultSet, $input->getOption('depth')); + } +} 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..a87eb20 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputInterface, + Symfony\Component\Console\Output\OutputInterface, + Symfony\Component\Console\Command\Command, + Doctrine\ORM\Tools\SchemaTool; + +abstract class AbstractCommand extends Command +{ + /** + * @param InputInterface $input + * @param OutputInterface $output + * @param SchemaTool $schemaTool + * @param array $metadatas + */ + abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas); + + /** + * @see Console\Command\Command + */ + 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 \Doctrine\ORM\Tools\SchemaTool($em); + + $this->executeSchemaCommand($input, $output, $tool, $metadatas); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} 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..69d3f36 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console\Input\InputInterface, + Symfony\Component\Console\Output\OutputInterface, + 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 +{ + /** + * @see Console\Command\Command + */ + 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 try to apply generated SQLs into EntityManager Storage Connection, output them.' + ) + )) + ->setHelp(<<getOption('dump-sql') === true) { + $sqls = $schemaTool->getCreateSchemaSql($metadatas); + $output->write(implode(';' . PHP_EOL, $sqls) . ';' . PHP_EOL); + } else { + $output->write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL . PHP_EOL); + + $output->write('Creating database schema...' . PHP_EOL); + $schemaTool->createSchema($metadatas); + $output->write('Database schema created successfully!' . PHP_EOL); + } + } +} 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..50fa645 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -0,0 +1,108 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console\Input\InputInterface, + Symfony\Component\Console\Output\OutputInterface, + 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 +{ + /** + * @see Console\Command\Command + */ + 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 try to apply generated SQLs into EntityManager Storage Connection, output them.' + ), + new InputOption( + 'force', null, 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(<<getOption('full-database')); + + if ($input->getOption('dump-sql') === true) { + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } + $output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL); + } else if ($input->getOption('force') === true) { + $output->write('Dropping database schema...' . PHP_EOL); + if ($isFullDatabaseDrop) { + $schemaTool->dropDatabase(); + } else { + $schemaTool->dropSchema($metadatas); + } + $output->write('Database schema dropped successfully!' . PHP_EOL); + } else { + $output->write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL . PHP_EOL); + + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } + + if (count($sqls)) { + $output->write('Schema-Tool would execute ' . count($sqls) . ' queries to drop the database.' . PHP_EOL); + $output->write('Please run the operation with --force to execute these queries or use --dump-sql to see them.' . PHP_EOL); + } else { + $output->write('Nothing to drop. The database is empty!' . PHP_EOL); + } + } + } +} 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..26552cc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -0,0 +1,131 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console\Input\InputInterface, + Symfony\Component\Console\Output\OutputInterface, + 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 +{ + protected $name = 'orm:schema-tool:update'; + + /** + * @see Console\Command\Command + */ + 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', null, 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 + +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. +EOT + ); + } + + 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') !== true); + + $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; + } + + $dumpSql = (true === $input->getOption('dump-sql')); + $force = (true === $input->getOption('force')); + if ($dumpSql && $force) { + throw new \InvalidArgumentException('You can pass either the --dump-sql or the --force option (but not both simultaneously).'); + } + + if ($dumpSql) { + $output->writeln(implode(';' . PHP_EOL, $sqls)); + } else if ($force) { + $output->writeln('Updating database schema...'); + $schemaTool->updateSchema($metadatas, $saveMode); + $output->writeln(sprintf('Database schema updated successfully! "%s" queries were executed', count($sqls))); + } else { + $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 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())); + } + } +} 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..3dbc54f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console; + +/** + * Validate that the current mapping is valid + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ValidateSchemaCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:validate-schema') + ->setDescription('Validate the mapping files.') + ->setHelp(<<getHelper('em')->getEntityManager(); + + $validator = new \Doctrine\ORM\Tools\SchemaValidator($em); + $errors = $validator->validateMapping(); + + $exit = 0; + if ($errors) { + foreach ($errors as $className => $errorMessages) { + $output->write("[Mapping] FAIL - The entity-class '" . $className . "' mapping is invalid:\n"); + foreach ($errorMessages as $errorMessage) { + $output->write('* ' . $errorMessage . "\n"); + } + $output->write("\n"); + } + $exit += 1; + } else { + $output->write('[Mapping] OK - The mapping files are correct.' . "\n"); + } + + if (!$validator->schemaInSyncWithMetadata()) { + $output->write('[Database] FAIL - The database schema is not in sync with the current mapping file.' . "\n"); + $exit += 2; + } else { + $output->write('[Database] OK - The database schema is in sync with the mapping files.' . "\n"); + } + + 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..e8bc6ec --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; + +class ConsoleRunner +{ + /** + * Run 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', \Doctrine\ORM\Version::VERSION); + $cli->setCatchExceptions(true); + $cli->setHelperSet($helperSet); + self::addCommands($cli); + $cli->addCommands($commands); + $cli->run(); + } + + /** + * @param Application $cli + */ + 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() + )); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Helper/EntityManagerHelper.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Helper/EntityManagerHelper.php new file mode 100644 index 0000000..a5da041 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Helper/EntityManagerHelper.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Helper; + +use Symfony\Component\Console\Helper\Helper, + Doctrine\ORM\EntityManager; + +/** + * 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 EntityManager + * @var EntityManager + */ + protected $_em; + + /** + * Constructor + * + * @param Connection $connection Doctrine Database Connection + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + } + + /** + * Retrieves Doctrine ORM EntityManager + * + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * @see Helper + */ + 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..72c753e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php @@ -0,0 +1,77 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console; + +/** + * Used by CLI Tools to restrict entity-based commands to given patterns. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @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 +{ + /** + * 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); + } + + private $_filter = array(); + + public function __construct(\ArrayIterator $metadata, $filter) + { + $this->_filter = (array)$filter; + parent::__construct($metadata); + } + + public function accept() + { + if (count($this->_filter) == 0) { + return true; + } + + $it = $this->getInnerIterator(); + $metadata = $it->current(); + + foreach ($this->_filter as $filter) { + if (strpos($metadata->name, $filter) !== false) { + return true; + } + } + return false; + } + + 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..46f358a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php @@ -0,0 +1,271 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\ORM\Tools\Export\Driver\AbstractExporter, + Doctrine\Common\Util\Inflector; + +/** + * 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 +{ + 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; + } + + /** + * Get an array of ClassMetadataInfo instances from the passed + * Doctrine 1 schema + * + * @return array $metadatas 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) \Symfony\Component\Yaml\Yaml::parse($file)); + } + } else { + $schema = array_merge($schema, (array) \Symfony\Component\Yaml\Yaml::parse($path)); + } + } + + $metadatas = array(); + foreach ($schema as $className => $mappingInformation) { + $metadatas[] = $this->_convertToClassMetadataInfo($className, $mappingInformation); + } + + return $metadatas; + } + + 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; + } + + 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]; + } + } + } + + 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); + } + } + + 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 ( ! \Doctrine\DBAL\Types\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); + } else if (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; + } + + private function _convertIndexes($className, array $model, ClassMetadataInfo $metadata) + { + if (isset($model['indexes']) && $model['indexes']) { + foreach ($model['indexes'] as $name => $index) { + $type = (isset($index['type']) && $index['type'] == 'unique') + ? 'uniqueConstraints' : 'indexes'; + + $metadata->table[$type][$name] = array( + 'columns' => $index['fields'] + ); + } + } + } + + private function _convertRelations($className, array $model, ClassMetadataInfo $metadata) + { + if (isset($model['relations']) && $model['relations']) { + 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'; + } else if ($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..c2bb0fd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php @@ -0,0 +1,151 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +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 +{ + private $file; + private $context; + + /** + * Pass a stream and contet 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; + } + + public function onFlush(OnFlushEventArgs $args) + { + $this->dumpIdentityMap($args->getEntityManager()); + } + + /** + * Dump the contents of the identity map into a stream. + * + * @param EntityManager $em + * @return void + */ + public function dumpIdentityMap(EntityManager $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->reflFields[$field]->getValue($entity); + + 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"); + } else if ($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); + } + + private function getType($var) + { + if (is_object($var)) { + $refl = new \ReflectionObject($var); + return $refl->getShortname(); + } else { + return gettype($var); + } + } + + private function getIdString($entity, $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]"; + } else if ($state == UnitOfWork::STATE_REMOVED) { + $idstring .= " [REMOVED]"; + } else if ($state == UnitOfWork::STATE_MANAGED) { + $idstring .= " [MANAGED]"; + } else if ($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..e44f99f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataFactory; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * 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 +{ + public function getReflectionService() + { + return new \Doctrine\Common\Persistence\Mapping\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..73c52fb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -0,0 +1,1299 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\Common\Util\Inflector, + 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 + */ + private $backupExisting = true; + + /** + * The extension to use for written php files + * + * @var string + */ + private $extension = '.php'; + + /** + * Whether or not the current ClassMetadataInfo instance is new or old + * + * @var boolean + */ + private $isNew = true; + + /** + * @var array + */ + private $staticReflection = array(); + + /** + * Number of spaces to use for indention in generated code + */ + private $numSpaces = 4; + + /** + * The actual spaces to use for indention + * + * @var string + */ + private $spaces = ' '; + + /** + * The class all generated entities should extend + * + * @var string + */ + private $classToExtend; + + /** + * Whether or not to generation annotations + * + * @var boolean + */ + private $generateAnnotations = false; + + /** + * @var string + */ + private $annotationsPrefix = ''; + + /** + * Whether or not to generated sub methods + * + * @var boolean + */ + private $generateEntityStubMethods = false; + + /** + * Whether or not to update the entity class if it exists already + * + * @var boolean + */ + private $updateEntityIfExists = false; + + /** + * Whether or not to re-generate entity class if it exists already + * + * @var boolean + */ + private $regenerateEntityIfExists = false; + + /** + * @var boolean + */ + private $fieldVisibility = 'private'; + + /** + * Hash-map for handle types + * + * @var array + */ + private $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 => 'float', + Type::JSON_ARRAY => 'array', + Type::SIMPLE_ARRAY => 'array', + ); + + /** + * @var array Hash-map to handle generator types string. + */ + 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' + ); + + /** + * @var array Hash-map to handle the change tracking policy string. + */ + protected static $changeTrackingPolicyMap = array( + ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT => 'DEFERRED_IMPLICIT', + ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT => 'DEFERRED_EXPLICIT', + ClassMetadataInfo::CHANGETRACKING_NOTIFY => 'NOTIFY', + ); + + /** + * @var array Hash-map to handle the inheritance type string. + */ + 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 + */ + private static $classTemplate = +' + +use Doctrine\ORM\Mapping as ORM; + + + +{ + +} +'; + + /** + * @var string + */ + private static $getMethodTemplate = +'/** + * + * + * @return + */ +public function () +{ +return $this->; +}'; + + /** + * @var string + */ + private static $setMethodTemplate = +'/** + * + * + * @param $ + * @return + */ +public function ($) +{ +$this-> = $; + +return $this; +}'; + + /** + * @var string + */ + private static $addMethodTemplate = +'/** + * + * + * @param $ + * @return + */ +public function ($) +{ +$this->[] = $; + +return $this; +}'; + + /** + * @var string + */ + private static $removeMethodTemplate = +'/** + * + * + * @param $ + */ +public function ($) +{ +$this->->removeElement($); +}'; + + /** + * @var string + */ + private static $lifecycleCallbackMethodTemplate = +'/** + * @ + */ +public function () +{ +// Add your code here +}'; + + /** + * @var string + */ + private static $constructorMethodTemplate = +'/** + * Constructor + */ +public function __construct() +{ + +} +'; + + public function __construct() + { + if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) { + $this->annotationsPrefix = 'ORM\\'; + } + } + + /** + * Generate and write 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); + } + } + + /** + * Generated and write entity class to disk for the given ClassMetadataInfo instance + * + * @param ClassMetadataInfo $metadata + * @param string $outputDirectory + * @return void + */ + 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, 0777, 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 + } else if ( ! $this->isNew && $this->updateEntityIfExists) { + file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path)); + } + } + + /** + * Generate a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance + * + * @param ClassMetadataInfo $metadata + * @return string $code + */ + public function generateEntityClass(ClassMetadataInfo $metadata) + { + $placeHolders = array( + '', + '', + '', + '' + ); + + $replacements = array( + $this->generateEntityNamespace($metadata), + $this->generateEntityDocBlock($metadata), + $this->generateEntityClassName($metadata), + $this->generateEntityBody($metadata) + ); + + $code = str_replace($placeHolders, $replacements, self::$classTemplate); + return str_replace('', $this->spaces, $code); + } + + /** + * Generate the updated code for the given ClassMetadataInfo and entity at path + * + * @param ClassMetadataInfo $metadata + * @param string $path + * @return string $code; + */ + 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" : ''). "}"; + } + + /** + * Set 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; + } + + /** + * Set the extension to use when writing php files to disk + * + * @param string $extension + * @return void + */ + public function setExtension($extension) + { + $this->extension = $extension; + } + + /** + * Set the name of the class the generated classes should extend from + * + * @return void + */ + public function setClassToExtend($classToExtend) + { + $this->classToExtend = $classToExtend; + } + + /** + * Set whether or not to generate annotations for the entity + * + * @param bool $bool + * @return void + */ + public function setGenerateAnnotations($bool) + { + $this->generateAnnotations = $bool; + } + + /** + * Set the class fields visibility for the entity (can either be private or protected) + * + * @param bool $bool + * @return void + */ + public function setFieldVisibility($visibility) + { + if ($visibility !== self::FIELD_VISIBLE_PRIVATE && $visibility !== self::FIELD_VISIBLE_PROTECTED) { + throw new \InvalidArgumentException('Invalid provided visibilty (only private and protected are allowed): ' . $visibility); + } + + $this->fieldVisibility = $visibility; + } + + /** + * Set an annotation prefix. + * + * @param string $prefix + */ + public function setAnnotationPrefix($prefix) + { + $this->annotationsPrefix = $prefix; + } + + /** + * Set 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; + } + + /** + * Set whether or not to regenerate the entity if it exists + * + * @param bool $bool + * @return void + */ + public function setRegenerateEntityIfExists($bool) + { + $this->regenerateEntityIfExists = $bool; + } + + /** + * Set 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? + */ + public function setBackupExisting($bool) + { + $this->backupExisting = $bool; + } + + /** + * @param string $type + * @return string + */ + private function getType($type) + { + if (isset($this->typeAlias[$type])) { + return $this->typeAlias[$type]; + } + + return $type; + } + + private function generateEntityNamespace(ClassMetadataInfo $metadata) + { + if ($this->hasNamespace($metadata)) { + return 'namespace ' . $this->getNamespace($metadata) .';'; + } + } + + private function generateEntityClassName(ClassMetadataInfo $metadata) + { + return 'class ' . $this->getClassName($metadata) . + ($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null); + } + + private function generateEntityBody(ClassMetadataInfo $metadata) + { + $fieldMappingProperties = $this->generateEntityFieldMappingProperties($metadata); + $associationMappingProperties = $this->generateEntityAssociationMappingProperties($metadata); + $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods($metadata) : null; + $lifecycleCallbackMethods = $this->generateEntityLifecycleCallbackMethods($metadata); + + $code = array(); + + if ($fieldMappingProperties) { + $code[] = $fieldMappingProperties; + } + + if ($associationMappingProperties) { + $code[] = $associationMappingProperties; + } + + $code[] = $this->generateEntityConstructor($metadata); + + if ($stubMethods) { + $code[] = $stubMethods; + } + + if ($lifecycleCallbackMethods) { + $code[] = $lifecycleCallbackMethods; + } + + return implode("\n", $code); + } + + private function generateEntityConstructor(ClassMetadataInfo $metadata) + { + if ($this->hasMethod('__construct', $metadata)) { + return ''; + } + + $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), self::$constructorMethodTemplate)); + } + + return ''; + } + + /** + * @todo this won't work if there is a namespace in brackets and a class outside of it. + * @param string $src + */ + private 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]; + } else if (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; + } else if ($token[0] == T_CLASS) { + $inClass = true; + } else if ($token[0] == T_FUNCTION) { + if ($tokens[$i+2][0] == T_STRING) { + $this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1]; + } else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) { + $this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1]; + } + } else if (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); + } + } + } + + private function hasProperty($property, ClassMetadataInfo $metadata) + { + if ($this->extendsClass()) { + // don't generate property if its already on the base class. + $reflClass = new \ReflectionClass($this->getClassToExtend()); + if ($reflClass->hasProperty($property)) { + return true; + } + } + + return ( + isset($this->staticReflection[$metadata->name]) && + in_array($property, $this->staticReflection[$metadata->name]['properties']) + ); + } + + private function hasMethod($method, ClassMetadataInfo $metadata) + { + if ($this->extendsClass()) { + // don't generate method if its already on the base class. + $reflClass = new \ReflectionClass($this->getClassToExtend()); + if ($reflClass->hasMethod($method)) { + return true; + } + } + + return ( + isset($this->staticReflection[$metadata->name]) && + in_array($method, $this->staticReflection[$metadata->name]['methods']) + ); + } + + private function hasNamespace(ClassMetadataInfo $metadata) + { + return strpos($metadata->name, '\\') ? true : false; + } + + private function extendsClass() + { + return $this->classToExtend ? true : false; + } + + private function getClassToExtend() + { + return $this->classToExtend; + } + + private function getClassToExtendName() + { + $refl = new \ReflectionClass($this->getClassToExtend()); + + return '\\' . $refl->getName(); + } + + private function getClassName(ClassMetadataInfo $metadata) + { + return ($pos = strrpos($metadata->name, '\\')) + ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name; + } + + private function getNamespace(ClassMetadataInfo $metadata) + { + return substr($metadata->name, 0, strrpos($metadata->name, '\\')); + } + + private function generateEntityDocBlock(ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = '/**'; + $lines[] = ' * ' . $this->getClassName($metadata); + + if ($this->generateAnnotations) { + $lines[] = ' *'; + + $methods = array( + 'generateTableAnnotation', + 'generateInheritanceAnnotation', + 'generateDiscriminatorColumnAnnotation', + 'generateDiscriminatorMapAnnotation' + ); + + foreach ($methods as $method) { + if ($code = $this->$method($metadata)) { + $lines[] = ' * ' . $code; + } + } + + if ($metadata->isMappedSuperclass) { + $lines[] = ' * @' . $this->annotationsPrefix . 'MappedSuperClass'; + } else { + $lines[] = ' * @' . $this->annotationsPrefix . 'Entity'; + } + + if ($metadata->customRepositoryClassName) { + $lines[count($lines) - 1] .= '(repositoryClass="' . $metadata->customRepositoryClassName . '")'; + } + + if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { + $lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks'; + } + } + + $lines[] = ' */'; + + return implode("\n", $lines); + } + + private function generateTableAnnotation($metadata) + { + $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['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) . ')'; + } + + private 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); + } + + private function generateInheritanceAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + return '@' . $this->annotationsPrefix . 'InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")'; + } + } + + private function generateDiscriminatorColumnAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $discrColumn = $metadata->discriminatorValue; + $columnDefinition = 'name="' . $discrColumn['name'] + . '", type="' . $discrColumn['type'] + . '", length=' . $discrColumn['length']; + + return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')'; + } + } + + private 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) . '})'; + } + } + + private function generateEntityStubMethods(ClassMetadataInfo $metadata) + { + $methods = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ( ! isset($fieldMapping['id']) || ! $fieldMapping['id'] || $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE) { + 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->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; + } + } else if ($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); + } + + private function isAssociationIsNullable($associationMapping) + { + if (isset($associationMapping['id']) && $associationMapping['id']) { + return false; + } + if (isset($associationMapping['joinColumns'])) { + $joinColumns = $associationMapping['joinColumns']; + } else { + //@todo thereis no way to retreive targetEntity metadata + $joinColumns = array(); + } + foreach ($joinColumns as $joinColumn) { + if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) { + return false; + } + } + return true; + } + + private 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 ""; + } + + private 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); + } + + private function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ($this->hasProperty($fieldMapping['fieldName'], $metadata) || + $metadata->isInheritedField($fieldMapping['fieldName'])) { + continue; + } + + $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata); + $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName'] + . (isset($fieldMapping['default']) ? ' = ' . var_export($fieldMapping['default'], true) : null) . ";\n"; + } + + return implode("\n", $lines); + } + + private function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null) + { + $methodName = $type . Inflector::classify($fieldName); + if (in_array($type, array("add", "remove")) && substr($methodName, -1) == "s") { + $methodName = substr($methodName, 0, -1); + } + + if ($this->hasMethod($methodName, $metadata)) { + return; + } + $this->staticReflection[$metadata->name]['methods'][] = $methodName; + + $var = sprintf('%sMethodTemplate', $type); + $template = self::$$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) . ' ' . $fieldName, + '' => $methodTypeHint, + '' => $variableType, + '' => Inflector::camelize($fieldName), + '' => $methodName, + '' => $fieldName, + '' => ($defaultValue !== null ) ? (' = '.$defaultValue) : '', + '' => $this->getClassName($metadata) + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + $template + ); + + return $this->prefixCodeWithSpaces($method); + } + + private 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), + self::$lifecycleCallbackMethodTemplate + ); + + return $this->prefixCodeWithSpaces($method); + } + + private 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) . ')'; + } + + private 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"'; + + $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); + } + + private 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['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); + } + + private function prefixCodeWithSpaces($code, $num = 1) + { + $lines = explode("\n", $code); + + foreach ($lines as $key => $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 it's subclasses. + * @return string The literal string for the inheritance type. + * @throws \InvalidArgumentException When the inheritance type does not exists. + */ + protected function getInheritanceTypeString($type) + { + if ( ! isset(self::$inheritanceTypeMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided InheritanceType: %s', $type)); + } + + return self::$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 exists. + */ + protected function getChangeTrackingPolicyString($type) + { + if ( ! isset(self::$changeTrackingPolicyMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided ChangeTrackingPolicy: %s', $type)); + } + + return self::$changeTrackingPolicyMap[$type]; + } + + /** + * @param integer $type The generator to use for the mapped class. + * @return string The literal string for the generetor type. + * @throws \InvalidArgumentException When the generator type does not exists. + */ + protected function getIdGeneratorTypeString($type) + { + if ( ! isset(self::$generatorStrategyMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided IdGeneratorType: %s', $type)); + } + + return self::$generatorStrategyMap[$type]; + } +} 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..e696abc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php @@ -0,0 +1,81 @@ +. + */ + +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 +{ + protected static $_template = +'; + +use Doctrine\ORM\EntityRepository; + +/** + * + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class extends EntityRepository +{ +} +'; + + public function generateEntityRepositoryClass($fullClassName) + { + $namespace = substr($fullClassName, 0, strrpos($fullClassName, '\\')); + $className = substr($fullClassName, strrpos($fullClassName, '\\') + 1, strlen($fullClassName)); + + $variables = array( + '' => $namespace, + '' => $className + ); + return str_replace(array_keys($variables), array_values($variables), self::$_template); + } + + 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, 0777, true); + } + + if ( ! file_exists($path)) { + file_put_contents($path, $code); + } + } +} 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..0473111 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Tools\Event; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\ORM\EntityManager; + +/** + * Event Args used for the Events::postGenerateSchema event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +class GenerateSchemaEventArgs extends \Doctrine\Common\EventArgs +{ + private $_em = null; + private $_schema = null; + + /** + * @param ClassMetadata $classMetadata + * @param Schema $schema + * @param Table $classTable + */ + public function __construct(EntityManager $em, Schema $schema) + { + $this->_em = $em; + $this->_schema = $schema; + } + + /** + * @return EntityManager + */ + 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..060c554 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php @@ -0,0 +1,71 @@ +. + */ +namespace Doctrine\ORM\Tools\Event; + +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/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +class GenerateSchemaTableEventArgs extends \Doctrine\Common\EventArgs +{ + private $_classMetadata = null; + private $_schema = null; + private $_classTable = null; + + /** + * @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..843c004 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export; + +use Doctrine\ORM\Tools\Export\ExportException, + Doctrine\ORM\EntityManager; + +/** + * 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 +{ + 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' + ); + + /** + * Register a new exporter driver class under a specified name + * + * @param string $name + * @param string $class + */ + public static function registerExportDriver($name, $class) + { + self::$_exporterDrivers[$name] = $class; + } + + /** + * Get a exporter driver instance + * + * @param string $type The type to get (yml, xml, etc.) + * @param string $source The directory where the exporter will export to + * @return AbstractExporter $exporter + */ + 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..7c13397 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php @@ -0,0 +1,213 @@ +. + */ + +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 +{ + protected $_metadata = array(); + protected $_outputDir; + protected $_extension; + protected $_overwriteExistingFiles = false; + + public function __construct($dir = null) + { + $this->_outputDir = $dir; + } + + public function setOverwriteExistingFiles($overwrite) + { + $this->_overwriteExistingFiles = $overwrite; + } + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it + * + * @param ClassMetadataInfo $metadata + * @return mixed $exported + */ + abstract public function exportClassMetadata(ClassMetadataInfo $metadata); + + /** + * Set the array of ClassMetadataInfo instances to export + * + * @param array $metadata + * @return void + */ + public function setMetadata(array $metadata) + { + $this->_metadata = $metadata; + } + + /** + * Get the extension used to generated the path to a class + * + * @return string $extension + */ + public function getExtension() + { + return $this->_extension; + } + + /** + * Set 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; + } + + /** + * Export each ClassMetadata instance to a single Doctrine Mapping file + * named after the entity + * + * @return void + */ + public function export() + { + if ( ! is_dir($this->_outputDir)) { + mkdir($this->_outputDir, 0777, 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, 0777, true); + } + if (file_exists($path) && !$this->_overwriteExistingFiles) { + throw ExportException::attemptOverwriteExistingFile($path); + } + file_put_contents($path, $output); + } + } + } + + /** + * Generate the path to write the class for the given ClassMetadataInfo instance + * + * @param ClassMetadataInfo $metadata + * @return string $path + */ + protected function _generateOutputPath(ClassMetadataInfo $metadata) + { + return $this->_outputDir . '/' . str_replace('\\', '.', $metadata->name) . $this->_extension; + } + + /** + * Set 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; + } + + protected function _getInheritanceTypeString($type) + { + switch ($type) + { + case ClassMetadataInfo::INHERITANCE_TYPE_NONE: + return 'NONE'; + break; + + case ClassMetadataInfo::INHERITANCE_TYPE_JOINED: + return 'JOINED'; + break; + + case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE: + return 'SINGLE_TABLE'; + break; + + case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS: + return 'PER_CLASS'; + break; + } + } + + protected function _getChangeTrackingPolicyString($policy) + { + switch ($policy) + { + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT: + return 'DEFERRED_IMPLICIT'; + break; + + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT: + return 'DEFERRED_EXPLICIT'; + break; + + case ClassMetadataInfo::CHANGETRACKING_NOTIFY: + return 'NOTIFY'; + break; + } + } + + protected function _getIdGeneratorTypeString($type) + { + switch ($type) + { + case ClassMetadataInfo::GENERATOR_TYPE_AUTO: + return 'AUTO'; + break; + + case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE: + return 'SEQUENCE'; + break; + + case ClassMetadataInfo::GENERATOR_TYPE_TABLE: + return 'TABLE'; + break; + + case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY: + return 'IDENTITY'; + break; + } + } +} 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..d1ec92a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo, + 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 +{ + protected $_extension = '.php'; + private $_entityGenerator; + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it + * + * @param ClassMetadataInfo $metadata + * @return string $exported + */ + 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); + } + + protected function _generateOutputPath(ClassMetadataInfo $metadata) + { + return $this->_outputDir . '/' . str_replace('\\', '/', $metadata->name) . $this->_extension; + } + + 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..24f7243 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php @@ -0,0 +1,165 @@ +. + */ + +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 +{ + protected $_extension = '.php'; + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it + * + * @param ClassMetadataInfo $metadata + * @return mixed $exported + */ + 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]); + } + } + $associationMappingArray = array( + 'fieldName' => $associationMapping['fieldName'], + 'targetEntity' => $associationMapping['targetEntity'], + 'cascade' => $cascade, + ); + + 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); + } else if ($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); + } else if ($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); + } + + 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..48cf019 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php @@ -0,0 +1,343 @@ +. + */ + +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 +{ + protected $_extension = '.dcm.xml'; + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it + * + * @param ClassMetadataInfo $metadata + * @return mixed $exported + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + $xml = new \SimpleXmlElement(""); + + /*$xml->addAttribute('xmlns', 'http://doctrine-project.org/schemas/orm/doctrine-mapping'); + $xml->addAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + $xml->addAttribute('xsi:schemaLocation', 'http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd');*/ + + 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 (isset($metadata->table['inheritance-type'])) { + $root->addAttribute('inheritance-type', $metadata->table['inheritance-type']); + } + + 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($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]); + } + } + + 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']); + $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); + } + } + } + + 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']); + } + if (isset($field['options'])) { + $optionsXml = $fieldXml->addChild('options'); + foreach ($field['options'] as $key => $value) { + $optionsXml->addAttribute($key, $value); + } + } + 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 $name => $associationMapping) { + if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_ONE) { + $associationMappingXml = $root->addChild('one-to-one'); + } else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_ONE) { + $associationMappingXml = $root->addChild('many-to-one'); + } else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { + $associationMappingXml = $root->addChild('one-to-many'); + } else if ($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['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); + } + } + $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($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); + } + + /** + * @param \SimpleXMLElement $simpleXml + * @return string $xml + */ + private function _asXml($simpleXml) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->loadXML($simpleXml->asXML()); + $dom->formatOutput = true; + + $result = $dom->saveXML(); + return $result; + } +} 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..ceafa83 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php @@ -0,0 +1,205 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +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 +{ + protected $_extension = '.dcm.yml'; + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it + * + * TODO: Should this code be pulled out in to a toArray() method in ClassMetadata + * + * @param ClassMetadataInfo $metadata + * @return mixed $exported + */ + 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']; + } + + $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; + } + + if ($ids) { + $array['fields'] = $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 ($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; + } + + } else if ($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; + } else if ($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 \Symfony\Component\Yaml\Yaml::dump(array($metadata->name => $array), 10); + } +} 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..5ba8bd2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php @@ -0,0 +1,23 @@ + 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 + */ + 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"); + } + + $rootAlias = $from[0]->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..0fc7e75 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php @@ -0,0 +1,90 @@ + + * @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 + */ + 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'); + } + + $rootComponents = array(); + foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) { + $isParent = array_key_exists('parent', $qComp) + && $qComp['parent'] === null + && $qComp['nestingLevel'] == 0 + ; + if ($isParent) { + $rootComponents[] = array($dqlAlias => $qComp); + } + } + if (count($rootComponents) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + $root = reset($rootComponents); + $parentName = key($root); + $parent = current($root); + $identifierFieldName = $parent['metadata']->getSingleIdentifierFieldName(); + + $pathType = PathExpression::TYPE_STATE_FIELD; + if (isset($parent['metadata']->associationMappings[$identifierFieldName])) { + $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + } + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, + $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..35f8219 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -0,0 +1,202 @@ + 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; + + /** + * 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); + + parent::__construct($query, $parserResult, $queryComponents); + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT + * + * @param SelectStatement $AST + * @return string + */ + public function walkSelectStatement(SelectStatement $AST) + { + $innerSql = 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"); + } + + $rootAlias = $from[0]->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 + $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', + implode(', ', $sqlIdentifier), $innerSql); + + if ($this->platform instanceof PostgreSqlPlatform) { + //http://www.doctrine-project.org/jira/browse/DDC-1958 + $this->getPostgresqlSql($AST, $sqlIdentifier, $innerSql, $sql); + } + + // 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; + } + + /** + * Generate new SQL for postgresql if necessary + * + * @param SelectStatement $AST + * @param array sqlIdentifier + * @param string $sql + */ + public function getPostgresqlSql(SelectStatement $AST, array $sqlIdentifier, $innerSql, &$sql) + { + // For every order by, find out the SQL alias by inspecting the ResultSetMapping + $sqlOrderColumns = array(); + $orderBy = array(); + if (isset($AST->orderByClause)) { + foreach ($AST->orderByClause->orderByItems as $item) { + $possibleAliases = array_keys($this->rsm->fieldMappings, $item->expression->field); + + foreach ($possibleAliases as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $item->expression->identificationVariable) { + $sqlOrderColumns[] = $alias; + $orderBy[] = $alias . ' ' . $item->type; + break; + } + } + } + //remove identifier aliases + $sqlOrderColumns = array_diff($sqlOrderColumns, $sqlIdentifier); + } + + //we don't need orderBy in inner query + //However at least on 5.4.6 I'm getting a segmentation fault and thus we don't clear it for now + /*$AST->orderByClause = null; + $innerSql = parent::walkSelectStatement($AST);*/ + + if (count($orderBy)) { + $sql = sprintf( + 'SELECT DISTINCT %s FROM (%s) dctrn_result ORDER BY %s', + implode(', ', array_merge($sqlIdentifier, $sqlOrderColumns)), + $innerSql, + implode(', ', $orderBy) + ); + } + } +} 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..1ae74f4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php @@ -0,0 +1,119 @@ + + * @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, + Doctrine\ORM\Query\TreeWalkerAdapter, + Doctrine\ORM\Query\AST\SelectStatement, + Doctrine\ORM\Query\AST\SelectExpression, + Doctrine\ORM\Query\AST\PathExpression, + Doctrine\ORM\Query\AST\AggregateExpression; + +/** + * 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'; + + /** + * @var int Counter for generating unique order column aliases + */ + private $_aliasCounter = 0; + + /** + * Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids + * of the root Entity + * + * @param SelectStatement $AST + * @return void + */ + public function walkSelectStatement(SelectStatement $AST) + { + $parent = null; + $parentName = null; + $selectExpressions = array(); + + foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) { + // preserve mixed data in query for ordering + if (isset($qComp['resultVariable'])) { + $selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias); + continue; + } + + if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) { + $parent = $qComp; + $parentName = $dqlAlias; + continue; + } + } + + $identifier = $parent['metadata']->getSingleIdentifierFieldName(); + if (isset($parent['metadata']->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($parent['metadata']->getTypeOfField($identifier)) + ); + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, + $parentName, + $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) { + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, + $item->expression->identificationVariable, + $item->expression->field + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + $AST->selectClause->selectExpressions[] = new SelectExpression( + $pathExpression, + '_dctrn_ord' . $this->_aliasCounter++ + ); + } + } + } + + $AST->selectClause->isDistinct = true; + } + +} + + + 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..ee139f7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php @@ -0,0 +1,234 @@ +. + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\ORM\QueryBuilder, + Doctrine\ORM\Query, + Doctrine\ORM\Query\ResultSetMapping, + Doctrine\ORM\NoResultException; + +/** + * Paginator + * + * 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; + } + + /** + * Set 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) { + /* @var $countQuery Query */ + $countQuery = $this->cloneQuery($this->query); + + if ( ! $countQuery->getHint(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 { + $countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker')); + } + + $countQuery->setFirstResult(null)->setMaxResults(null); + + try { + $data = $countQuery->getScalarResult(); + $data = array_map('current', $data); + $this->count = array_sum($data); + } 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 { + $subQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('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()); + } + + $whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('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); + + $result = $whereInQuery->getResult($this->query->getHydrationMode()); + } else { + $result = $this->cloneQuery($this->query) + ->setMaxResults($length) + ->setFirstResult($offset) + ->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()); + + foreach ($query->getHints() as $name => $value) { + $cloneQuery->setHint($name, $value); + } + + return $cloneQuery; + } + + /** + * Determine 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; + } +} + 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..d65b581 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php @@ -0,0 +1,145 @@ + + * @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, + Doctrine\ORM\Query\AST\SimpleArithmeticExpression, + Doctrine\ORM\Query\TreeWalkerAdapter, + Doctrine\ORM\Query\AST\SelectStatement, + Doctrine\ORM\Query\AST\PathExpression, + Doctrine\ORM\Query\AST\InExpression, + Doctrine\ORM\Query\AST\NullComparisonExpression, + Doctrine\ORM\Query\AST\InputParameter, + Doctrine\ORM\Query\AST\ConditionalPrimary, + Doctrine\ORM\Query\AST\ConditionalTerm, + Doctrine\ORM\Query\AST\ConditionalExpression, + Doctrine\ORM\Query\AST\ConditionalFactor, + 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 + */ + public function walkSelectStatement(SelectStatement $AST) + { + $rootComponents = array(); + foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) { + $isParent = array_key_exists('parent', $qComp) + && $qComp['parent'] === null + && $qComp['nestingLevel'] == 0 + ; + if ($isParent) { + $rootComponents[] = array($dqlAlias => $qComp); + } + } + if (count($rootComponents) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + $root = reset($rootComponents); + $parentName = key($root); + $parent = current($root); + $identifierFieldName = $parent['metadata']->getSingleIdentifierFieldName(); + + $pathType = PathExpression::TYPE_STATE_FIELD; + if (isset($parent['metadata']->associationMappings[$identifierFieldName])) { + $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + } + + $pathExpression = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, $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..ac14140 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php @@ -0,0 +1,95 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * ResolveTargetEntityListener + * + * Mechanism to overwrite interfaces or classes specified as association + * targets. + * + * @author Benjamin Eberlei + * @since 2.2 + */ +class ResolveTargetEntityListener +{ + /** + * @var array + */ + private $resolveTargetEntities = array(); + + /** + * Add 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; + } + + /** + * Process event and resolve new target entity names. + * + * @param LoadClassMetadataEventArgs $args + * @return void + */ + public function loadClassMetadata(LoadClassMetadataEventArgs $args) + { + $cm = $args->getClassMetadata(); + foreach ($cm->associationMappings as $mapping) { + if (isset($this->resolveTargetEntities[$mapping['targetEntity']])) { + $this->remapAssociation($cm, $mapping); + } + } + } + + 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..3dee05b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -0,0 +1,737 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\ORMException, + Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets, + Doctrine\ORM\EntityManager, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Internal\CommitOrderCalculator, + Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs, + 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 + */ +class SchemaTool +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + 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\EntityManager $em + */ + public function __construct(EntityManager $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. + * + * @throws ToolsException + * @param array $classes + * @return void + */ + 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 $sql 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); + } + + /** + * Some instances of ClassMetadata don't need to be processed in the SchemaTool context. This method detects them. + * + * @param ClassMetadata $class + * @param array $processedClasses + * @return bool + */ + private function processingNotRequired($class, array $processedClasses) + { + return ( + isset($processedClasses[$class->name]) || + $class->isMappedSuperclass || + ($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName) + ); + } + + /** + * From a given set of metadata classes this method creates a Schema instance. + * + * @param array $classes + * @return Schema + */ + 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); + + foreach ($classes as $class) { + if ($this->processingNotRequired($class, $processedClasses)) { + continue; + } + + $table = $schema->createTable($this->quoteStrategy->getTableName($class, $this->platform)); + $columns = array(); // table columns + + if ($class->isInheritanceTypeSingleTable()) { + $columns = $this->_gatherColumns($class, $table); + $this->_gatherRelationsSql($class, $table, $schema); + + // 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); + $processedClasses[$subClassName] = true; + } + } else if ($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); + + // 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 + /* @var \Doctrine\ORM\Mapping\ClassMetadata $class */ + $idMapping = $class->fieldMappings[$class->identifier[0]]; + $this->_gatherColumn($class, $idMapping, $table); + $columnName = $this->quoteStrategy->getColumnName($class->identifier[0], $class, $this->platform); + // TODO: This seems rather hackish, can we optimize it? + $table->getColumn($columnName)->setAutoincrement(false); + + $pkColumns[] = $columnName; + + // Add a FK constraint on the ID column + $table->addUnnamedForeignKeyConstraint( + $this->quoteStrategy->getTableName($this->em->getClassMetadata($class->rootEntityName), $this->platform), + array($columnName), array($columnName), array('onDelete' => 'CASCADE') + ); + } + + $table->setPrimaryKey($pkColumns); + + } else if ($class->isInheritanceTypeTablePerClass()) { + throw ORMException::notSupported(); + } else { + $this->_gatherColumns($class, $table); + $this->_gatherRelationsSql($class, $table, $schema); + } + + $pkColumns = array(); + foreach ($class->identifier as $identifierField) { + if (isset($class->fieldMappings[$identifierField])) { + $pkColumns[] = $this->quoteStrategy->getColumnName($identifierField, $class, $this->platform); + } else if (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) { + $table->addIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName); + } + } + + if (isset($class->table['uniqueConstraints'])) { + foreach ($class->table['uniqueConstraints'] as $indexName => $indexData) { + $table->addUniqueIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName); + } + } + + 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 + * @return array The portable column definition of the discriminator column as required by + * the DBAL. + */ + private function addDiscriminatorColumnDefinition($class, $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) + { + $columns = array(); + $pkColumns = array(); + + foreach ($class->fieldMappings as $fieldName => $mapping) { + if ($class->isInheritanceTypeSingleTable() && isset($mapping['inherited'])) { + continue; + } + + $column = $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); + } + + return $columns; + } + + /** + * 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) + { + $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 ( isset($mapping['options'][$knownOption])) { + $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 \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Schema $schema + * @return void + */ + private function _gatherRelationsSql($class, $table, $schema) + { + foreach ($class->associationMappings as $fieldName => $mapping) { + if (isset($mapping['inherited'])) { + continue; + } + + $foreignClass = $this->em->getClassMetadata($mapping['targetEntity']); + + if ($mapping['type'] & ClassMetadata::TO_ONE && $mapping['isOwningSide']) { + $primaryKeyColumns = $uniqueConstraints = array(); // PK is unnecessary for this relation-type + + $this->_gatherRelationJoinColumns($mapping['joinColumns'], $table, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints); + + foreach($uniqueConstraints as $indexName => $unique) { + $table->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); + } + } else if ($mapping['type'] == ClassMetadata::ONE_TO_MANY && $mapping['isOwningSide']) { + //... create join table, one-many through join table supported later + throw ORMException::notSupported(); + } else if ($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 = $uniqueConstraints = array(); + + // Build first FK constraint (relation table => source table) + $this->_gatherRelationJoinColumns($joinTable['joinColumns'], $theJoinTable, $class, $mapping, $primaryKeyColumns, $uniqueConstraints); + + // Build second FK constraint (relation table => target table) + $this->_gatherRelationJoinColumns($joinTable['inverseJoinColumns'], $theJoinTable, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints); + + $theJoinTable->setPrimaryKey($primaryKeyColumns); + + foreach($uniqueConstraints as $indexName => $unique) { + $theJoinTable->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); + } + } + } + } + + /** + * Get 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 recursivly 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); + } else 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; + } + + /** + * Gather columns and fk constraints that are required for one part of relationship. + * + * @param array $joinColumns + * @param \Doctrine\DBAL\Schema\Table $theJoinTable + * @param ClassMetadata $class + * @param array $mapping + * @param array $primaryKeyColumns + * @param array $uniqueConstraints + */ + private function _gatherRelationJoinColumns($joinColumns, $theJoinTable, $class, $mapping, &$primaryKeyColumns, &$uniqueConstraints) + { + $localColumns = array(); + $foreignColumns = array(); + $fkOptions = array(); + $foreignTableName = $this->quoteStrategy->getTableName($class, $this->platform); + + 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']; + } else if (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']; + } else if ($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']; + } + } + + $theJoinTable->addUnnamedForeignKeyConstraint( + $foreignTableName, $localColumns, $foreignColumns, $fkOptions + ); + } + + /** + * Drops the database schema for the given classes. + * + * In any way when an exception is thrown it is supressed 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 \Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector($this->platform); + /* @var $schema \Doctrine\DBAL\Schema\Schema */ + $schema->visit($visitor); + return $visitor->getQueries(); + } + + /** + * Get SQL to drop the tables defined by the passed classes. + * + * @param array $classes + * @return array + */ + public function getDropSchemaSQL(array $classes) + { + $visitor = new \Doctrine\DBAL\Schema\Visitor\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 \Doctrine\DBAL\Schema\Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + + if ($saveMode) { + return $schemaDiff->toSaveSql($this->platform); + } else { + 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..7a3f6f5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php @@ -0,0 +1,289 @@ +. +*/ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\DBAL\Types\Type; + +/** + * Performs strict validation of the mapping schema + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SchemaValidator +{ + /** + * @var EntityManager + */ + private $em; + + /** + * @param EntityManager $em + */ + public function __construct(EntityManager $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. + * 4. Check if there are public properties that might cause problems with lazy loading. + * + * @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; + } + + /** + * Validate 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."; + } + + /* @var $assoc AssociationMapping */ + 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."; + } + 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."; + } else if ($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."; + } else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] != $fieldName) { + $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " are ". + "incosistent 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."; + } else if ($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."; + } else if ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] != $fieldName) { + $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . + $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " are ". + "incosistent 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."; + } else if ($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."; + } else if ($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."; + } + + } else if ($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 '". $class->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)) { + $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a foreign field " . + $orderField . " that is not a field on the target entity " . $targetMetadata->name; + } + } + } + } + + foreach ($class->reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $publicAttr) { + if ($publicAttr->isStatic()) { + continue; + } + $ce[] = "Field '".$publicAttr->getName()."' in class '".$class->name."' must be private ". + "or protected. Public fields may break lazy-loading."; + } + + 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; + } + + /** + * @param string $columnName + * @param ClassMetadataInfo $class + * @return bool + */ + private function columnExistsOnEntity($columnName, $class) + { + if (isset($class->fieldNames[$columnName])) { + return true; + } + foreach ($class->associationMappings as $assoc) { + if ($assoc['isOwningSide']) { + foreach ($assoc['joinColumns'] as $columnMapping) { + if ($columnMapping['name'] == $columnName) { + return true; + } + } + } + } + return false; + } + + /** + * Check 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..fe4be7a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php @@ -0,0 +1,196 @@ +. +*/ + +namespace Doctrine\ORM\Tools; + +use Doctrine\Common\ClassLoader; +use Doctrine\Common\Cache\Cache; +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 autoloaders for a setup where Doctrine is checked out from + * its github repository at {@link http://github.com/doctrine/doctrine2} + * + * @param string $gitCheckoutRootPath + * @return void + */ + static public function registerAutoloadGit($gitCheckoutRootPath) + { + if (!class_exists('Doctrine\Common\ClassLoader', false)) { + require_once $gitCheckoutRootPath . "/lib/vendor/doctrine-common/lib/Doctrine/Common/ClassLoader.php"; + } + + $loader = new ClassLoader("Doctrine\Common", $gitCheckoutRootPath . "/lib/vendor/doctrine-common/lib"); + $loader->register(); + + $loader = new ClassLoader("Doctrine\DBAL", $gitCheckoutRootPath . "/lib/vendor/doctrine-dbal/lib"); + $loader->register(); + + $loader = new ClassLoader("Doctrine\ORM", $gitCheckoutRootPath . "/lib"); + $loader->register(); + + $loader = new ClassLoader("Symfony\Component", $gitCheckoutRootPath . "/lib/vendor"); + $loader->register(); + } + + /** + * Use this method to register all autoloaders for a setup where Doctrine is installed + * though {@link http://pear.doctrine-project.org}. + * + * This method registers autoloaders for both Doctrine and Symfony top + * level namespaces. + * + * @return void + */ + static public function registerAutoloadPEAR() + { + if (!class_exists('Doctrine\Common\ClassLoader', false)) { + require_once "Doctrine/Common/ClassLoader.php"; + } + + $loader = new ClassLoader("Doctrine"); + $loader->register(); + + $loader = new ClassLoader("Symfony"); + $loader->register(); + } + + /** + * Use this method to register all autoloads for a downloaded Doctrine library. + * Pick the directory the library was uncompressed into. + * + * @param string $directory + */ + static public 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(); + } + + /** + * Create a configuration with an annotation metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * @param bool $useSimpleAnnotationReader + * @return Configuration + */ + static public 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; + } + + /** + * Create a configuration with a xml metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * @return Configuration + */ + static public function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl(new XmlDriver($paths)); + return $config; + } + + /** + * Create a configuration with a yaml metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * @return Configuration + */ + static public function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl(new YamlDriver($paths)); + return $config; + } + + /** + * Create a configuration without a metadata driver. + * + * @param bool $isDevMode + * @param string $proxyDir + * @param Cache $cache + * @return Configuration + */ + static public 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(); + } else if (extension_loaded('xcache')) { + $cache = new \Doctrine\Common\Cache\XcacheCache(); + } else if (extension_loaded('memcache')) { + $memcache = new \Memcache(); + $memcache->connect('127.0.0.1'); + $cache = new \Doctrine\Common\Cache\MemcacheCache(); + $cache->setMemcache($memcache); + } else if (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(); + } + } else if ($cache === null) { + $cache = new ArrayCache(); + } + $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..7aa98d9 --- /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 recieves + * 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..30f7ea1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\ORMException; + +/** + * Tools related Exceptions + * + * @author Benjamin Eberlei + */ +class ToolsException extends ORMException +{ + public static function schemaToolFailure($sql, \Exception $e) + { + return new self("Schema-Tool failed with Error '" . $e->getMessage() . "' while executing DDL: " . $sql, "0", $e); + } + + 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..3e04224 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/TransactionRequiredException.php @@ -0,0 +1,37 @@ +. + */ + +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/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class TransactionRequiredException extends ORMException +{ + 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..5d6b6d1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/UnexpectedResultException.php @@ -0,0 +1,31 @@ +. + */ + +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..e937733 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php @@ -0,0 +1,3087 @@ +. + */ + +namespace Doctrine\ORM; + +use Exception, InvalidArgumentException, UnexpectedValueException, + Doctrine\Common\Collections\ArrayCollection, + Doctrine\Common\Collections\Collection, + Doctrine\Common\NotifyPropertyChanged, + Doctrine\Common\PropertyChangedListener, + Doctrine\Common\Persistence\ObjectManagerAware, + Doctrine\ORM\Event\LifecycleEventArgs, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Proxy\Proxy; + +/** + * 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. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @internal This class contains highly performance-sensitive code. + */ +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. + * + * @var array + * @internal 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. + */ + 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 + * @todo rename: scheduledForSynchronization + */ + private $scheduledForDirtyCheck = 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 \Doctrine\ORM\EntityManager + */ + 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; + + /** + * 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(); + + /** + * Initializes a new UnitOfWork instance, bound to the given EntityManager. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + $this->evm = $em->getEventManager(); + } + + /** + * 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 + * + * @throws \Exception + * + * @return void + */ + public function commit($entity = null) + { + // Raise preFlush + if ($this->evm->hasListeners(Events::preFlush)) { + $this->evm->dispatchEvent(Events::preFlush, new Event\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)) { + return; // Nothing to do. + } + + if ($this->orphanRemovals) { + foreach ($this->orphanRemovals as $orphan) { + $this->remove($orphan); + } + } + + // Raise onFlush + if ($this->evm->hasListeners(Events::onFlush)) { + $this->evm->dispatchEvent(Events::onFlush, new Event\OnFlushEventArgs($this->em)); + } + + // 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; --$i) { + $this->executeDeletions($commitOrder[$i]); + } + } + + $conn->commit(); + } catch (Exception $e) { + $this->em->close(); + $conn->rollback(); + + throw $e; + } + + // Take new snapshots from visited collections + foreach ($this->visitedCollections as $coll) { + $coll->takeSnapshot(); + } + + // Raise postFlush + if ($this->evm->hasListeners(Events::postFlush)) { + $this->evm->dispatchEvent(Events::postFlush, new Event\PostFlushEventArgs($this->em)); + } + + // Clear up + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->extraUpdates = + $this->entityChangeSets = + $this->collectionUpdates = + $this->collectionDeletions = + $this->visitedCollections = + $this->scheduledForDirtyCheck = + $this->orphanRemovals = array(); + } + + /** + * Compute 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 flush 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 + * + * @throws \InvalidArgumentException + * + * @return void + */ + 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 are processed here. + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityInsertions[$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); + } + } + + /** + * 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. + */ + 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)); + } + + // Fire PreFlush lifecycle callbacks + if (isset($class->lifecycleCallbacks[Events::preFlush])) { + $class->invokeLifecycleCallbacks(Events::preFlush, $entity); + } + + $actualData = array(); + + foreach ($class->reflFields as $name => $refProp) { + $value = $refProp->getValue($entity); + + if ($class->isCollectionValuedAssociation($name) && $value !== null && ! ($value instanceof PersistentCollection)) { + // 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 havent 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) { + $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. + */ + 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->scheduledForDirtyCheck[$className])): + $entitiesToProcess = $this->scheduledForDirtyCheck[$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 are processed here. + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityInsertions[$oid]) && isset($this->entityStates[$oid])) { + $this->computeChangeSet($class, $entity); + } + } + } + } + + /** + * Computes the changes of an association. + * + * @param array $assoc + * @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) { + $state = $this->getEntityState($entry, self::STATE_NEW); + + if ( ! ($entry instanceof $assoc['targetEntity'])) { + throw new ORMException( + sprintf( + 'Found entity of type %s on association %s#%s, but expecting %s', + get_class($entry), + $assoc['sourceEntity'], + $assoc['fieldName'], + $targetClass->name + ) + ); + } + + 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 ClassMetadata $class + * @param object $entity + */ + private function persistNew($class, $entity) + { + $oid = spl_object_hash($entity); + + if (isset($class->lifecycleCallbacks[Events::prePersist])) { + $class->invokeLifecycleCallbacks(Events::prePersist, $entity); + } + + if ($this->evm->hasListeners(Events::prePersist)) { + $this->evm->dispatchEvent(Events::prePersist, new LifecycleEventArgs($entity, $this->em)); + } + + $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. + * + * @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()) { + $actualData[$name] = $refProp->getValue($entity); + } + } + + $originalData = $this->originalEntityData[$oid]; + $changeSet = array(); + + foreach ($actualData as $propName => $actualValue) { + $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; + + if (is_object($orgValue) && $orgValue !== $actualValue) { + $changeSet[$propName] = array($orgValue, $actualValue); + } else if ($orgValue != $actualValue || ($orgValue === null ^ $actualValue === null)) { + $changeSet[$propName] = array($orgValue, $actualValue); + } + } + + if ($changeSet) { + if (isset($this->entityChangeSets[$oid])) { + $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet); + } + + $this->originalEntityData[$oid] = $actualData; + } + } + + /** + * Executes all entity insertions for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + */ + private function executeInserts($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + $entities = array(); + + $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postPersist]); + $hasListeners = $this->evm->hasListeners(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 ($hasLifecycleCallbacks || $hasListeners) { + $entities[] = $entity; + } + } + + $postInsertIds = $persister->executeInserts(); + + if ($postInsertIds) { + // Persister returned post-insert IDs + foreach ($postInsertIds as $id => $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) { + if ($hasLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::postPersist, $entity); + } + + if ($hasListeners) { + $this->evm->dispatchEvent(Events::postPersist, new LifecycleEventArgs($entity, $this->em)); + } + } + } + + /** + * Executes all entity updates for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + */ + private function executeUpdates($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + + $hasPreUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::preUpdate]); + $hasPreUpdateListeners = $this->evm->hasListeners(Events::preUpdate); + + $hasPostUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postUpdate]); + $hasPostUpdateListeners = $this->evm->hasListeners(Events::postUpdate); + + foreach ($this->entityUpdates as $oid => $entity) { + if ($this->em->getClassMetadata(get_class($entity))->name !== $className) { + continue; + } + + if ($hasPreUpdateLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::preUpdate, $entity); + + $this->recomputeSingleEntityChangeSet($class, $entity); + } + + if ($hasPreUpdateListeners) { + $this->evm->dispatchEvent( + Events::preUpdate, + new Event\PreUpdateEventArgs($entity, $this->em, $this->entityChangeSets[$oid]) + ); + } + + if (!empty($this->entityChangeSets[$oid])) { + $persister->update($entity); + } + + unset($this->entityUpdates[$oid]); + + if ($hasPostUpdateLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::postUpdate, $entity); + } + + if ($hasPostUpdateListeners) { + $this->evm->dispatchEvent(Events::postUpdate, new LifecycleEventArgs($entity, $this->em)); + } + } + } + + /** + * Executes all entity deletions for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + */ + private function executeDeletions($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + + $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postRemove]); + $hasListeners = $this->evm->hasListeners(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 ($hasLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::postRemove, $entity); + } + + if ($hasListeners) { + $this->evm->dispatchEvent(Events::postRemove, new LifecycleEventArgs($entity, $this->em)); + } + } + } + + /** + * Gets the commit order. + * + * @param array $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 (dont 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) { + $className = $this->em->getClassMetadata(get_class($entity))->name; + + if ($calc->hasClass($className)) { + continue; + } + + $class = $this->em->getClassMetadata($className); + $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. + * + * @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. + * + * @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). + */ + 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->scheduledForDirtyCheck[$rootEntityName][spl_object_hash($entity)]); + } + + /** + * INTERNAL: + * Schedules an entity for deletion. + * + * @param object $entity + */ + 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 $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. + * + * @throws ORMInvalidArgumentException + * + * @return boolean TRUE if the registration was successful, FALSE if the identity of + * the entity in question is already managed. + */ + 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 integer $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->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 + * + * @throws ORMInvalidArgumentException + * + * @return boolean + */ + 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 string $idHash + * @param string $rootClassName + * + * @return mixed The found entity or FALSE. + */ + public function tryGetByIdHash($idHash, $rootClassName) + { + if (isset($this->identityMap[$rootClassName][$idHash])) { + return $this->identityMap[$rootClassName][$idHash]; + } + + 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. + */ + 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. + * + * @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->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. + */ + 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. + * + * @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: + if (isset($class->lifecycleCallbacks[Events::preRemove])) { + $class->invokeLifecycleCallbacks(Events::preRemove, $entity); + } + + if ($this->evm->hasListeners(Events::preRemove)) { + $this->evm->dispatchEvent(Events::preRemove, new LifecycleEventArgs($entity, $this->em)); + } + + $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 + * + * @throws OptimisticLockException If the entity uses optimistic locking through a version + * attribute and the version check against the managed copy fails. + * + * @return object The managed copy of the entity. + * + * @todo Require active transaction!? OptimisticLockException may result in undefined state!? + */ + public function merge($entity) + { + $visited = array(); + + return $this->doMerge($entity, $visited); + } + + /** + * convert foreign identifiers into scalar foreign key values to avoid object to string conversion failures. + * + * @param ClassMetadata $class + * @param array $id + * @return array + */ + private function flattenIdentifier($class, $id) + { + $flatId = array(); + + foreach ($id as $idField => $idValue) { + if (isset($class->associationMappings[$idField])) { + $targetClassMetadata = $this->em->getClassMetadata($class->associationMappings[$idField]['targetEntity']); + $associatedId = $this->getEntityIdentifier($idValue); + + $flatId[$idField] = $associatedId[$targetClassMetadata->identifier[0]]; + } + } + + return $flatId; + } + + /** + * Executes a merge operation on an entity. + * + * @param object $entity + * @param array $visited + * @param object $prevManagedCopy + * @param array $assoc + * + * @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 + * + * @return object The managed copy of the entity. + */ + private function doMerge($entity, array &$visited, $prevManagedCopy = null, $assoc = null) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return $visited[$oid]; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + $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) { + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + $entity->__load(); + } + + // 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->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 new EntityNotFoundException; + } + + $managedCopy = $this->newInstance($class); + $class->setIdentifierValues($managedCopy, $id); + + $this->persistNew($class, $managedCopy); + } else { + if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized__) { + $managedCopy->__load(); + } + } + } + + if ($class->isVersioned) { + $managedCopyVersion = $class->reflFields[$class->versionField]->getValue($managedCopy); + $entityVersion = $class->reflFields[$class->versionField]->getValue($entity); + + // Throw exception if versions dont match. + if ($managedCopyVersion != $entityVersion) { + throw OptimisticLockException::lockFailedVersionMissmatch($entity, $entityVersion, $managedCopyVersion); + } + } + + // Merge state of $entity into existing (managed) entity + foreach ($class->reflClass->getProperties() 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; + } else 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[$oid][$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)); + } + } + + if ($class->isChangeTrackingDeferredExplicit()) { + $this->scheduleForDirtyCheck($entity); + } + } + + if ($prevManagedCopy !== null) { + $assocField = $assoc['fieldName']; + $prevClass = $this->em->getClassMetadata(get_class($prevManagedCopy)); + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + $prevClass->reflFields[$assocField]->setValue($prevManagedCopy, $managedCopy); + } else { + $prevClass->reflFields[$assocField]->getValue($prevManagedCopy)->add($managedCopy); + + if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) { + $class->reflFields[$assoc['mappedBy']]->setValue($managedCopy, $prevManagedCopy); + } + } + } + + // Mark the managed copy visited as well + $visited[spl_object_hash($managedCopy)] = true; + + $this->cascadeMerge($entity, $managedCopy, $visited); + + return $managedCopy; + } + + /** + * Detaches an entity from the persistence management. It's persistence will + * no longer be managed by Doctrine. + * + * @param object $entity The entity to detach. + */ + 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 + */ + 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. + * + * @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. + * + * @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 + */ + 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 + */ + 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 + */ + 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)): + foreach ($relatedEntities as $relatedEntity) { + $this->doPersist($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doPersist($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Cascades the delete operation to associated entities. + * + * @param object $entity + * @param array $visited + */ + private function cascadeRemove($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeRemove']; } + ); + + 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) { + $this->doRemove($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doRemove($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Acquire a lock on the given entity. + * + * @param object $entity + * @param int $lockMode + * @param int $lockVersion + * + * @throws ORMInvalidArgumentException + * @throws TransactionRequiredException + * @throws OptimisticLockException + * + * @return void + */ + 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 ($lockMode) { + case \Doctrine\DBAL\LockMode::OPTIMISTIC; + if ( ! $class->isVersioned) { + throw OptimisticLockException::notVersioned($class->name); + } + + if ($lockVersion === null) { + return; + } + + $entityVersion = $class->reflFields[$class->versionField]->getValue($entity); + + if ($entityVersion != $lockVersion) { + throw OptimisticLockException::lockFailedVersionMissmatch($entity, $lockVersion, $entityVersion); + } + + break; + + case \Doctrine\DBAL\LockMode::PESSIMISTIC_READ: + case \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE: + 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 $entityName if given, only entities of this type will get detached + */ + public function clear($entityName = null) + { + if ($entityName === null) { + $this->identityMap = + $this->entityIdentifiers = + $this->originalEntityData = + $this->entityChangeSets = + $this->entityStates = + $this->scheduledForDirtyCheck = + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->collectionDeletions = + $this->collectionUpdates = + $this->extraUpdates = + $this->readOnlyObjects = + $this->orphanRemovals = array(); + + if ($this->commitOrderCalculator !== null) { + $this->commitOrderCalculator->clear(); + } + } else { + $visited = array(); + foreach ($this->identityMap as $className => $entities) { + if ($className === $entityName) { + foreach ($entities as $entity) { + $this->doDetach($entity, $visited, true); + } + } + } + } + + 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 + */ + 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 + */ + 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. + * + * @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. + * @internal Highly performance-sensitive method. + * + * @todo Rename: getOrCreateEntity + */ + public function createEntity($className, array $data, &$hints = array()) + { + $class = $this->em->getClassMetadata($className); + //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]); + + 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]; + } + + $idHash = implode(' ', $id); + } else { + $idHash = isset($class->associationMappings[$class->identifier[0]]) + ? $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']] + : $data[$class->identifier[0]]; + + $id = array($class->identifier[0] => $idHash); + } + + if (isset($this->identityMap[$class->rootEntityName][$idHash])) { + $entity = $this->identityMap[$class->rootEntityName][$idHash]; + $oid = spl_object_hash($entity); + + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + $entity->__isInitialized__ = true; + $overrideLocalValues = true; + + if ($entity instanceof NotifyPropertyChanged) { + $entity->addPropertyChangedListener($this); + } + + // inject ObjectManager into just loaded proxies. + if ($overrideLocalValues && $entity instanceof ObjectManagerAware) { + $entity->injectObjectManager($this->em, $class); + } + + } 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; + } + + // inject ObjectManager upon refresh. + if ($overrideLocalValues && $entity instanceof ObjectManagerAware) { + $entity->injectObjectManager($this->em, $class); + } + } + + if ($overrideLocalValues) { + $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']) { + // Inverse side of x-to-one can never be lazy + $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity)); + + continue 2; + } + + $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; + } + } + } + + 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->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: + // 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) { + if (isset($class->lifecycleCallbacks[Events::postLoad])) { + $class->invokeLifecycleCallbacks(Events::postLoad, $entity); + } + + + if ($this->evm->hasListeners(Events::postLoad)) { + $this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->em)); + } + } + + 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; + } + } + + /** + * 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 + */ + 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 + */ + 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)]; + } + + /** + * 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 mixed 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. + * @todo Rename: scheduleForSynchronization + */ + public function scheduleForDirtyCheck($entity) + { + $rootClassName = $this->em->getClassMetadata(get_class($entity))->rootEntityName; + + $this->scheduledForDirtyCheck[$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\BasicEntityPersister + */ + 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 Persisters\BasicEntityPersister($this->em, $class); + break; + + case ($class->isInheritanceTypeSingleTable()): + $persister = new Persisters\SingleTablePersister($this->em, $class); + break; + + case ($class->isInheritanceTypeJoined()): + $persister = new Persisters\JoinedSubclassPersister($this->em, $class); + break; + + default: + $persister = new Persisters\UnionSubclassPersister($this->em, $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\AbstractCollectionPersister + */ + public function getCollectionPersister(array $association) + { + $type = $association['type']; + + if (isset($this->collectionPersisters[$type])) { + return $this->collectionPersisters[$type]; + } + + switch ($type) { + case ClassMetadata::ONE_TO_MANY: + $persister = new Persisters\OneToManyPersister($this->em); + break; + + case ClassMetadata::MANY_TO_MANY: + $persister = new Persisters\ManyToManyPersister($this->em); + break; + } + + $this->collectionPersisters[$type] = $persister; + + return $this->collectionPersisters[$type]; + } + + /** + * INTERNAL: + * Registers an entity as managed. + * + * @param object $entity The entity. + * @param array $id The identifier values. + * @param array $data The original entity data. + */ + 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->addPropertyChangedListener($this); + } + } + + /** + * INTERNAL: + * Clears the property changeset of the entity with the given OID. + * + * @param string $oid The entity's OID. + */ + 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. + */ + 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->scheduledForDirtyCheck[$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; + } + + /** + * Get 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 + * + * @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 + * + * @throws ORMInvalidArgumentException + * + * @return void + */ + 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 + * + * @throws ORMInvalidArgumentException + * + * @return bool + */ + public function isReadOnly($object) + { + if ( ! is_object($object)) { + throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object); + } + + return isset($this->readOnlyObjects[spl_object_hash($object)]); + } +} 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..8638b36 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Version.php @@ -0,0 +1,55 @@ +. + */ + +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.3.4'; + + /** + * 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/doctrine/orm/phpunit.xml.dist b/vendor/doctrine/orm/phpunit.xml.dist new file mode 100644 index 0000000..3ab5edb --- /dev/null +++ b/vendor/doctrine/orm/phpunit.xml.dist @@ -0,0 +1,59 @@ + + + + + + + ./tests/Doctrine/Tests/ORM + + + + + + performance + locking_functional + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/doctrine/orm/run-all.sh b/vendor/doctrine/orm/run-all.sh new file mode 100644 index 0000000..80712ee --- /dev/null +++ b/vendor/doctrine/orm/run-all.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# This script is a small convenience wrapper for running the doctrine testsuite against a large bunch of databases. +# Just create the phpunit.xmls as described in the array below and configure the specific files section +# to connect to that database. Just omit a file if you dont have that database and the tests will be skipped. + +configs[1]="mysql.phpunit.xml" +configs[2]='postgres.phpunit.xml' +configs[3]='sqlite.phpunit.xml' +configs[4]='oracle.phpunit.xml' +configs[5]='db2.phpunit.xml' +configs[6]='pdo-ibm.phpunit.xml' +configs[7]='sqlsrv.phpunit.xml' + +for i in "${configs[@]}"; do + if [ -f "$i" ]; + then + echo "RUNNING TESTS WITH CONFIG $i" + phpunit -c "$i" "$@" + fi; +done diff --git a/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/CHANGELOG.md b/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/CHANGELOG.md new file mode 100644 index 0000000..e28434a --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/CHANGELOG.md @@ -0,0 +1,7 @@ +## 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/Incenteev/ParameterHandler/LICENSE b/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/LICENSE new file mode 100644 index 0000000..72adcbf --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/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/Incenteev/ParameterHandler/README.md b/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/README.md new file mode 100644 index 0000000..d7b9bb3 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/README.md @@ -0,0 +1,98 @@ +# 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 ``parameters`` key. + +## Usage + +Add the following in your root composer.json file: + +```json +{ + "require": { + "incenteev/composer-parameter-handler": "1.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 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 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. + +Warning: This parameters handler will overwrite any comments or spaces into +your parameters.yml file so handle with care. So if you want to give format +and comments to your parameter's file you should do it on your dist version. diff --git a/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/ScriptHandler.php b/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/ScriptHandler.php new file mode 100644 index 0000000..6950bfc --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/ScriptHandler.php @@ -0,0 +1,123 @@ +getComposer()->getPackage()->getExtra(); + + if (empty($extras['incenteev-parameters']['file'])) { + throw new \InvalidArgumentException('The extra.incenteev-parameters.file setting is required to use this script handler.'); + } + + $realFile = $extras['incenteev-parameters']['file']; + + if (empty($extras['incenteev-parameters']['dist-file'])) { + $distFile = $realFile.'.dist'; + } else { + $distFile = $extras['incenteev-parameters']['dist-file']; + } + + $keepOutdatedParams = false; + if (isset($extras['incenteev-parameters']['keep-outdated'])) { + $keepOutdatedParams = (boolean)$extras['incenteev-parameters']['keep-outdated']; + } + + if (!is_file($distFile)) { + throw new \InvalidArgumentException(sprintf('The dist file "%s" does not exist. Check your dist-file config or create it.', $distFile)); + } + + $exists = is_file($realFile); + + $yamlParser = new Parser(); + $io = $event->getIO(); + + $action = $exists ? 'Updating' : 'Creating'; + $io->write(sprintf('%s the "%s" file.', $action, $realFile)); + + // Find the expected params + $expectedValues = $yamlParser->parse(file_get_contents($distFile)); + if (!isset($expectedValues['parameters'])) { + throw new \InvalidArgumentException('The dist file seems invalid.'); + } + $expectedParams = (array) $expectedValues['parameters']; + + // find the actual params + $actualValues = array('parameters' => array()); + if ($exists) { + $existingValues = $yamlParser->parse(file_get_contents($realFile)); + if (!is_array($existingValues)) { + throw new \InvalidArgumentException(sprintf('The existing "%s" file does not contain an array', $realFile)); + } + $actualValues = array_merge($actualValues, $existingValues); + } + $actualParams = (array) $actualValues['parameters']; + + if (!$keepOutdatedParams) { + // Remove the outdated params + foreach ($actualParams as $key => $value) { + if (!array_key_exists($key, $expectedParams)) { + unset($actualParams[$key]); + } + } + } + + $envMap = empty($extras['incenteev-parameters']['env-map']) ? array() : (array) $extras['incenteev-parameters']['env-map']; + + // Add the params coming from the environment values + $actualParams = array_replace($actualParams, self::getEnvValues($envMap)); + + $actualParams = self::getParams($io, $expectedParams, $actualParams); + + file_put_contents($realFile, "# This file is auto-generated during the composer install\n" . Yaml::dump(array('parameters' => $actualParams))); + } + + private static function getEnvValues(array $envMap) + { + $params = array(); + foreach ($envMap as $param => $env) { + $value = getenv($env); + if ($value) { + $params[$param] = Inline::parse($value); + } + } + + return $params; + } + + private static function getParams(IOInterface $io, array $expectedParams, array $actualParams) + { + // Simply use the expectedParams value as default for the missing params. + if (!$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; + $io->write('Some parameters are missing. Please provide them.'); + } + + $default = Inline::dump($message); + $value = $io->ask(sprintf('%s (%s):', $key, $default), $default); + + $actualParams[$key] = Inline::parse($value); + } + + return $actualParams; + } +} diff --git a/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/composer.json b/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/composer.json new file mode 100644 index 0000000..160558c --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Incenteev/ParameterHandler/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" + } + ], + "minimum-stability": "dev", + "require": { + "php": ">=5.3.3", + "symfony/yaml": "~2.0" + }, + "require-dev": { + "composer/composer": "*" + }, + "autoload": { + "psr-0": { "Incenteev\\ParameterHandler": "" } + }, + "target-dir": "Incenteev/ParameterHandler", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + } +} 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..d9a9942 --- /dev/null +++ b/vendor/jdorn/sql-formatter/README.md @@ -0,0 +1,159 @@ +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..0d3c641 --- /dev/null +++ b/vendor/jdorn/sql-formatter/examples/examples.php @@ -0,0 +1,243 @@ + + + + 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 MyTable WHERE id = 46", + + "SELECT count(*),`Column1`,`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
+
+
+ + +

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..fec46e3 --- /dev/null +++ b/vendor/jdorn/sql-formatter/lib/SqlFormatter.php @@ -0,0 +1,939 @@ + + * @author Florin Patan + * @copyright 2013 Jeremy Dorn + * @license http://opensource.org/licenses/MIT + * @link http://github.com/jdorn/sql-formatter + * @version 1.2.9 + */ +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; + + // 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', + 'CHARSET', 'CHECK', 'CHECKSUM', 'COLLATE', 'COLLATION', 'COLUMN', 'COLUMNS', 'COMMENT', 'COMMIT', 'COMMITTED', 'COMPRESSED', 'CONCURRENT', + 'CONSTRAINT', 'CONTAINS', 'CONVERT', 'COUNT', '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', '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', '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_CACHE', 'SQL_NO_CACHE', 'START', 'STARTING', 'STATUS', 'STOP', 'STORAGE', + 'STRAIGHT_JOIN', 'STRING', 'SUPER', 'TABLE', 'TABLES', 'TEMPORARY', 'TERMINATED', 'THEN', 'TO', 'TRAILING', 'TRANSACTIONAL', + '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 JOIN', 'RIGHT JOIN', 'OUTER JOIN', 'INNER JOIN', 'JOIN', 'XOR', 'OR', 'AND' + ); + + // 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 $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"; + + // 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;
    +
    +    // 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
    +        usort(self::$reserved, array('SqlFormatter', 'sortLength'));
    +
    +        // Set up regular expressions
    +        self::$regex_boundaries = '('.implode('|',array_map(array('SqlFormatter', 'quote_regex'),self::$boundaries)).')';
    +        self::$regex_reserved = '('.implode('|',array_map(array('SqlFormatter', 'quote_regex'),self::$reserved)).')';
    +        self::$regex_reserved_toplevel = str_replace(' ','\\s+','('.implode('|',array_map(array('SqlFormatter', 'quote_regex'),self::$reserved_toplevel)).')');
    +        self::$regex_reserved_newline = str_replace(' ','\\s+','('.implode('|',array_map(array('SqlFormatter', 'quote_regex'),self::$reserved_newline)).')');
    +
    +        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 => $string
    +            );
    +            
    +            // 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)) {
    +                $return[self::TOKEN_VALUE] = $matches[1];
    +            }
    +            
    +            return $return;
    +        }
    +       
    +        // Number
    +        if(preg_match('/^([0-9]+(\.[0-9]+)?)($|\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]))
    +                );
    +            }
    +        }
    +
    +        // 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
    +        );
    +    }
    +
    +    /**
    +     * 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;
    +        
    +        // 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);
    +                }               
    +            }
    +           
    +            // Commas start a new line (unless within inline parentheses)
    +            elseif ($token[self::TOKEN_VALUE] === ',' && !$inline_parentheses) {
    +                $newline = true;
    +            }
    +           
    +            // 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);
    +                }
    +            }
    +
    +            // 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($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 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];
    +        }
    +
    +        return $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_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 . '';
    +        }
    +    }
    +
    +    /**
    +     * Helper function for sorting the list of reserved words by length
    +     *
    +     * @param String $a The first string
    +     * @param String $b The second string
    +     *
    +     * @return int The comparison of the string lengths
    +     */
    +    private static function sortLength($a, $b)
    +    {
    +        return strlen($b) - strlen($a);
    +    }
    +   
    +    /**
    +     * 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..a1679b9 --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/SqlFormatterTest.php @@ -0,0 +1,148 @@ +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))); + } + + 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 = "SELECT\n * FROM\n MyTable"; + $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 = array_slice($return, 0, 50); + + 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 = array_slice($return, 0, 50); + 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 = array_slice($return, 0, 50); + return $return; + } + + + + function sqlData() { + if(!$this->sqlData) { + $this->sqlData = explode("\n\n",file_get_contents(__DIR__."/sql.sql")); + } + + /** + $formatHighlight = array(); + $highlight = array(); + $format = array(); + + foreach($this->sqlData as $sql) { + $formatHighlight[] = trim(SqlFormatter::format($sql)); + $highlight[] = trim(SqlFormatter::highlight($sql)); + $format[] = trim(SqlFormatter::format($sql, 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)); + /**/ + + return $this->sqlData; + } + +} 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..15908b1 --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/format-highlight.html @@ -0,0 +1,758 @@ +
    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
    \ 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..6c88c75 --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/format.html @@ -0,0 +1,757 @@ +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); \ 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..5db651d --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/highlight.html @@ -0,0 +1,245 @@ +
    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);
    \ 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..f1b358f --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/sql.sql @@ -0,0 +1,245 @@ +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); diff --git a/vendor/kriswallsmith/assetic/Gemfile b/vendor/kriswallsmith/assetic/Gemfile new file mode 100644 index 0000000..1c53249 --- /dev/null +++ b/vendor/kriswallsmith/assetic/Gemfile @@ -0,0 +1,5 @@ +source "https://rubygems.org" + +gem "sprockets", "~> 1.0.0" +gem "sass" +gem "compass" diff --git a/vendor/kriswallsmith/assetic/LICENSE b/vendor/kriswallsmith/assetic/LICENSE new file mode 100644 index 0000000..50b8e21 --- /dev/null +++ b/vendor/kriswallsmith/assetic/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010-2013 OpenSky Project Inc + +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/kriswallsmith/assetic/README.md b/vendor/kriswallsmith/assetic/README.md new file mode 100644 index 0000000..a6e5664 --- /dev/null +++ b/vendor/kriswallsmith/assetic/README.md @@ -0,0 +1,327 @@ +# Assetic [![Build Status](https://travis-ci.org/kriswallsmith/assetic.png?branch=master)](https://travis-ci.org/kriswallsmith/assetic) ![project status](http://stillmaintained.com/kriswallsmith/assetic.png) # + +Assetic is an asset management framework for PHP. + +``` php +dump(); +``` + +Assets +------ + +An Assetic asset is something with filterable content that can be loaded and +dumped. An asset also includes metadata, some of which can be manipulated and +some of which is immutable. + +| **Property** | **Accessor** | **Mutator** | +|--------------|-----------------|---------------| +| content | getContent | setContent | +| mtime | getLastModified | n/a | +| source root | getSourceRoot | n/a | +| source path | getSourcePath | n/a | +| target path | getTargetPath | setTargetPath | + +Filters +------- + +Filters can be applied to manipulate assets. + +``` php +dump(); +``` + +The filters applied to the collection will cascade to each asset leaf if you +iterate over it. + +``` php +dump(); +} +``` + +The core provides the following filters in the `Assetic\Filter` namespace: + + * `CoffeeScriptFilter`: compiles CoffeeScript into Javascript + * `CompassFilter`: Compass CSS authoring framework + * `CssEmbedFilter`: embeds image data in your stylesheets + * `CssImportFilter`: inlines imported stylesheets + * `CssMinFilter`: minifies CSS + * `CssRewriteFilter`: fixes relative URLs in CSS assets when moving to a new URL + * `DartFilter`: compiles Javascript using dart2js + * `EmberPrecompileFilter`: precompiles Handlebars templates into Javascript for use in the Ember.js framework + * `GoogleClosure\CompilerApiFilter`: compiles Javascript using the Google Closure Compiler API + * `GoogleClosure\CompilerJarFilter`: compiles Javascript using the Google Closure Compiler JAR + * `GssFilter`: compliles CSS using the Google Closure Stylesheets Compiler + * `HandlebarsFilter`: compiles Handlebars templates into Javascript + * `JpegoptimFilter`: optimize your JPEGs + * `JpegtranFilter`: optimize your JPEGs + * `JSMinFilter`: minifies Javascript + * `JSMinPlusFilter`: minifies Javascript + * `LessFilter`: parses LESS into CSS (using less.js with node.js) + * `LessphpFilter`: parses LESS into CSS (using lessphp) + * `OptiPngFilter`: optimize your PNGs + * `PackagerFilter`: parses Javascript for packager tags + * `PackerFilter`: compresses Javascript using Dean Edwards's Packer + * `PhpCssEmbedFilter`: embeds image data in your stylesheet + * `PngoutFilter`: optimize your PNGs + * `Sass\SassFilter`: parses SASS into CSS + * `Sass\ScssFilter`: parses SCSS into CSS + * `ScssphpFilter`: parses SCSS using scssphp + * `SprocketsFilter`: Sprockets Javascript dependency management + * `StylusFilter`: parses STYL into CSS + * `TypeScriptFilter`: parses TypeScript into Javascript + * `UglifyCssFilter`: minifies CSS + * `UglifyJs2Filter`: minifies Javascript + * `UglifyJsFilter`: minifies Javascript + * `Yui\CssCompressorFilter`: compresses CSS using the YUI compressor + * `Yui\JsCompressorFilter`: compresses Javascript using the YUI compressor + +Asset Manager +------------- + +An asset manager is provided for organizing assets. + +``` php +set('jquery', new FileAsset('/path/to/jquery.js')); +$am->set('base_css', new GlobAsset('/path/to/css/*')); +``` + +The asset manager can also be used to reference assets to avoid duplication. + +``` php +set('my_plugin', new AssetCollection(array( + new AssetReference($am, 'jquery'), + new FileAsset('/path/to/jquery.plugin.js'), +))); +``` + +Filter Manager +-------------- + +A filter manager is also provided for organizing filters. + +``` php +set('sass', new SassFilter('/path/to/parser/sass')); +$fm->set('yui_css', new Yui\CssCompressorFilter('/path/to/yuicompressor.jar')); +``` + +Asset Factory +------------- + +If you'd rather not create all these objects by hand, you can use the asset +factory, which will do most of the work for you. + +``` php +setAssetManager($am); +$factory->setFilterManager($fm); +$factory->setDebug(true); + +$css = $factory->createAsset(array( + '@reset', // load the asset manager's "reset" asset + 'css/src/*.scss', // load every scss files from "/path/to/asset/directory/css/src/" +), array( + 'scss', // filter through the filter manager's "scss" filter + '?yui_css', // don't use this filter in debug mode +)); + +echo $css->dump(); +``` + +Prefixing a filter name with a question mark, as `yui_css` is here, will cause +that filter to be omitted when the factory is in debug mode. + +Caching +------- + +A simple caching mechanism is provided to avoid unnecessary work. + +``` php +dump(); +$js->dump(); +$js->dump(); +``` + +Cache Busting +------------- + +You can use the CacheBustingWorker to provide unique names. + +Two strategies are provided: CacheBustingWorker::STRATEGY_CONTENT (content based), CacheBustingWorker::STRATEGY_MODIFICATION (modification time based) + +``` php +setAssetManager($am); +$factory->setFilterManager($fm); +$factory->setDebug(true); +$factory->addWorker(new CacheBustingWorker(CacheBustingWorker::STRATEGY_CONTENT)); + +$css = $factory->createAsset(array( + '@reset', // load the asset manager's "reset" asset + 'css/src/*.scss', // load every scss files from "/path/to/asset/directory/css/src/" +), array( + 'scss', // filter through the filter manager's "scss" filter + '?yui_css', // don't use this filter in debug mode +)); + +echo $css->dump(); +``` + +Static Assets +------------- + +Alternatively you can just write filtered assets to your web directory and be +done with it. + +``` php +writeManagerAssets($am); +``` + +Twig +---- + +To use the Assetic [Twig][3] extension you must register it to your Twig +environment: + +``` php +addExtension(new AsseticExtension($factory, $debug)); +``` + +Once in place, the extension exposes a stylesheets and a javascripts tag with a syntax similar +to what the asset factory uses: + +``` html+jinja +{% stylesheets '/path/to/sass/main.sass' filter='sass,?yui_css' output='css/all.css' %} + +{% endstylesheets %} +``` + +This example will render one `link` element on the page that includes a URL +where the filtered asset can be found. + +When the extension is in debug mode, this same tag will render multiple `link` +elements, one for each asset referenced by the `css/src/*.sass` glob. The +specified filters will still be applied, unless they are marked as optional +using the `?` prefix. + +This behavior can also be triggered by setting a `debug` attribute on the tag: + +``` html+jinja +{% stylesheets 'css/*' debug=true %} ... {% stylesheets %} +``` + +These assets need to be written to the web directory so these URLs don't +return 404 errors. + +``` php +setLoader('twig', new TwigFormulaLoader($twig)); + +// loop through all your templates +foreach ($templates as $template) { + $resource = new TwigResource($twigLoader, $template); + $am->addResource($resource, 'twig'); +} + +$writer = new AssetWriter('/path/to/web'); +$writer->writeManagerAssets($am); +``` + +--- + +Assetic is based on the Python [webassets][1] library (available on +[GitHub][2]). + +[1]: http://elsdoerfer.name/docs/webassets +[2]: https://github.com/miracle2k/webassets +[3]: http://twig.sensiolabs.org diff --git a/vendor/kriswallsmith/assetic/composer.json b/vendor/kriswallsmith/assetic/composer.json new file mode 100644 index 0000000..a9e2091 --- /dev/null +++ b/vendor/kriswallsmith/assetic/composer.json @@ -0,0 +1,81 @@ +{ + "name": "kriswallsmith/assetic", + "minimum-stability": "dev", + "description": "Asset Management for PHP", + "keywords": [ "assets", "compression", "minification" ], + "homepage": "https://github.com/kriswallsmith/assetic", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "require": { + "php": ">=5.3.1", + "symfony/process": "~2.1" + }, + "require-dev": { + "phpunit/phpunit": "~3.7", + "twig/twig": "~1.6", + "leafo/lessphp": "*", + "leafo/scssphp": "*", + "ptachoire/cssembed": "*", + "leafo/scssphp-compass": "*", + + "cssmin/cssmin": "*", + "mrclay/minify": "*", + "kamicane/packager": "*", + "joliclic/javascript-packer": "*" + }, + "suggest": { + "twig/twig": "Assetic provides the integration with the Twig templating engine", + "leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler", + "leafo/scssphp": "Assetic provides the integration with the scssphp SCSS compiler", + "ptachoire/cssembed": "Assetic provides the integration with phpcssembed to embed data uris", + "leafo/scssphp-compass": "Assetic provides the integration with the SCSS compass plugin" + }, + "autoload": { + "psr-0": { "Assetic": "src/" }, + "files": [ "src/functions.php" ] + }, + "config": { + "bin-dir": "bin" + }, + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "repositories": [ + { + "type": "package", + "package": { + "name": "cssmin/cssmin", + "version": "3.0.1", + "dist": { "url": "http://cssmin.googlecode.com/files/cssmin-v3.0.1.php", "type": "file" }, + "autoload": { "classmap": [ "cssmin-v3.0.1.php" ] } + } + }, + { + "type": "package", + "package": { + "name": "kamicane/packager", + "version": "1.0", + "dist": { "url": "https://github.com/kamicane/packager/archive/1.0.zip", "type": "zip" }, + "autoload": { "classmap": [ "." ] } + } + }, + { + "type": "package", + "package": { + "name": "joliclic/javascript-packer", + "version": "1.1", + "dist": { "url": "http://joliclic.free.fr/php/javascript-packer/telechargement.php?id=2&action=telecharger", "type": "zip" }, + "autoload": { "classmap": [ "class.JavaScriptPacker.php" ] } + } + } + ] +} diff --git a/vendor/kriswallsmith/assetic/package.json b/vendor/kriswallsmith/assetic/package.json new file mode 100644 index 0000000..c5f534f --- /dev/null +++ b/vendor/kriswallsmith/assetic/package.json @@ -0,0 +1,12 @@ +{ + "devDependencies": { + "uglifycss": "*", + "coffee-script": "*", + "stylus": "*", + "ember-precompile": "*", + "typescript": "*", + "less": "*", + "handlebars": "*", + "uglify-js": "*" + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCache.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCache.php new file mode 100644 index 0000000..0179390 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCache.php @@ -0,0 +1,169 @@ + + */ +class AssetCache implements AssetInterface +{ + private $asset; + private $cache; + + public function __construct(AssetInterface $asset, CacheInterface $cache) + { + $this->asset = $asset; + $this->cache = $cache; + } + + public function ensureFilter(FilterInterface $filter) + { + $this->asset->ensureFilter($filter); + } + + public function getFilters() + { + return $this->asset->getFilters(); + } + + public function clearFilters() + { + $this->asset->clearFilters(); + } + + public function load(FilterInterface $additionalFilter = null) + { + $cacheKey = self::getCacheKey($this->asset, $additionalFilter, 'load'); + if ($this->cache->has($cacheKey)) { + $this->asset->setContent($this->cache->get($cacheKey)); + + return; + } + + $this->asset->load($additionalFilter); + $this->cache->set($cacheKey, $this->asset->getContent()); + } + + public function dump(FilterInterface $additionalFilter = null) + { + $cacheKey = self::getCacheKey($this->asset, $additionalFilter, 'dump'); + if ($this->cache->has($cacheKey)) { + return $this->cache->get($cacheKey); + } + + $content = $this->asset->dump($additionalFilter); + $this->cache->set($cacheKey, $content); + + return $content; + } + + public function getContent() + { + return $this->asset->getContent(); + } + + public function setContent($content) + { + $this->asset->setContent($content); + } + + public function getSourceRoot() + { + return $this->asset->getSourceRoot(); + } + + public function getSourcePath() + { + return $this->asset->getSourcePath(); + } + + public function getTargetPath() + { + return $this->asset->getTargetPath(); + } + + public function setTargetPath($targetPath) + { + $this->asset->setTargetPath($targetPath); + } + + public function getLastModified() + { + return $this->asset->getLastModified(); + } + + public function getVars() + { + return $this->asset->getVars(); + } + + public function setValues(array $values) + { + $this->asset->setValues($values); + } + + public function getValues() + { + return $this->asset->getValues(); + } + + /** + * Returns a cache key for the current asset. + * + * The key is composed of everything but an asset's content: + * + * * source root + * * source path + * * target url + * * last modified + * * filters + * + * @param AssetInterface $asset The asset + * @param FilterInterface $additionalFilter Any additional filter being applied + * @param string $salt Salt for the key + * + * @return string A key for identifying the current asset + */ + private static function getCacheKey(AssetInterface $asset, FilterInterface $additionalFilter = null, $salt = '') + { + if ($additionalFilter) { + $asset = clone $asset; + $asset->ensureFilter($additionalFilter); + } + + $cacheKey = $asset->getSourceRoot(); + $cacheKey .= $asset->getSourcePath(); + $cacheKey .= $asset->getTargetPath(); + $cacheKey .= $asset->getLastModified(); + + foreach ($asset->getFilters() as $filter) { + if ($filter instanceof HashableInterface) { + $cacheKey .= $filter->hash(); + } else { + $cacheKey .= serialize($filter); + } + } + + if ($values = $asset->getValues()) { + asort($values); + $cacheKey .= serialize($values); + } + + return md5($cacheKey.$salt); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollection.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollection.php new file mode 100644 index 0000000..6cfa3e8 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollection.php @@ -0,0 +1,233 @@ + + */ +class AssetCollection implements \IteratorAggregate, AssetCollectionInterface +{ + private $assets; + private $filters; + private $sourceRoot; + private $targetPath; + private $content; + private $clones; + private $vars; + private $values; + + /** + * Constructor. + * + * @param array $assets Assets for the current collection + * @param array $filters Filters for the current collection + * @param string $sourceRoot The root directory + * @param array $vars + */ + public function __construct($assets = array(), $filters = array(), $sourceRoot = null, array $vars = array()) + { + $this->assets = array(); + foreach ($assets as $asset) { + $this->add($asset); + } + + $this->filters = new FilterCollection($filters); + $this->sourceRoot = $sourceRoot; + $this->clones = new \SplObjectStorage(); + $this->vars = $vars; + $this->values = array(); + } + + public function __clone() + { + $this->filters = clone $this->filters; + $this->clones = new \SplObjectStorage(); + } + + public function all() + { + return $this->assets; + } + + public function add(AssetInterface $asset) + { + $this->assets[] = $asset; + } + + public function removeLeaf(AssetInterface $needle, $graceful = false) + { + foreach ($this->assets as $i => $asset) { + $clone = isset($this->clones[$asset]) ? $this->clones[$asset] : null; + if (in_array($needle, array($asset, $clone), true)) { + unset($this->clones[$asset], $this->assets[$i]); + + return true; + } + + if ($asset instanceof AssetCollectionInterface && $asset->removeLeaf($needle, true)) { + return true; + } + } + + if ($graceful) { + return false; + } + + throw new \InvalidArgumentException('Leaf not found.'); + } + + public function replaceLeaf(AssetInterface $needle, AssetInterface $replacement, $graceful = false) + { + foreach ($this->assets as $i => $asset) { + $clone = isset($this->clones[$asset]) ? $this->clones[$asset] : null; + if (in_array($needle, array($asset, $clone), true)) { + unset($this->clones[$asset]); + $this->assets[$i] = $replacement; + + return true; + } + + if ($asset instanceof AssetCollectionInterface && $asset->replaceLeaf($needle, $replacement, true)) { + return true; + } + } + + if ($graceful) { + return false; + } + + throw new \InvalidArgumentException('Leaf not found.'); + } + + public function ensureFilter(FilterInterface $filter) + { + $this->filters->ensure($filter); + } + + public function getFilters() + { + return $this->filters->all(); + } + + public function clearFilters() + { + $this->filters->clear(); + } + + public function load(FilterInterface $additionalFilter = null) + { + // loop through leaves and load each asset + $parts = array(); + foreach ($this as $asset) { + $asset->load($additionalFilter); + $parts[] = $asset->getContent(); + } + + $this->content = implode("\n", $parts); + } + + public function dump(FilterInterface $additionalFilter = null) + { + // loop through leaves and dump each asset + $parts = array(); + foreach ($this as $asset) { + $parts[] = $asset->dump($additionalFilter); + } + + return implode("\n", $parts); + } + + public function getContent() + { + return $this->content; + } + + public function setContent($content) + { + $this->content = $content; + } + + public function getSourceRoot() + { + return $this->sourceRoot; + } + + public function getSourcePath() + { + } + + public function getTargetPath() + { + return $this->targetPath; + } + + public function setTargetPath($targetPath) + { + $this->targetPath = $targetPath; + } + + /** + * Returns the highest last-modified value of all assets in the current collection. + * + * @return integer|null A UNIX timestamp + */ + public function getLastModified() + { + if (!count($this->assets)) { + return; + } + + $mtime = 0; + foreach ($this as $asset) { + $assetMtime = $asset->getLastModified(); + if ($assetMtime > $mtime) { + $mtime = $assetMtime; + } + } + + return $mtime; + } + + /** + * Returns an iterator for looping recursively over unique leaves. + */ + public function getIterator() + { + return new \RecursiveIteratorIterator(new AssetCollectionFilterIterator(new AssetCollectionIterator($this, $this->clones))); + } + + public function getVars() + { + return $this->vars; + } + + public function setValues(array $values) + { + $this->values = $values; + + foreach ($this as $asset) { + $asset->setValues(array_intersect_key($values, array_flip($asset->getVars()))); + } + } + + public function getValues() + { + return $this->values; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollectionInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollectionInterface.php new file mode 100644 index 0000000..8a7927e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollectionInterface.php @@ -0,0 +1,59 @@ + + */ +interface AssetCollectionInterface extends AssetInterface, \Traversable +{ + /** + * Returns all child assets. + * + * @return array An array of AssetInterface objects + */ + public function all(); + + /** + * Adds an asset to the current collection. + * + * @param AssetInterface $asset An asset + */ + public function add(AssetInterface $asset); + + /** + * Removes a leaf. + * + * @param AssetInterface $leaf The leaf to remove + * @param Boolean $graceful Whether the failure should return false or throw an exception + * + * @return Boolean Whether the asset has been found + * + * @throws \InvalidArgumentException If the asset cannot be found + */ + public function removeLeaf(AssetInterface $leaf, $graceful = false); + + /** + * Replaces an existing leaf with a new one. + * + * @param AssetInterface $needle The current asset to replace + * @param AssetInterface $replacement The new asset + * @param Boolean $graceful Whether the failure should return false or throw an exception + * + * @return Boolean Whether the asset has been found + * + * @throws \InvalidArgumentException If the asset cannot be found + */ + public function replaceLeaf(AssetInterface $needle, AssetInterface $replacement, $graceful = false); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetInterface.php new file mode 100644 index 0000000..0f36aea --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetInterface.php @@ -0,0 +1,156 @@ + + */ +interface AssetInterface +{ + /** + * Ensures the current asset includes the supplied filter. + * + * @param FilterInterface $filter A filter + */ + public function ensureFilter(FilterInterface $filter); + + /** + * Returns an array of filters currently applied. + * + * @return array An array of filters + */ + public function getFilters(); + + /** + * Clears all filters from the current asset. + */ + public function clearFilters(); + + /** + * Loads the asset into memory and applies load filters. + * + * You may provide an additional filter to apply during load. + * + * @param FilterInterface $additionalFilter An additional filter + */ + public function load(FilterInterface $additionalFilter = null); + + /** + * Applies dump filters and returns the asset as a string. + * + * You may provide an additional filter to apply during dump. + * + * Dumping an asset should not change its state. + * + * If the current asset has not been loaded yet, it should be + * automatically loaded at this time. + * + * @param FilterInterface $additionalFilter An additional filter + * + * @return string The filtered content of the current asset + */ + public function dump(FilterInterface $additionalFilter = null); + + /** + * Returns the loaded content of the current asset. + * + * @return string The content + */ + public function getContent(); + + /** + * Sets the content of the current asset. + * + * Filters can use this method to change the content of the asset. + * + * @param string $content The asset content + */ + public function setContent($content); + + /** + * Returns an absolute path or URL to the source asset's root directory. + * + * This value should be an absolute path to a directory in the filesystem, + * an absolute URL with no path, or null. + * + * For example: + * + * * '/path/to/web' + * * 'http://example.com' + * * null + * + * @return string|null The asset's root + */ + public function getSourceRoot(); + + /** + * Returns the relative path for the source asset. + * + * This value can be combined with the asset's source root (if both are + * non-null) to get something compatible with file_get_contents(). + * + * For example: + * + * * 'js/main.js' + * * 'main.js' + * * null + * + * @return string|null The source asset path + */ + public function getSourcePath(); + + /** + * Returns the URL for the current asset. + * + * @return string|null A web URL where the asset will be dumped + */ + public function getTargetPath(); + + /** + * Sets the URL for the current asset. + * + * @param string $targetPath A web URL where the asset will be dumped + */ + public function setTargetPath($targetPath); + + /** + * Returns the time the current asset was last modified. + * + * @return integer|null A UNIX timestamp + */ + public function getLastModified(); + + /** + * Returns an array of variable names for this asset. + * + * @return array + */ + public function getVars(); + + /** + * Sets the values for the asset's variables. + * + * @param array $values + */ + public function setValues(array $values); + + /** + * Returns the current values for this asset. + * + * @return array an array of strings + */ + public function getValues(); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetReference.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetReference.php new file mode 100644 index 0000000..57c3930 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetReference.php @@ -0,0 +1,133 @@ + + */ +class AssetReference implements AssetInterface +{ + private $am; + private $name; + private $filters = array(); + + public function __construct(AssetManager $am, $name) + { + $this->am = $am; + $this->name = $name; + } + + public function ensureFilter(FilterInterface $filter) + { + $this->filters[] = $filter; + } + + public function getFilters() + { + $this->flushFilters(); + + return $this->callAsset(__FUNCTION__); + } + + public function clearFilters() + { + $this->filters = array(); + $this->callAsset(__FUNCTION__); + } + + public function load(FilterInterface $additionalFilter = null) + { + $this->flushFilters(); + + return $this->callAsset(__FUNCTION__, array($additionalFilter)); + } + + public function dump(FilterInterface $additionalFilter = null) + { + $this->flushFilters(); + + return $this->callAsset(__FUNCTION__, array($additionalFilter)); + } + + public function getContent() + { + return $this->callAsset(__FUNCTION__); + } + + public function setContent($content) + { + $this->callAsset(__FUNCTION__, array($content)); + } + + public function getSourceRoot() + { + return $this->callAsset(__FUNCTION__); + } + + public function getSourcePath() + { + return $this->callAsset(__FUNCTION__); + } + + public function getTargetPath() + { + return $this->callAsset(__FUNCTION__); + } + + public function setTargetPath($targetPath) + { + $this->callAsset(__FUNCTION__, array($targetPath)); + } + + public function getLastModified() + { + return $this->callAsset(__FUNCTION__); + } + + public function getVars() + { + return $this->callAsset(__FUNCTION__); + } + + public function getValues() + { + return $this->callAsset(__FUNCTION__); + } + + public function setValues(array $values) + { + $this->callAsset(__FUNCTION__, array($values)); + } + + // private + + private function callAsset($method, $arguments = array()) + { + $asset = $this->am->get($this->name); + + return call_user_func_array(array($asset, $method), $arguments); + } + + private function flushFilters() + { + $asset = $this->am->get($this->name); + + while ($filter = array_shift($this->filters)) { + $asset->ensureFilter($filter); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/BaseAsset.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/BaseAsset.php new file mode 100644 index 0000000..12b0340 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/BaseAsset.php @@ -0,0 +1,172 @@ + + */ +abstract class BaseAsset implements AssetInterface +{ + private $filters; + private $sourceRoot; + private $sourcePath; + private $targetPath; + private $content; + private $loaded; + private $vars; + private $values; + + /** + * Constructor. + * + * @param array $filters Filters for the asset + * @param string $sourceRoot The root directory + * @param string $sourcePath The asset path + * @param array $vars + */ + public function __construct($filters = array(), $sourceRoot = null, $sourcePath = null, array $vars = array()) + { + $this->filters = new FilterCollection($filters); + $this->sourceRoot = $sourceRoot; + $this->sourcePath = $sourcePath; + $this->vars = $vars; + $this->values = array(); + $this->loaded = false; + } + + public function __clone() + { + $this->filters = clone $this->filters; + } + + public function ensureFilter(FilterInterface $filter) + { + $this->filters->ensure($filter); + } + + public function getFilters() + { + return $this->filters->all(); + } + + public function clearFilters() + { + $this->filters->clear(); + } + + /** + * Encapsulates asset loading logic. + * + * @param string $content The asset content + * @param FilterInterface $additionalFilter An additional filter + */ + protected function doLoad($content, FilterInterface $additionalFilter = null) + { + $filter = clone $this->filters; + if ($additionalFilter) { + $filter->ensure($additionalFilter); + } + + $asset = clone $this; + $asset->setContent($content); + + $filter->filterLoad($asset); + $this->content = $asset->getContent(); + + $this->loaded = true; + } + + public function dump(FilterInterface $additionalFilter = null) + { + if (!$this->loaded) { + $this->load(); + } + + $filter = clone $this->filters; + if ($additionalFilter) { + $filter->ensure($additionalFilter); + } + + $asset = clone $this; + $filter->filterDump($asset); + + return $asset->getContent(); + } + + public function getContent() + { + return $this->content; + } + + public function setContent($content) + { + $this->content = $content; + } + + public function getSourceRoot() + { + return $this->sourceRoot; + } + + public function getSourcePath() + { + return $this->sourcePath; + } + + public function getTargetPath() + { + return $this->targetPath; + } + + public function setTargetPath($targetPath) + { + if ($this->vars) { + foreach ($this->vars as $var) { + if (false === strpos($targetPath, $var)) { + throw new \RuntimeException(sprintf('The asset target path "%s" must contain the variable "{%s}".', $targetPath, $var)); + } + } + } + + $this->targetPath = $targetPath; + } + + public function getVars() + { + return $this->vars; + } + + public function setValues(array $values) + { + foreach ($values as $var => $v) { + if (!in_array($var, $this->vars, true)) { + throw new \InvalidArgumentException(sprintf('The asset with source path "%s" has no variable named "%s".', $this->sourcePath, $var)); + } + } + + $this->values = $values; + $this->loaded = false; + } + + public function getValues() + { + return $this->values; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/FileAsset.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/FileAsset.php new file mode 100644 index 0000000..7a7e113 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/FileAsset.php @@ -0,0 +1,78 @@ + + */ +class FileAsset extends BaseAsset +{ + private $source; + + /** + * Constructor. + * + * @param string $source An absolute path + * @param array $filters An array of filters + * @param string $sourceRoot The source asset root directory + * @param string $sourcePath The source asset path + * @param array $vars + * + * @throws \InvalidArgumentException If the supplied root doesn't match the source when guessing the path + */ + public function __construct($source, $filters = array(), $sourceRoot = null, $sourcePath = null, array $vars = array()) + { + if (null === $sourceRoot) { + $sourceRoot = dirname($source); + if (null === $sourcePath) { + $sourcePath = basename($source); + } + } elseif (null === $sourcePath) { + if (0 !== strpos($source, $sourceRoot)) { + throw new \InvalidArgumentException(sprintf('The source "%s" is not in the root directory "%s"', $source, $sourceRoot)); + } + + $sourcePath = substr($source, strlen($sourceRoot) + 1); + } + + $this->source = $source; + + parent::__construct($filters, $sourceRoot, $sourcePath, $vars); + } + + public function load(FilterInterface $additionalFilter = null) + { + $source = VarUtils::resolve($this->source, $this->getVars(), $this->getValues()); + + if (!is_file($source)) { + throw new \RuntimeException(sprintf('The source file "%s" does not exist.', $source)); + } + + $this->doLoad(file_get_contents($source), $additionalFilter); + } + + public function getLastModified() + { + $source = VarUtils::resolve($this->source, $this->getVars(), $this->getValues()); + + if (!is_file($source)) { + throw new \RuntimeException(sprintf('The source file "%s" does not exist.', $source)); + } + + return filemtime($source); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/GlobAsset.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/GlobAsset.php new file mode 100644 index 0000000..f6c32ab --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/GlobAsset.php @@ -0,0 +1,113 @@ + + */ +class GlobAsset extends AssetCollection +{ + private $globs; + private $initialized; + + /** + * Constructor. + * + * @param string|array $globs A single glob path or array of paths + * @param array $filters An array of filters + * @param string $root The root directory + * @param array $vars + */ + public function __construct($globs, $filters = array(), $root = null, array $vars = array()) + { + $this->globs = (array) $globs; + $this->initialized = false; + + parent::__construct(array(), $filters, $root, $vars); + } + + public function all() + { + if (!$this->initialized) { + $this->initialize(); + } + + return parent::all(); + } + + public function load(FilterInterface $additionalFilter = null) + { + if (!$this->initialized) { + $this->initialize(); + } + + parent::load($additionalFilter); + } + + public function dump(FilterInterface $additionalFilter = null) + { + if (!$this->initialized) { + $this->initialize(); + } + + return parent::dump($additionalFilter); + } + + public function getLastModified() + { + if (!$this->initialized) { + $this->initialize(); + } + + return parent::getLastModified(); + } + + public function getIterator() + { + if (!$this->initialized) { + $this->initialize(); + } + + return parent::getIterator(); + } + + public function setValues(array $values) + { + parent::setValues($values); + $this->initialized = false; + } + + /** + * Initializes the collection based on the glob(s) passed in. + */ + private function initialize() + { + foreach ($this->globs as $glob) { + $glob = VarUtils::resolve($glob, $this->getVars(), $this->getValues()); + + if (false !== $paths = glob($glob)) { + foreach ($paths as $path) { + if (is_file($path)) { + $this->add(new FileAsset($path, array(), $this->getSourceRoot())); + } + } + } + } + + $this->initialized = true; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/HttpAsset.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/HttpAsset.php new file mode 100644 index 0000000..eea2350 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/HttpAsset.php @@ -0,0 +1,79 @@ + + */ +class HttpAsset extends BaseAsset +{ + private $sourceUrl; + private $ignoreErrors; + + /** + * Constructor. + * + * @param string $sourceUrl The source URL + * @param array $filters An array of filters + * @param Boolean $ignoreErrors + * @param array $vars + * + * @throws \InvalidArgumentException If the first argument is not an URL + */ + public function __construct($sourceUrl, $filters = array(), $ignoreErrors = false, array $vars = array()) + { + if (0 === strpos($sourceUrl, '//')) { + $sourceUrl = 'http:'.$sourceUrl; + } elseif (false === strpos($sourceUrl, '://')) { + throw new \InvalidArgumentException(sprintf('"%s" is not a valid URL.', $sourceUrl)); + } + + $this->sourceUrl = $sourceUrl; + $this->ignoreErrors = $ignoreErrors; + + list($scheme, $url) = explode('://', $sourceUrl, 2); + list($host, $path) = explode('/', $url, 2); + + parent::__construct($filters, $scheme.'://'.$host, $path, $vars); + } + + public function load(FilterInterface $additionalFilter = null) + { + $content = @file_get_contents( + VarUtils::resolve($this->sourceUrl, $this->getVars(), $this->getValues()) + ); + + if (false === $content && !$this->ignoreErrors) { + throw new \RuntimeException(sprintf('Unable to load asset from URL "%s"', $this->sourceUrl)); + } + + $this->doLoad($content, $additionalFilter); + } + + public function getLastModified() + { + if (false !== @file_get_contents($this->sourceUrl, false, stream_context_create(array('http' => array('method' => 'HEAD'))))) { + foreach ($http_response_header as $header) { + if (0 === stripos($header, 'Last-Modified: ')) { + list(, $mtime) = explode(':', $header, 2); + + return strtotime(trim($mtime)); + } + } + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionFilterIterator.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionFilterIterator.php new file mode 100644 index 0000000..de9c169 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionFilterIterator.php @@ -0,0 +1,84 @@ + + */ +class AssetCollectionFilterIterator extends \RecursiveFilterIterator +{ + private $visited; + private $sources; + + /** + * Constructor. + * + * @param AssetCollectionIterator $iterator The inner iterator + * @param array $visited An array of visited asset objects + * @param array $sources An array of visited source strings + */ + public function __construct(AssetCollectionIterator $iterator, array $visited = array(), array $sources = array()) + { + parent::__construct($iterator); + + $this->visited = $visited; + $this->sources = $sources; + } + + /** + * Determines whether the current asset is a duplicate. + * + * De-duplication is performed based on either strict equality or by + * matching sources. + * + * @return Boolean Returns true if we have not seen this asset yet + */ + public function accept() + { + $asset = $this->getInnerIterator()->current(true); + $duplicate = false; + + // check strict equality + if (in_array($asset, $this->visited, true)) { + $duplicate = true; + } else { + $this->visited[] = $asset; + } + + // check source + $sourceRoot = $asset->getSourceRoot(); + $sourcePath = $asset->getSourcePath(); + if ($sourceRoot && $sourcePath) { + $source = $sourceRoot.'/'.$sourcePath; + if (in_array($source, $this->sources)) { + $duplicate = true; + } else { + $this->sources[] = $source; + } + } + + return !$duplicate; + } + + /** + * Passes visited objects and source URLs to the child iterator. + */ + public function getChildren() + { + return new self($this->getInnerIterator()->getChildren(), $this->visited, $this->sources); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionIterator.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionIterator.php new file mode 100644 index 0000000..134b0a8 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionIterator.php @@ -0,0 +1,110 @@ + + */ +class AssetCollectionIterator implements \RecursiveIterator +{ + private $assets; + private $filters; + private $output; + private $clones; + + public function __construct(AssetCollectionInterface $coll, \SplObjectStorage $clones) + { + $this->assets = $coll->all(); + $this->filters = $coll->getFilters(); + $this->output = $coll->getTargetPath(); + $this->clones = $clones; + + if (false === $pos = strrpos($this->output, '.')) { + $this->output .= '_*'; + } else { + $this->output = substr($this->output, 0, $pos).'_*'.substr($this->output, $pos); + } + } + + /** + * Returns a copy of the current asset with filters and a target URL applied. + * + * @param Boolean $raw Returns the unmodified asset if true + * @return \Assetic\Asset\AssetInterface + */ + public function current($raw = false) + { + $asset = current($this->assets); + + if ($raw) { + return $asset; + } + + // clone once + if (!isset($this->clones[$asset])) { + $clone = $this->clones[$asset] = clone $asset; + + // generate a target path based on asset name + $name = sprintf('%s_%d', pathinfo($asset->getSourcePath(), PATHINFO_FILENAME) ?: 'part', $this->key() + 1); + $clone->setTargetPath(str_replace('*', $name, $this->output)); + } else { + $clone = $this->clones[$asset]; + } + + // cascade filters + foreach ($this->filters as $filter) { + $clone->ensureFilter($filter); + } + + return $clone; + } + + public function key() + { + return key($this->assets); + } + + public function next() + { + return next($this->assets); + } + + public function rewind() + { + return reset($this->assets); + } + + public function valid() + { + return false !== current($this->assets); + } + + public function hasChildren() + { + return current($this->assets) instanceof AssetCollectionInterface; + } + + /** + * @uses current() + */ + public function getChildren() + { + return new self($this->current(), $this->clones); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/StringAsset.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/StringAsset.php new file mode 100644 index 0000000..7222fe5 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/StringAsset.php @@ -0,0 +1,55 @@ + + */ +class StringAsset extends BaseAsset +{ + private $content; + private $lastModified; + + /** + * Constructor. + * + * @param string $content The content of the asset + * @param array $filters Filters for the asset + * @param string $sourceRoot The source asset root directory + * @param string $sourcePath The source asset path + */ + public function __construct($content, $filters = array(), $sourceRoot = null, $sourcePath = null) + { + $this->content = $content; + + parent::__construct($filters, $sourceRoot, $sourcePath); + } + + public function load(FilterInterface $additionalFilter = null) + { + $this->doLoad($this->content, $additionalFilter); + } + + public function setLastModified($lastModified) + { + $this->lastModified = $lastModified; + } + + public function getLastModified() + { + return $this->lastModified; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/AssetManager.php b/vendor/kriswallsmith/assetic/src/Assetic/AssetManager.php new file mode 100644 index 0000000..a55cd2e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/AssetManager.php @@ -0,0 +1,89 @@ + + */ +class AssetManager +{ + private $assets = array(); + + /** + * Gets an asset by name. + * + * @param string $name The asset name + * + * @return AssetInterface The asset + * + * @throws \InvalidArgumentException If there is no asset by that name + */ + public function get($name) + { + if (!isset($this->assets[$name])) { + throw new \InvalidArgumentException(sprintf('There is no "%s" asset.', $name)); + } + + return $this->assets[$name]; + } + + /** + * Checks if the current asset manager has a certain asset. + * + * @param string $name an asset name + * + * @return Boolean True if the asset has been set, false if not + */ + public function has($name) + { + return isset($this->assets[$name]); + } + + /** + * Registers an asset to the current asset manager. + * + * @param string $name The asset name + * @param AssetInterface $asset The asset + * + * @throws \InvalidArgumentException If the asset name is invalid + */ + public function set($name, AssetInterface $asset) + { + if (!ctype_alnum(str_replace('_', '', $name))) { + throw new \InvalidArgumentException(sprintf('The name "%s" is invalid.', $name)); + } + + $this->assets[$name] = $asset; + } + + /** + * Returns an array of asset names. + * + * @return array An array of asset names + */ + public function getNames() + { + return array_keys($this->assets); + } + + /** + * Clears all assets. + */ + public function clear() + { + $this->assets = array(); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/AssetWriter.php b/vendor/kriswallsmith/assetic/src/Assetic/AssetWriter.php new file mode 100644 index 0000000..bbace99 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/AssetWriter.php @@ -0,0 +1,94 @@ + + * @author Johannes M. Schmitt + */ +class AssetWriter +{ + private $dir; + private $values; + + /** + * Constructor. + * + * @param string $dir The base web directory + * @param array $values Variable values + * + * @throws \InvalidArgumentException if a variable value is not a string + */ + public function __construct($dir, array $values = array()) + { + foreach ($values as $var => $vals) { + foreach ($vals as $value) { + if (!is_string($value)) { + throw new \InvalidArgumentException(sprintf('All variable values must be strings, but got %s for variable "%s".', json_encode($value), $var)); + } + } + } + + $this->dir = $dir; + $this->values = $values; + } + + public function writeManagerAssets(AssetManager $am) + { + foreach ($am->getNames() as $name) { + $this->writeAsset($am->get($name)); + } + } + + public function writeAsset(AssetInterface $asset) + { + foreach (VarUtils::getCombinations($asset->getVars(), $this->values) as $combination) { + $asset->setValues($combination); + + static::write( + $this->dir.'/'.VarUtils::resolve( + $asset->getTargetPath(), + $asset->getVars(), + $asset->getValues() + ), + $asset->dump() + ); + } + } + + protected static function write($path, $contents) + { + if (!is_dir($dir = dirname($path)) && false === @mkdir($dir, 0777, true)) { + throw new \RuntimeException('Unable to create directory '.$dir); + } + + if (false === @file_put_contents($path, $contents)) { + throw new \RuntimeException('Unable to write file '.$path); + } + } + + /** + * Not used. + * + * This method is provided for backward compatibility with certain versions + * of AsseticBundle. + */ + private function getCombinations(array $vars) + { + return VarUtils::getCombinations($vars, $this->values); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Cache/ApcCache.php b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ApcCache.php new file mode 100644 index 0000000..6a56f39 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ApcCache.php @@ -0,0 +1,66 @@ + + */ +class ApcCache implements CacheInterface +{ + public $ttl = 0; + + /** + * @see CacheInterface::has() + */ + public function has($key) + { + return apc_exists($key); + } + + /** + * @see CacheInterface::get() + */ + public function get($key) + { + $value = apc_fetch($key, $success); + + if (!$success) { + throw new \RuntimeException('There is no cached value for ' . $key); + } + + return $value; + } + + /** + * @see CacheInterface::set() + */ + public function set($key, $value) + { + $store = apc_store($key, $value, $this->ttl); + + if (!$store) { + throw new \RuntimeException('Unable to store "' . $key . '" for ' . $this->ttl . ' seconds.'); + } + + return $store; + } + + /** + * @see CacheInterface::remove() + */ + public function remove($key) + { + return apc_delete($key); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Cache/ArrayCache.php b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ArrayCache.php new file mode 100644 index 0000000..e322cb3 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ArrayCache.php @@ -0,0 +1,58 @@ + + */ +class ArrayCache implements CacheInterface +{ + private $cache = array(); + + /** + * @see CacheInterface::has() + */ + public function has($key) + { + return isset($this->cache[$key]); + } + + /** + * @see CacheInterface::get() + */ + public function get($key) + { + if(!$this->has($key)) { + throw new \RuntimeException('There is no cached value for '.$key); + } + + return $this->cache[$key]; + } + + /** + * @see CacheInterface::set() + */ + public function set($key, $value) + { + $this->cache[$key] = $value; + } + + /** + * @see CacheInterface::remove() + */ + public function remove($key) + { + unset($this->cache[$key]); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Cache/CacheInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Cache/CacheInterface.php new file mode 100644 index 0000000..7f301f3 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Cache/CacheInterface.php @@ -0,0 +1,53 @@ + + */ +interface CacheInterface +{ + /** + * Checks if the cache has a value for a key. + * + * @param string $key A unique key + * + * @return Boolean Whether the cache has a value for this key + */ + public function has($key); + + /** + * Returns the value for a key. + * + * @param string $key A unique key + * + * @return string|null The value in the cache + */ + public function get($key); + + /** + * Sets a value in the cache. + * + * @param string $key A unique key + * @param string $value The value to cache + */ + public function set($key, $value); + + /** + * Removes a value from the cache. + * + * @param string $key A unique key + */ + public function remove($key); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Cache/ConfigCache.php b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ConfigCache.php new file mode 100644 index 0000000..b5ad0c1 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ConfigCache.php @@ -0,0 +1,123 @@ + + */ +class ConfigCache +{ + private $dir; + + /** + * Construct. + * + * @param string $dir The cache directory + */ + public function __construct($dir) + { + $this->dir = $dir; + } + + /** + * Checks of the cache has a file. + * + * @param string $resource A cache key + * + * @return Boolean True if a file exists + */ + public function has($resource) + { + return file_exists($this->getSourcePath($resource)); + } + + /** + * Writes a value to a file. + * + * @param string $resource A cache key + * @param mixed $value A value to cache + */ + public function set($resource, $value) + { + $path = $this->getSourcePath($resource); + + if (!is_dir($dir = dirname($path)) && false === @mkdir($dir, 0777, true)) { + // @codeCoverageIgnoreStart + throw new \RuntimeException('Unable to create directory '.$dir); + // @codeCoverageIgnoreEnd + } + + if (false === @file_put_contents($path, sprintf("getSourcePath($resource); + + if (!file_exists($path)) { + throw new \RuntimeException('There is no cached value for '.$resource); + } + + return include $path; + } + + /** + * Returns a timestamp for when the cache was created. + * + * @param string $resource A cache key + * + * @return integer A UNIX timestamp + */ + public function getTimestamp($resource) + { + $path = $this->getSourcePath($resource); + + if (!file_exists($path)) { + throw new \RuntimeException('There is no cached value for '.$resource); + } + + if (false === $mtime = @filemtime($path)) { + // @codeCoverageIgnoreStart + throw new \RuntimeException('Unable to determine file mtime for '.$path); + // @codeCoverageIgnoreEnd + } + + return $mtime; + } + + /** + * Returns the path where the file corresponding to the supplied cache key can be included from. + * + * @param string $resource A cache key + * + * @return string A file path + */ + private function getSourcePath($resource) + { + $key = md5($resource); + + return $this->dir.'/'.$key[0].'/'.$key.'.php'; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Cache/ExpiringCache.php b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ExpiringCache.php new file mode 100644 index 0000000..74ca1ad --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ExpiringCache.php @@ -0,0 +1,60 @@ + + */ +class ExpiringCache implements CacheInterface +{ + private $cache; + private $lifetime; + + public function __construct(CacheInterface $cache, $lifetime) + { + $this->cache = $cache; + $this->lifetime = $lifetime; + } + + public function has($key) + { + if ($this->cache->has($key)) { + if (time() < $this->cache->get($key.'.expires')) { + return true; + } + + $this->cache->remove($key.'.expires'); + $this->cache->remove($key); + } + + return false; + } + + public function get($key) + { + return $this->cache->get($key); + } + + public function set($key, $value) + { + $this->cache->set($key.'.expires', time() + $this->lifetime); + $this->cache->set($key, $value); + } + + public function remove($key) + { + $this->cache->remove($key.'.expires'); + $this->cache->remove($key); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Cache/FilesystemCache.php b/vendor/kriswallsmith/assetic/src/Assetic/Cache/FilesystemCache.php new file mode 100644 index 0000000..7698aed --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Cache/FilesystemCache.php @@ -0,0 +1,65 @@ + + */ +class FilesystemCache implements CacheInterface +{ + private $dir; + + public function __construct($dir) + { + $this->dir = $dir; + } + + public function has($key) + { + return file_exists($this->dir.'/'.$key); + } + + public function get($key) + { + $path = $this->dir.'/'.$key; + + if (!file_exists($path)) { + throw new \RuntimeException('There is no cached value for '.$key); + } + + return file_get_contents($path); + } + + public function set($key, $value) + { + if (!is_dir($this->dir) && false === @mkdir($this->dir, 0777, true)) { + throw new \RuntimeException('Unable to create directory '.$this->dir); + } + + $path = $this->dir.'/'.$key; + + if (false === @file_put_contents($path, $value)) { + throw new \RuntimeException('Unable to write file '.$path); + } + } + + public function remove($key) + { + $path = $this->dir.'/'.$key; + + if (file_exists($path) && false === @unlink($path)) { + throw new \RuntimeException('Unable to remove file '.$path); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Exception/Exception.php b/vendor/kriswallsmith/assetic/src/Assetic/Exception/Exception.php new file mode 100644 index 0000000..e9e37c7 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Exception/Exception.php @@ -0,0 +1,21 @@ + + */ +interface Exception +{ +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Exception/FilterException.php b/vendor/kriswallsmith/assetic/src/Assetic/Exception/FilterException.php new file mode 100644 index 0000000..ced5449 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Exception/FilterException.php @@ -0,0 +1,73 @@ + + */ +class FilterException extends \RuntimeException implements Exception +{ + private $originalMessage; + private $input; + + public static function fromProcess(Process $proc) + { + $message = sprintf("An error occurred while running:\n%s", $proc->getCommandLine()); + + $errorOutput = $proc->getErrorOutput(); + if (!empty($errorOutput)) { + $message .= "\n\nError Output:\n".str_replace("\r", '', $errorOutput); + } + + $output = $proc->getOutput(); + if (!empty($output)) { + $message .= "\n\nOutput:\n".str_replace("\r", '', $output); + } + + return new self($message); + } + + public function __construct($message, $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->originalMessage = $message; + } + + public function setInput($input) + { + $this->input = $input; + $this->updateMessage(); + + return $this; + } + + public function getInput() + { + return $this->input; + } + + private function updateMessage() + { + $message = $this->originalMessage; + + if (!empty($this->input)) { + $message .= "\n\nInput:\n".$this->input; + } + + $this->message = $message; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticExtension.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticExtension.php new file mode 100644 index 0000000..a63bc9e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticExtension.php @@ -0,0 +1,76 @@ +factory = $factory; + $this->functions = array(); + $this->valueSupplier = $valueSupplier; + + foreach ($functions as $function => $options) { + if (is_integer($function) && is_string($options)) { + $this->functions[$options] = array('filter' => $options); + } else { + $this->functions[$function] = $options + array('filter' => $function); + } + } + } + + public function getTokenParsers() + { + return array( + new AsseticTokenParser($this->factory, 'javascripts', 'js/*.js'), + new AsseticTokenParser($this->factory, 'stylesheets', 'css/*.css'), + new AsseticTokenParser($this->factory, 'image', 'images/*', true), + ); + } + + public function getFunctions() + { + $functions = array(); + foreach ($this->functions as $function => $filter) { + $functions[$function] = new AsseticFilterFunction($function); + } + + return $functions; + } + + public function getGlobals() + { + return array( + 'assetic' => array( + 'debug' => $this->factory->isDebug(), + 'vars' => null !== $this->valueSupplier ? new ValueContainer($this->valueSupplier) : array(), + ), + ); + } + + public function getFilterInvoker($function) + { + return new AsseticFilterInvoker($this->factory, $this->functions[$function]); + } + + public function getName() + { + return 'assetic'; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterFunction.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterFunction.php new file mode 100644 index 0000000..c43aa30 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterFunction.php @@ -0,0 +1,29 @@ +filter = $filter; + + parent::__construct($options); + } + + public function compile() + { + return sprintf('$this->env->getExtension(\'assetic\')->getFilterInvoker(\'%s\')->invoke', $this->filter); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterInvoker.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterInvoker.php new file mode 100644 index 0000000..577e1f6 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterInvoker.php @@ -0,0 +1,59 @@ + + */ +class AsseticFilterInvoker +{ + private $factory; + private $filters; + private $options; + + public function __construct($factory, $filter) + { + $this->factory = $factory; + + if (is_array($filter) && isset($filter['filter'])) { + $this->filters = (array) $filter['filter']; + $this->options = isset($filter['options']) ? (array) $filter['options'] : array(); + } else { + $this->filters = (array) $filter; + $this->options = array(); + } + } + + public function getFactory() + { + return $this->factory; + } + + public function getFilters() + { + return $this->filters; + } + + public function getOptions() + { + return $this->options; + } + + public function invoke($input, array $options = array()) + { + $asset = $this->factory->createAsset($input, $this->filters, $options + $this->options); + + return $asset->getTargetPath(); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticNode.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticNode.php new file mode 100644 index 0000000..0b32e0a --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticNode.php @@ -0,0 +1,166 @@ + $body); + + $attributes = array_replace( + array('debug' => null, 'combine' => null, 'var_name' => 'asset_url'), + $attributes, + array('asset' => $asset, 'inputs' => $inputs, 'filters' => $filters, 'name' => $name) + ); + + parent::__construct($nodes, $attributes, $lineno, $tag); + } + + public function compile(\Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + $combine = $this->getAttribute('combine'); + $debug = $this->getAttribute('debug'); + + if (null === $combine && null !== $debug) { + $combine = !$debug; + } + + if (null === $combine) { + $compiler + ->write("if (isset(\$context['assetic']['debug']) && \$context['assetic']['debug']) {\n") + ->indent() + ; + + $this->compileDebug($compiler); + + $compiler + ->outdent() + ->write("} else {\n") + ->indent() + ; + + $this->compileAsset($compiler, $this->getAttribute('asset'), $this->getAttribute('name')); + + $compiler + ->outdent() + ->write("}\n") + ; + } elseif ($combine) { + $this->compileAsset($compiler, $this->getAttribute('asset'), $this->getAttribute('name')); + } else { + $this->compileDebug($compiler); + } + + $compiler + ->write('unset($context[') + ->repr($this->getAttribute('var_name')) + ->raw("]);\n") + ; + } + + protected function compileDebug(\Twig_Compiler $compiler) + { + $i = 0; + foreach ($this->getAttribute('asset') as $leaf) { + $leafName = $this->getAttribute('name').'_'.$i++; + $this->compileAsset($compiler, $leaf, $leafName); + } + } + + protected function compileAsset(\Twig_Compiler $compiler, AssetInterface $asset, $name) + { + if ($vars = $asset->getVars()) { + $compiler->write("// check variable conditions\n"); + + foreach ($vars as $var) { + $compiler + ->write("if (!isset(\$context['assetic']['vars']['$var'])) {\n") + ->indent() + ->write("throw new \RuntimeException(sprintf('The asset \"".$name."\" expected variable \"".$var."\" to be set, but got only these vars: %s. Did you set-up a value supplier?', isset(\$context['assetic']['vars']) && \$context['assetic']['vars'] ? implode(', ', \$context['assetic']['vars']) : '# none #'));\n") + ->outdent() + ->write("}\n") + ; + } + + $compiler->raw("\n"); + } + + $compiler + ->write("// asset \"$name\"\n") + ->write('$context[') + ->repr($this->getAttribute('var_name')) + ->raw('] = ') + ; + + $this->compileAssetUrl($compiler, $asset, $name); + + $compiler + ->raw(";\n") + ->subcompile($this->getNode('body')) + ; + } + + protected function compileAssetUrl(\Twig_Compiler $compiler, AssetInterface $asset, $name) + { + if (!$vars = $asset->getVars()) { + $compiler->repr($asset->getTargetPath()); + + return; + } + + $compiler + ->raw("strtr(") + ->string($asset->getTargetPath()) + ->raw(", array("); + ; + + $first = true; + foreach ($vars as $var) { + if (!$first) { + $compiler->raw(", "); + } + $first = false; + + $compiler + ->string("{".$var."}") + ->raw(" => \$context['assetic']['vars']['$var']") + ; + } + + $compiler + ->raw("))") + ; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticTokenParser.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticTokenParser.php new file mode 100644 index 0000000..3e5fb93 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticTokenParser.php @@ -0,0 +1,153 @@ +factory = $factory; + $this->tag = $tag; + $this->output = $output; + $this->single = $single; + $this->extensions = $extensions; + } + + public function parse(\Twig_Token $token) + { + $inputs = array(); + $filters = array(); + $name = null; + $attributes = array( + 'output' => $this->output, + 'var_name' => 'asset_url', + 'vars' => array(), + ); + + $stream = $this->parser->getStream(); + while (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) { + if ($stream->test(\Twig_Token::STRING_TYPE)) { + // '@jquery', 'js/src/core/*', 'js/src/extra.js' + $inputs[] = $stream->next()->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'filter')) { + // filter='yui_js' + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $filters = array_merge($filters, array_filter(array_map('trim', explode(',', $stream->expect(\Twig_Token::STRING_TYPE)->getValue())))); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'output')) { + // output='js/packed/*.js' OR output='js/core.js' + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $attributes['output'] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'name')) { + // name='core_js' + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $name = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'as')) { + // as='the_url' + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $attributes['var_name'] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'debug')) { + // debug=true + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $attributes['debug'] = 'true' == $stream->expect(\Twig_Token::NAME_TYPE, array('true', 'false'))->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'combine')) { + // combine=true + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $attributes['combine'] = 'true' == $stream->expect(\Twig_Token::NAME_TYPE, array('true', 'false'))->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'vars')) { + // vars=['locale','browser'] + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $stream->expect(\Twig_Token::PUNCTUATION_TYPE, '['); + + while ($stream->test(\Twig_Token::STRING_TYPE)) { + $attributes['vars'][] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); + + if (!$stream->test(\Twig_Token::PUNCTUATION_TYPE, ',')) { + break; + } + + $stream->next(); + } + + $stream->expect(\Twig_Token::PUNCTUATION_TYPE, ']'); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, $this->extensions)) { + // an arbitrary configured attribute + $key = $stream->next()->getValue(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $attributes[$key] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); + } else { + $token = $stream->getCurrent(); + throw new \Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', \Twig_Token::typeToEnglish($token->getType(), $token->getLine()), $token->getValue()), $token->getLine()); + } + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + $body = $this->parser->subparse(array($this, 'testEndTag'), true); + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + if ($this->single && 1 < count($inputs)) { + $inputs = array_slice($inputs, -1); + } + + if (!$name) { + $name = $this->factory->generateAssetName($inputs, $filters, $attributes); + } + + $asset = $this->factory->createAsset($inputs, $filters, $attributes + array('name' => $name)); + + return $this->createNode($asset, $body, $inputs, $filters, $name, $attributes, $token->getLine(), $this->getTag()); + } + + public function getTag() + { + return $this->tag; + } + + public function testEndTag(\Twig_Token $token) + { + return $token->test(array('end'.$this->getTag())); + } + + protected function createNode(AssetInterface $asset, \Twig_NodeInterface $body, array $inputs, array $filters, $name, array $attributes = array(), $lineno = 0, $tag = null) + { + return new AsseticNode($asset, $body, $inputs, $filters, $name, $attributes, $lineno, $tag); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigFormulaLoader.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigFormulaLoader.php new file mode 100644 index 0000000..ddfe892 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigFormulaLoader.php @@ -0,0 +1,99 @@ + + */ +class TwigFormulaLoader implements FormulaLoaderInterface +{ + private $twig; + + public function __construct(\Twig_Environment $twig) + { + $this->twig = $twig; + } + + public function load(ResourceInterface $resource) + { + try { + $tokens = $this->twig->tokenize($resource->getContent(), (string) $resource); + $nodes = $this->twig->parse($tokens); + } catch (\Exception $e) { + return array(); + } + + return $this->loadNode($nodes); + } + + /** + * Loads assets from the supplied node. + * + * @param \Twig_Node $node + * + * @return array An array of asset formulae indexed by name + */ + private function loadNode(\Twig_Node $node) + { + $formulae = array(); + + if ($node instanceof AsseticNode) { + $formulae[$node->getAttribute('name')] = array( + $node->getAttribute('inputs'), + $node->getAttribute('filters'), + array( + 'output' => $node->getAttribute('asset')->getTargetPath(), + 'name' => $node->getAttribute('name'), + 'debug' => $node->getAttribute('debug'), + 'combine' => $node->getAttribute('combine'), + 'vars' => $node->getAttribute('vars'), + ), + ); + } elseif ($node instanceof \Twig_Node_Expression_Function) { + $name = version_compare(\Twig_Environment::VERSION, '1.2.0-DEV', '<') + ? $node->getNode('name')->getAttribute('name') + : $node->getAttribute('name'); + + if ($this->twig->getFunction($name) instanceof AsseticFilterFunction) { + $arguments = array(); + foreach ($node->getNode('arguments') as $argument) { + $arguments[] = eval('return '.$this->twig->compile($argument).';'); + } + + $invoker = $this->twig->getExtension('assetic')->getFilterInvoker($name); + + $inputs = isset($arguments[0]) ? (array) $arguments[0] : array(); + $filters = $invoker->getFilters(); + $options = array_replace($invoker->getOptions(), isset($arguments[1]) ? $arguments[1] : array()); + + if (!isset($options['name'])) { + $options['name'] = $invoker->getFactory()->generateAssetName($inputs, $filters, $options); + } + + $formulae[$options['name']] = array($inputs, $filters, $options); + } + } + + foreach ($node as $child) { + if ($child instanceof \Twig_Node) { + $formulae += $this->loadNode($child); + } + } + + return $formulae; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigResource.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigResource.php new file mode 100644 index 0000000..7a07164 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigResource.php @@ -0,0 +1,54 @@ + + */ +class TwigResource implements ResourceInterface +{ + private $loader; + private $name; + + public function __construct(\Twig_LoaderInterface $loader, $name) + { + $this->loader = $loader; + $this->name = $name; + } + + public function getContent() + { + try { + return $this->loader->getSource($this->name); + } catch (\Twig_Error_Loader $e) { + return ''; + } + } + + public function isFresh($timestamp) + { + try { + return $this->loader->isFresh($this->name, $timestamp); + } catch (\Twig_Error_Loader $e) { + return false; + } + } + + public function __toString() + { + return $this->name; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/ValueContainer.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/ValueContainer.php new file mode 100644 index 0000000..f959c33 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/ValueContainer.php @@ -0,0 +1,79 @@ + + */ +class ValueContainer implements \ArrayAccess, \IteratorAggregate, \Countable +{ + private $values; + private $valueSupplier; + + public function __construct(ValueSupplierInterface $valueSupplier) + { + $this->valueSupplier = $valueSupplier; + } + + public function offsetExists($offset) + { + $this->initialize(); + + return array_key_exists($offset, $this->values); + } + + public function offsetGet($offset) + { + $this->initialize(); + + if (!array_key_exists($offset, $this->values)) { + throw new \OutOfRangeException(sprintf('The variable "%s" does not exist.', $offset)); + } + + return $this->values[$offset]; + } + + public function offsetSet($offset, $value) + { + throw new \BadMethodCallException('The ValueContainer is read-only.'); + } + + public function offsetUnset($offset) + { + throw new \BadMethodCallException('The ValueContainer is read-only.'); + } + + public function getIterator() + { + $this->initialize(); + + return new \ArrayIterator($this->values); + } + + public function count() + { + $this->initialize(); + + return count($this->values); + } + + private function initialize() + { + if (null === $this->values) { + $this->values = $this->valueSupplier->getValues(); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php new file mode 100644 index 0000000..9e90f5d --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php @@ -0,0 +1,388 @@ + + */ +class AssetFactory +{ + private $root; + private $debug; + private $output; + private $workers; + private $am; + private $fm; + + /** + * Constructor. + * + * @param string $root The default root directory + * @param Boolean $debug Filters prefixed with a "?" will be omitted in debug mode + */ + public function __construct($root, $debug = false) + { + $this->root = rtrim($root, '/'); + $this->debug = $debug; + $this->output = 'assetic/*'; + $this->workers = array(); + } + + /** + * Sets debug mode for the current factory. + * + * @param Boolean $debug Debug mode + */ + public function setDebug($debug) + { + $this->debug = $debug; + } + + /** + * Checks if the factory is in debug mode. + * + * @return Boolean Debug mode + */ + public function isDebug() + { + return $this->debug; + } + + /** + * Sets the default output string. + * + * @param string $output The default output string + */ + public function setDefaultOutput($output) + { + $this->output = $output; + } + + /** + * Adds a factory worker. + * + * @param WorkerInterface $worker A worker + */ + public function addWorker(WorkerInterface $worker) + { + $this->workers[] = $worker; + } + + /** + * Returns the current asset manager. + * + * @return AssetManager|null The asset manager + */ + public function getAssetManager() + { + return $this->am; + } + + /** + * Sets the asset manager to use when creating asset references. + * + * @param AssetManager $am The asset manager + */ + public function setAssetManager(AssetManager $am) + { + $this->am = $am; + } + + /** + * Returns the current filter manager. + * + * @return FilterManager|null The filter manager + */ + public function getFilterManager() + { + return $this->fm; + } + + /** + * Sets the filter manager to use when adding filters. + * + * @param FilterManager $fm The filter manager + */ + public function setFilterManager(FilterManager $fm) + { + $this->fm = $fm; + } + + /** + * Creates a new asset. + * + * Prefixing a filter name with a question mark will cause it to be + * omitted when the factory is in debug mode. + * + * Available options: + * + * * output: An output string + * * name: An asset name for interpolation in output patterns + * * debug: Forces debug mode on or off for this asset + * * root: An array or string of more root directories + * + * @param array|string $inputs An array of input strings + * @param array|string $filters An array of filter names + * @param array $options An array of options + * + * @return AssetCollection An asset collection + */ + public function createAsset($inputs = array(), $filters = array(), array $options = array()) + { + if (!is_array($inputs)) { + $inputs = array($inputs); + } + + if (!is_array($filters)) { + $filters = array($filters); + } + + if (!isset($options['output'])) { + $options['output'] = $this->output; + } + + if (!isset($options['vars'])) { + $options['vars'] = array(); + } + + if (!isset($options['debug'])) { + $options['debug'] = $this->debug; + } + + if (!isset($options['root'])) { + $options['root'] = array($this->root); + } else { + if (!is_array($options['root'])) { + $options['root'] = array($options['root']); + } + + $options['root'][] = $this->root; + } + + if (!isset($options['name'])) { + $options['name'] = $this->generateAssetName($inputs, $filters, $options); + } + + $asset = $this->createAssetCollection(array(), $options); + $extensions = array(); + + // inner assets + foreach ($inputs as $input) { + if (is_array($input)) { + // nested formula + $asset->add(call_user_func_array(array($this, 'createAsset'), $input)); + } else { + $asset->add($this->parseInput($input, $options)); + $extensions[pathinfo($input, PATHINFO_EXTENSION)] = true; + } + } + + // filters + foreach ($filters as $filter) { + if ('?' != $filter[0]) { + $asset->ensureFilter($this->getFilter($filter)); + } elseif (!$options['debug']) { + $asset->ensureFilter($this->getFilter(substr($filter, 1))); + } + } + + // append variables + if (!empty($options['vars'])) { + $toAdd = array(); + foreach ($options['vars'] as $var) { + if (false !== strpos($options['output'], '{'.$var.'}')) { + continue; + } + + $toAdd[] = '{'.$var.'}'; + } + + if ($toAdd) { + $options['output'] = str_replace('*', '*.'.implode('.', $toAdd), $options['output']); + } + } + + // append consensus extension if missing + if (1 == count($extensions) && !pathinfo($options['output'], PATHINFO_EXTENSION) && $extension = key($extensions)) { + $options['output'] .= '.'.$extension; + } + + // output --> target url + $asset->setTargetPath(str_replace('*', $options['name'], $options['output'])); + + // apply workers and return + return $this->applyWorkers($asset); + } + + public function generateAssetName($inputs, $filters, $options = array()) + { + foreach (array_diff(array_keys($options), array('output', 'debug', 'root')) as $key) { + unset($options[$key]); + } + + ksort($options); + + return substr(sha1(serialize($inputs).serialize($filters).serialize($options)), 0, 7); + } + + /** + * Parses an input string string into an asset. + * + * The input string can be one of the following: + * + * * A reference: If the string starts with an "at" sign it will be interpreted as a reference to an asset in the asset manager + * * An absolute URL: If the string contains "://" or starts with "//" it will be interpreted as an HTTP asset + * * A glob: If the string contains a "*" it will be interpreted as a glob + * * A path: Otherwise the string is interpreted as a filesystem path + * + * Both globs and paths will be absolutized using the current root directory. + * + * @param string $input An input string + * @param array $options An array of options + * + * @return AssetInterface An asset + */ + protected function parseInput($input, array $options = array()) + { + if ('@' == $input[0]) { + return $this->createAssetReference(substr($input, 1)); + } + + if (false !== strpos($input, '://') || 0 === strpos($input, '//')) { + return $this->createHttpAsset($input, $options['vars']); + } + + if (self::isAbsolutePath($input)) { + if ($root = self::findRootDir($input, $options['root'])) { + $path = ltrim(substr($input, strlen($root)), '/'); + } else { + $path = null; + } + } else { + $root = $this->root; + $path = $input; + $input = $this->root.'/'.$path; + } + + if (false !== strpos($input, '*')) { + return $this->createGlobAsset($input, $root, $options['vars']); + } + + return $this->createFileAsset($input, $root, $path, $options['vars']); + } + + protected function createAssetCollection(array $assets = array(), array $options = array()) + { + return new AssetCollection($assets, array(), null, isset($options['vars']) ? $options['vars'] : array()); + } + + protected function createAssetReference($name) + { + if (!$this->am) { + throw new \LogicException('There is no asset manager.'); + } + + return new AssetReference($this->am, $name); + } + + protected function createHttpAsset($sourceUrl, $vars) + { + return new HttpAsset($sourceUrl, array(), false, $vars); + } + + protected function createGlobAsset($glob, $root = null, $vars) + { + return new GlobAsset($glob, array(), $root, $vars); + } + + protected function createFileAsset($source, $root = null, $path = null, $vars) + { + return new FileAsset($source, array(), $root, $path, $vars); + } + + protected function getFilter($name) + { + if (!$this->fm) { + throw new \LogicException('There is no filter manager.'); + } + + return $this->fm->get($name); + } + + /** + * Filters an asset collection through the factory workers. + * + * Each leaf asset will be processed first, followed by the asset + * collection itself. + * + * @param AssetCollectionInterface $asset An asset collection + * + * @return AssetCollectionInterface + */ + private function applyWorkers(AssetCollectionInterface $asset) + { + foreach ($asset as $leaf) { + foreach ($this->workers as $worker) { + $retval = $worker->process($leaf); + + if ($retval instanceof AssetInterface && $leaf !== $retval) { + $asset->replaceLeaf($leaf, $retval); + } + } + } + + foreach ($this->workers as $worker) { + $retval = $worker->process($asset); + + if ($retval instanceof AssetInterface) { + $asset = $retval; + } + } + + return $asset instanceof AssetCollectionInterface ? $asset : $this->createAssetCollection(array($asset)); + } + + private static function isAbsolutePath($path) + { + return '/' == $path[0] || '\\' == $path[0] || (3 < strlen($path) && ctype_alpha($path[0]) && $path[1] == ':' && ('\\' == $path[2] || '/' == $path[2])); + } + + /** + * Loops through the root directories and returns the first match. + * + * @param string $path An absolute path + * @param array $roots An array of root directories + * + * @return string|null The matching root directory, if found + */ + private static function findRootDir($path, array $roots) + { + foreach ($roots as $root) { + if (0 === strpos($path, $root)) { + return $root; + } + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/LazyAssetManager.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/LazyAssetManager.php new file mode 100644 index 0000000..5f8fe3f --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/LazyAssetManager.php @@ -0,0 +1,242 @@ + + */ +class LazyAssetManager extends AssetManager +{ + private $factory; + private $loaders; + private $resources; + private $formulae; + private $loaded; + private $loading; + + /** + * Constructor. + * + * @param AssetFactory $factory The asset factory + * @param array $loaders An array of loaders indexed by alias + */ + public function __construct(AssetFactory $factory, $loaders = array()) + { + $this->factory = $factory; + $this->loaders = array(); + $this->resources = array(); + $this->formulae = array(); + $this->loaded = false; + $this->loading = false; + + foreach ($loaders as $alias => $loader) { + $this->setLoader($alias, $loader); + } + } + + /** + * Adds a loader to the asset manager. + * + * @param string $alias An alias for the loader + * @param FormulaLoaderInterface $loader A loader + */ + public function setLoader($alias, FormulaLoaderInterface $loader) + { + $this->loaders[$alias] = $loader; + $this->loaded = false; + } + + /** + * Adds a resource to the asset manager. + * + * @param ResourceInterface $resource A resource + * @param string $loader The loader alias for this resource + */ + public function addResource(ResourceInterface $resource, $loader) + { + $this->resources[$loader][] = $resource; + $this->loaded = false; + } + + /** + * Returns an array of resources. + * + * @return array An array of resources + */ + public function getResources() + { + $resources = array(); + foreach ($this->resources as $r) { + $resources = array_merge($resources, $r); + } + + return $resources; + } + + /** + * Checks for an asset formula. + * + * @param string $name An asset name + * + * @return Boolean If there is a formula + */ + public function hasFormula($name) + { + if (!$this->loaded) { + $this->load(); + } + + return isset($this->formulae[$name]); + } + + /** + * Returns an asset's formula. + * + * @param string $name An asset name + * + * @return array The formula + * + * @throws \InvalidArgumentException If there is no formula by that name + */ + public function getFormula($name) + { + if (!$this->loaded) { + $this->load(); + } + + if (!isset($this->formulae[$name])) { + throw new \InvalidArgumentException(sprintf('There is no "%s" formula.', $name)); + } + + return $this->formulae[$name]; + } + + /** + * Sets a formula on the asset manager. + * + * @param string $name An asset name + * @param array $formula A formula + */ + public function setFormula($name, array $formula) + { + $this->formulae[$name] = $formula; + } + + /** + * Loads formulae from resources. + * + * @throws \LogicException If a resource has been added to an invalid loader + */ + public function load() + { + if ($this->loading) { + return; + } + + if ($diff = array_diff(array_keys($this->resources), array_keys($this->loaders))) { + throw new \LogicException('The following loader(s) are not registered: '.implode(', ', $diff)); + } + + $this->loading = true; + + foreach ($this->resources as $loader => $resources) { + foreach ($resources as $resource) { + $this->formulae = array_replace($this->formulae, $this->loaders[$loader]->load($resource)); + } + } + + $this->loaded = true; + $this->loading = false; + } + + public function get($name) + { + if (!$this->loaded) { + $this->load(); + } + + if (!parent::has($name) && isset($this->formulae[$name])) { + list($inputs, $filters, $options) = $this->formulae[$name]; + $options['name'] = $name; + parent::set($name, $this->factory->createAsset($inputs, $filters, $options)); + } + + return parent::get($name); + } + + public function has($name) + { + if (!$this->loaded) { + $this->load(); + } + + return isset($this->formulae[$name]) || parent::has($name); + } + + public function getNames() + { + if (!$this->loaded) { + $this->load(); + } + + return array_unique(array_merge(parent::getNames(), array_keys($this->formulae))); + } + + public function isDebug() + { + return $this->factory->isDebug(); + } + + public function getLastModified(AssetInterface $asset) + { + $mtime = $asset->getLastModified(); + if (!$filters = $asset->getFilters()) { + return $mtime; + } + + // prepare load path + $sourceRoot = $asset->getSourceRoot(); + $sourcePath = $asset->getSourcePath(); + $loadPath = $sourceRoot && $sourcePath ? dirname($sourceRoot.'/'.$sourcePath) : null; + + $prevFilters = array(); + foreach ($filters as $filter) { + $prevFilters[] = $filter; + + if (!$filter instanceof DependencyExtractorInterface) { + continue; + } + + // extract children from asset after running all preceeding filters + $clone = clone $asset; + $clone->clearFilters(); + foreach (array_slice($prevFilters, 0, -1) as $prevFilter) { + $clone->ensureFilter($prevFilter); + } + $clone->load(); + + foreach ($filter->getChildren($this->factory, $clone->getContent(), $loadPath) as $child) { + $mtime = max($mtime, $this->getLastModified($child)); + } + } + + return $mtime; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/BasePhpFormulaLoader.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/BasePhpFormulaLoader.php new file mode 100644 index 0000000..122d53c --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/BasePhpFormulaLoader.php @@ -0,0 +1,159 @@ + + */ +abstract class BasePhpFormulaLoader implements FormulaLoaderInterface +{ + protected $factory; + protected $prototypes; + + public function __construct(AssetFactory $factory) + { + $this->factory = $factory; + $this->prototypes = array(); + + foreach ($this->registerPrototypes() as $prototype => $options) { + $this->addPrototype($prototype, $options); + } + } + + public function addPrototype($prototype, array $options = array()) + { + $tokens = token_get_all('prototypes[$prototype] = array($tokens, $options); + } + + public function load(ResourceInterface $resource) + { + if (!$nbProtos = count($this->prototypes)) { + throw new \LogicException('There are no prototypes registered.'); + } + + $buffers = array_fill(0, $nbProtos, ''); + $bufferLevels = array_fill(0, $nbProtos, 0); + $buffersInWildcard = array(); + + $tokens = token_get_all($resource->getContent()); + $calls = array(); + + while ($token = array_shift($tokens)) { + $current = self::tokenToString($token); + // loop through each prototype (by reference) + foreach (array_keys($this->prototypes) as $i) { + $prototype =& $this->prototypes[$i][0]; + $options = $this->prototypes[$i][1]; + $buffer =& $buffers[$i]; + $level =& $bufferLevels[$i]; + + if (isset($buffersInWildcard[$i])) { + switch ($current) { + case '(': ++$level; break; + case ')': --$level; break; + } + + $buffer .= $current; + + if (!$level) { + $calls[] = array($buffer.';', $options); + $buffer = ''; + unset($buffersInWildcard[$i]); + } + } elseif ($current == self::tokenToString(current($prototype))) { + $buffer .= $current; + if ('*' == self::tokenToString(next($prototype))) { + $buffersInWildcard[$i] = true; + ++$level; + } + } else { + reset($prototype); + unset($buffersInWildcard[$i]); + $buffer = ''; + } + } + } + + $formulae = array(); + foreach ($calls as $call) { + $formulae += call_user_func_array(array($this, 'processCall'), $call); + } + + return $formulae; + } + + private function processCall($call, array $protoOptions = array()) + { + $tmp = tempnam(sys_get_temp_dir(), 'assetic'); + file_put_contents($tmp, implode("\n", array( + 'registerSetupCode(), + $call, + 'echo serialize($_call);', + ))); + $args = unserialize(shell_exec('php '.escapeshellarg($tmp))); + unlink($tmp); + + $inputs = isset($args[0]) ? self::argumentToArray($args[0]) : array(); + $filters = isset($args[1]) ? self::argumentToArray($args[1]) : array(); + $options = isset($args[2]) ? $args[2] : array(); + + if (!isset($options['debug'])) { + $options['debug'] = $this->factory->isDebug(); + } + + if (!is_array($options)) { + throw new \RuntimeException('The third argument must be omitted, null or an array.'); + } + + // apply the prototype options + $options += $protoOptions; + + if (!isset($options['name'])) { + $options['name'] = $this->factory->generateAssetName($inputs, $filters, $options); + } + + return array($options['name'] => array($inputs, $filters, $options)); + } + + /** + * Returns an array of prototypical calls and options. + * + * @return array Prototypes and options + */ + abstract protected function registerPrototypes(); + + /** + * Returns setup code for the reflection scriptlet. + * + * @return string Some PHP setup code + */ + abstract protected function registerSetupCode(); + + protected static function tokenToString($token) + { + return is_array($token) ? $token[1] : $token; + } + + protected static function argumentToArray($argument) + { + return is_array($argument) ? $argument : array_filter(array_map('trim', explode(',', $argument))); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/CachedFormulaLoader.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/CachedFormulaLoader.php new file mode 100644 index 0000000..cd57def --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/CachedFormulaLoader.php @@ -0,0 +1,68 @@ + + */ +class CachedFormulaLoader implements FormulaLoaderInterface +{ + private $loader; + private $configCache; + private $debug; + + /** + * Constructor. + * + * When the loader is in debug mode it will ensure the cached formulae + * are fresh before returning them. + * + * @param FormulaLoaderInterface $loader A formula loader + * @param ConfigCache $configCache A config cache + * @param Boolean $debug The debug mode + */ + public function __construct(FormulaLoaderInterface $loader, ConfigCache $configCache, $debug = false) + { + $this->loader = $loader; + $this->configCache = $configCache; + $this->debug = $debug; + } + + public function load(ResourceInterface $resources) + { + if (!$resources instanceof IteratorResourceInterface) { + $resources = array($resources); + } + + $formulae = array(); + + foreach ($resources as $resource) { + $id = (string) $resource; + if (!$this->configCache->has($id) || ($this->debug && !$resource->isFresh($this->configCache->getTimestamp($id)))) { + $formulae += $this->loader->load($resource); + $this->configCache->set($id, $formulae); + } else { + $formulae += $this->configCache->get($id); + } + } + + return $formulae; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FormulaLoaderInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FormulaLoaderInterface.php new file mode 100644 index 0000000..f7adc1a --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FormulaLoaderInterface.php @@ -0,0 +1,34 @@ + + */ +interface FormulaLoaderInterface +{ + /** + * Loads formulae from a resource. + * + * Formulae should be loaded the same regardless of the current debug + * mode. Debug considerations should happen downstream. + * + * @param ResourceInterface $resource A resource + * + * @return array An array of formulae + */ + public function load(ResourceInterface $resource); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FunctionCallsFormulaLoader.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FunctionCallsFormulaLoader.php new file mode 100644 index 0000000..902a523 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FunctionCallsFormulaLoader.php @@ -0,0 +1,53 @@ + + */ +class FunctionCallsFormulaLoader extends BasePhpFormulaLoader +{ + protected function registerPrototypes() + { + return array( + 'assetic_javascripts(*)' => array('output' => 'js/*.js'), + 'assetic_stylesheets(*)' => array('output' => 'css/*.css'), + 'assetic_image(*)' => array('output' => 'images/*'), + ); + } + + protected function registerSetupCode() + { + return <<<'EOF' +function assetic_javascripts() +{ + global $_call; + $_call = func_get_args(); +} + +function assetic_stylesheets() +{ + global $_call; + $_call = func_get_args(); +} + +function assetic_image() +{ + global $_call; + $_call = func_get_args(); +} + +EOF; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/CoalescingDirectoryResource.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/CoalescingDirectoryResource.php new file mode 100644 index 0000000..da4a40e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/CoalescingDirectoryResource.php @@ -0,0 +1,112 @@ + + */ +class CoalescingDirectoryResource implements IteratorResourceInterface +{ + private $directories; + + public function __construct($directories) + { + $this->directories = array(); + + foreach ($directories as $directory) { + $this->addDirectory($directory); + } + } + + public function addDirectory(IteratorResourceInterface $directory) + { + $this->directories[] = $directory; + } + + public function isFresh($timestamp) + { + foreach ($this->getFileResources() as $file) { + if (!$file->isFresh($timestamp)) { + return false; + } + } + + return true; + } + + public function getContent() + { + $parts = array(); + foreach ($this->getFileResources() as $file) { + $parts[] = $file->getContent(); + } + + return implode("\n", $parts); + } + + /** + * Returns a string to uniquely identify the current resource. + * + * @return string An identifying string + */ + public function __toString() + { + $parts = array(); + foreach ($this->directories as $directory) { + $parts[] = (string) $directory; + } + + return implode(',', $parts); + } + + public function getIterator() + { + return new \ArrayIterator($this->getFileResources()); + } + + /** + * Returns the relative version of a filename. + * + * @param ResourceInterface $file The file + * @param ResourceInterface $directory The directory + * + * @return string The name to compare with files from other directories + */ + protected function getRelativeName(ResourceInterface $file, ResourceInterface $directory) + { + return substr((string) $file, strlen((string) $directory)); + } + + /** + * Performs the coalesce. + * + * @return array An array of file resources + */ + private function getFileResources() + { + $paths = array(); + + foreach ($this->directories as $directory) { + foreach ($directory as $file) { + $relative = $this->getRelativeName($file, $directory); + + if (!isset($paths[$relative])) { + $paths[$relative] = $file; + } + } + } + + return array_values($paths); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/DirectoryResource.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/DirectoryResource.php new file mode 100644 index 0000000..83c42be --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/DirectoryResource.php @@ -0,0 +1,133 @@ + + */ +class DirectoryResource implements IteratorResourceInterface +{ + private $path; + private $pattern; + + /** + * Constructor. + * + * @param string $path A directory path + * @param string $pattern A filename pattern + */ + public function __construct($path, $pattern = null) + { + if (DIRECTORY_SEPARATOR != substr($path, -1)) { + $path .= DIRECTORY_SEPARATOR; + } + + $this->path = $path; + $this->pattern = $pattern; + } + + public function isFresh($timestamp) + { + if (!is_dir($this->path) || filemtime($this->path) > $timestamp) { + return false; + } + + foreach ($this as $resource) { + if (!$resource->isFresh($timestamp)) { + return false; + } + } + + return true; + } + + /** + * Returns the combined content of all inner resources. + */ + public function getContent() + { + $content = array(); + foreach ($this as $resource) { + $content[] = $resource->getContent(); + } + + return implode("\n", $content); + } + + public function __toString() + { + return $this->path; + } + + public function getIterator() + { + return is_dir($this->path) + ? new DirectoryResourceIterator($this->getInnerIterator()) + : new \EmptyIterator(); + } + + protected function getInnerIterator() + { + return new DirectoryResourceFilterIterator(new \RecursiveDirectoryIterator($this->path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS), $this->pattern); + } +} + +/** + * An iterator that converts file objects into file resources. + * + * @author Kris Wallsmith + * @access private + */ +class DirectoryResourceIterator extends \RecursiveIteratorIterator +{ + public function current() + { + return new FileResource(parent::current()->getPathname()); + } +} + +/** + * Filters files by a basename pattern. + * + * @author Kris Wallsmith + * @access private + */ +class DirectoryResourceFilterIterator extends \RecursiveFilterIterator +{ + protected $pattern; + + public function __construct(\RecursiveDirectoryIterator $iterator, $pattern = null) + { + parent::__construct($iterator); + + $this->pattern = $pattern; + } + + public function accept() + { + $file = $this->current(); + $name = $file->getBasename(); + + if ($file->isDir()) { + return '.' != $name[0]; + } + + return null === $this->pattern || 0 < preg_match($this->pattern, $name); + } + + public function getChildren() + { + return new self(new \RecursiveDirectoryIterator($this->current()->getPathname(), \RecursiveDirectoryIterator::FOLLOW_SYMLINKS), $this->pattern); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/FileResource.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/FileResource.php new file mode 100644 index 0000000..5055006 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/FileResource.php @@ -0,0 +1,47 @@ + + */ +class FileResource implements ResourceInterface +{ + private $path; + + /** + * Constructor. + * + * @param string $path The path to a file + */ + public function __construct($path) + { + $this->path = $path; + } + + public function isFresh($timestamp) + { + return file_exists($this->path) && filemtime($this->path) <= $timestamp; + } + + public function getContent() + { + return file_exists($this->path) ? file_get_contents($this->path) : ''; + } + + public function __toString() + { + return $this->path; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/IteratorResourceInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/IteratorResourceInterface.php new file mode 100644 index 0000000..815c958 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/IteratorResourceInterface.php @@ -0,0 +1,21 @@ + + */ +interface IteratorResourceInterface extends ResourceInterface, \IteratorAggregate +{ +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/ResourceInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/ResourceInterface.php new file mode 100644 index 0000000..a33610b --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/ResourceInterface.php @@ -0,0 +1,43 @@ + + */ +interface ResourceInterface +{ + /** + * Checks if a timestamp represents the latest resource. + * + * @param integer $timestamp A UNIX timestamp + * + * @return Boolean True if the timestamp is up to date + */ + public function isFresh($timestamp); + + /** + * Returns the content of the resource. + * + * @return string The content + */ + public function getContent(); + + /** + * Returns a unique string for the current resource. + * + * @return string A unique string to identity the current resource + */ + public function __toString(); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/CacheBustingWorker.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/CacheBustingWorker.php new file mode 100644 index 0000000..8eaaee6 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/CacheBustingWorker.php @@ -0,0 +1,72 @@ + + */ +class CacheBustingWorker implements WorkerInterface +{ + protected $am; + private $separator; + + public function __construct(LazyAssetManager $am, $separator = '-') + { + $this->am = $am; + $this->separator = $separator; + } + + public function process(AssetInterface $asset) + { + if (!$path = $asset->getTargetPath()) { + // no path to work with + return; + } + + if (!$search = pathinfo($path, PATHINFO_EXTENSION)) { + // nothing to replace + return; + } + + $replace = $this->separator.$this->getHash($asset).'.'.$search; + if (preg_match('/'.preg_quote($replace, '/').'$/', $path)) { + // already replaced + return; + } + + $asset->setTargetPath( + preg_replace('/\.'.preg_quote($search, '/').'$/', $replace, $path) + ); + } + + protected function getHash(AssetInterface $asset) + { + $hash = hash_init('sha1'); + + hash_update($hash, $this->am->getLastModified($asset)); + + if ($asset instanceof AssetCollectionInterface) { + foreach ($asset as $i => $leaf) { + $sourcePath = $leaf->getSourcePath(); + hash_update($hash, $sourcePath ?: $i); + } + } + + return substr(hash_final($hash), 0, 7); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/EnsureFilterWorker.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/EnsureFilterWorker.php new file mode 100644 index 0000000..856e2ff --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/EnsureFilterWorker.php @@ -0,0 +1,60 @@ + + * @todo A better asset-matcher mechanism + */ +class EnsureFilterWorker implements WorkerInterface +{ + const CHECK_SOURCE = 1; + const CHECK_TARGET = 2; + + private $pattern; + private $filter; + private $flags; + + /** + * Constructor. + * + * @param string $pattern A regex for checking the asset's target URL + * @param FilterInterface $filter A filter to apply if the regex matches + * @param integer $flags Flags for what to check + */ + public function __construct($pattern, FilterInterface $filter, $flags = null) + { + if (null === $flags) { + $flags = self::CHECK_SOURCE | self::CHECK_TARGET; + } + + $this->pattern = $pattern; + $this->filter = $filter; + $this->flags = $flags; + } + + public function process(AssetInterface $asset) + { + if ( + (self::CHECK_SOURCE === (self::CHECK_SOURCE & $this->flags) && preg_match($this->pattern, $asset->getSourcePath())) + || + (self::CHECK_TARGET === (self::CHECK_TARGET & $this->flags) && preg_match($this->pattern, $asset->getTargetPath())) + ) { + $asset->ensureFilter($this->filter); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/WorkerInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/WorkerInterface.php new file mode 100644 index 0000000..786058c --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/WorkerInterface.php @@ -0,0 +1,31 @@ + + */ +interface WorkerInterface +{ + /** + * Processes an asset. + * + * @param AssetInterface $asset An asset + * + * @return AssetInterface|null May optionally return a replacement asset + */ + public function process(AssetInterface $asset); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseCssFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseCssFilter.php new file mode 100644 index 0000000..0d9ff70 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseCssFilter.php @@ -0,0 +1,54 @@ + + */ +abstract class BaseCssFilter implements FilterInterface +{ + /** + * @see CssUtils::filterReferences() + */ + protected function filterReferences($content, $callback, $limit = -1, &$count = 0) + { + return CssUtils::filterReferences($content, $callback, $limit, $count); + } + + /** + * @see CssUtils::filterUrls() + */ + protected function filterUrls($content, $callback, $limit = -1, &$count = 0) + { + return CssUtils::filterUrls($content, $callback, $limit, $count); + } + + /** + * @see CssUtils::filterImports() + */ + protected function filterImports($content, $callback, $limit = -1, &$count = 0, $includeUrl = true) + { + return CssUtils::filterImports($content, $callback, $limit, $count, $includeUrl); + } + + /** + * @see CssUtils::filterIEFilters() + */ + protected function filterIEFilters($content, $callback, $limit = -1, &$count = 0) + { + return CssUtils::filterIEFilters($content, $callback, $limit, $count); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseNodeFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseNodeFilter.php new file mode 100644 index 0000000..d88e9cd --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseNodeFilter.php @@ -0,0 +1,44 @@ +nodePaths; + } + + public function setNodePaths(array $nodePaths) + { + $this->nodePaths = $nodePaths; + } + + public function addNodePath($nodePath) + { + $this->nodePaths[] = $nodePath; + } + + protected function createProcessBuilder(array $arguments = array()) + { + $pb = parent::createProcessBuilder($arguments); + + if ($this->nodePaths) { + $pb->setEnv('NODE_PATH', implode(':', $this->nodePaths)); + $this->mergeEnv($pb); + } + + return $pb; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseProcessFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseProcessFilter.php new file mode 100644 index 0000000..b049f65 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseProcessFilter.php @@ -0,0 +1,58 @@ +timeout = $timeout; + } + + /** + * Creates a new process builder. + * + * @param array $arguments An optional array of arguments + * + * @return ProcessBuilder A new process builder + */ + protected function createProcessBuilder(array $arguments = array()) + { + $pb = new ProcessBuilder($arguments); + + if (null !== $this->timeout) { + $pb->setTimeout($this->timeout); + } + + return $pb; + } + + protected function mergeEnv(ProcessBuilder $pb) + { + foreach (array_filter($_SERVER, 'is_scalar') as $key => $value) { + $pb->setEnv($key, $value); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CallablesFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CallablesFilter.php new file mode 100644 index 0000000..25413b0 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CallablesFilter.php @@ -0,0 +1,49 @@ + + */ +class CallablesFilter implements FilterInterface +{ + private $loader; + private $dumper; + + /** + * @param callable|null $loader + * @param callable|null $dumper + */ + public function __construct($loader = null, $dumper = null) + { + $this->loader = $loader; + $this->dumper = $dumper; + } + + public function filterLoad(AssetInterface $asset) + { + if (null !== $callable = $this->loader) { + $callable($asset); + } + } + + public function filterDump(AssetInterface $asset) + { + if (null !== $callable = $this->dumper) { + $callable($asset); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CoffeeScriptFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CoffeeScriptFilter.php new file mode 100644 index 0000000..6e6c95a --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CoffeeScriptFilter.php @@ -0,0 +1,72 @@ + + */ +class CoffeeScriptFilter extends BaseNodeFilter +{ + private $coffeeBin; + private $nodeBin; + + // coffee options + private $bare; + + public function __construct($coffeeBin = '/usr/bin/coffee', $nodeBin = null) + { + $this->coffeeBin = $coffeeBin; + $this->nodeBin = $nodeBin; + } + + public function setBare($bare) + { + $this->bare = $bare; + } + + public function filterLoad(AssetInterface $asset) + { + $input = tempnam(sys_get_temp_dir(), 'assetic_coffeescript'); + file_put_contents($input, $asset->getContent()); + + $pb = $this->createProcessBuilder($this->nodeBin + ? array($this->nodeBin, $this->coffeeBin) + : array($this->coffeeBin)); + + $pb->add('-cp'); + + if ($this->bare) { + $pb->add('--bare'); + } + + $pb->add($input); + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 !== $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CompassFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CompassFilter.php new file mode 100644 index 0000000..c32fc46 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CompassFilter.php @@ -0,0 +1,401 @@ + + */ +class CompassFilter extends BaseProcessFilter implements DependencyExtractorInterface +{ + private $compassPath; + private $rubyPath; + private $scss; + + // sass options + private $unixNewlines; + private $debugInfo; + private $cacheLocation; + private $noCache; + + // compass options + private $force; + private $style; + private $quiet; + private $boring; + private $noLineComments; + private $imagesDir; + private $javascriptsDir; + private $fontsDir; + + // compass configuration file options + private $plugins = array(); + private $loadPaths = array(); + private $httpPath; + private $httpImagesPath; + private $httpFontsPath; + private $httpGeneratedImagesPath; + private $generatedImagesPath; + private $httpJavascriptsPath; + private $homeEnv = true; + + public function __construct($compassPath = '/usr/bin/compass', $rubyPath = null) + { + $this->compassPath = $compassPath; + $this->rubyPath = $rubyPath; + $this->cacheLocation = sys_get_temp_dir(); + + if ('cli' !== php_sapi_name()) { + $this->boring = true; + } + } + + public function setScss($scss) + { + $this->scss = $scss; + } + + // sass options setters + public function setUnixNewlines($unixNewlines) + { + $this->unixNewlines = $unixNewlines; + } + + public function setDebugInfo($debugInfo) + { + $this->debugInfo = $debugInfo; + } + + public function setCacheLocation($cacheLocation) + { + $this->cacheLocation = $cacheLocation; + } + + public function setNoCache($noCache) + { + $this->noCache = $noCache; + } + + // compass options setters + public function setForce($force) + { + $this->force = $force; + } + + public function setStyle($style) + { + $this->style = $style; + } + + public function setQuiet($quiet) + { + $this->quiet = $quiet; + } + + public function setBoring($boring) + { + $this->boring = $boring; + } + + public function setNoLineComments($noLineComments) + { + $this->noLineComments = $noLineComments; + } + + public function setImagesDir($imagesDir) + { + $this->imagesDir = $imagesDir; + } + + public function setJavascriptsDir($javascriptsDir) + { + $this->javascriptsDir = $javascriptsDir; + } + + public function setFontsDir($fontsDir) + { + $this->fontsDir = $fontsDir; + } + + // compass configuration file options setters + public function setPlugins(array $plugins) + { + $this->plugins = $plugins; + } + + public function addPlugin($plugin) + { + $this->plugins[] = $plugin; + } + + public function setLoadPaths(array $loadPaths) + { + $this->loadPaths = $loadPaths; + } + + public function addLoadPath($loadPath) + { + $this->loadPaths[] = $loadPath; + } + + public function setHttpPath($httpPath) + { + $this->httpPath = $httpPath; + } + + public function setHttpImagesPath($httpImagesPath) + { + $this->httpImagesPath = $httpImagesPath; + } + + public function setHttpFontsPath($httpFontsPath) + { + $this->httpFontsPath = $httpFontsPath; + } + + public function setHttpGeneratedImagesPath($httpGeneratedImagesPath) + { + $this->httpGeneratedImagesPath = $httpGeneratedImagesPath; + } + + public function setGeneratedImagesPath($generatedImagesPath) + { + $this->generatedImagesPath = $generatedImagesPath; + } + + public function setHttpJavascriptsPath($httpJavascriptsPath) + { + $this->httpJavascriptsPath = $httpJavascriptsPath; + } + + public function setHomeEnv($homeEnv) + { + $this->homeEnv = $homeEnv; + } + + public function filterLoad(AssetInterface $asset) + { + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + $loadPaths = $this->loadPaths; + if ($root && $path) { + $loadPaths[] = dirname($root.'/'.$path); + } + + // compass does not seems to handle symlink, so we use realpath() + $tempDir = realpath(sys_get_temp_dir()); + + $compassProcessArgs = array( + $this->compassPath, + 'compile', + $tempDir, + ); + if (null !== $this->rubyPath) { + $compassProcessArgs = array_merge(explode(' ', $this->rubyPath), $compassProcessArgs); + } + + $pb = $this->createProcessBuilder($compassProcessArgs); + + if ($this->force) { + $pb->add('--force'); + } + + if ($this->style) { + $pb->add('--output-style')->add($this->style); + } + + if ($this->quiet) { + $pb->add('--quiet'); + } + + if ($this->boring) { + $pb->add('--boring'); + } + + if ($this->noLineComments) { + $pb->add('--no-line-comments'); + } + + // these two options are not passed into the config file + // because like this, compass adapts this to be xxx_dir or xxx_path + // whether it's an absolute path or not + if ($this->imagesDir) { + $pb->add('--images-dir')->add($this->imagesDir); + } + + if ($this->javascriptsDir) { + $pb->add('--javascripts-dir')->add($this->javascriptsDir); + } + + // options in config file + $optionsConfig = array(); + + if (!empty($loadPaths)) { + $optionsConfig['additional_import_paths'] = $loadPaths; + } + + if ($this->unixNewlines) { + $optionsConfig['sass_options']['unix_newlines'] = true; + } + + if ($this->debugInfo) { + $optionsConfig['sass_options']['debug_info'] = true; + } + + if ($this->cacheLocation) { + $optionsConfig['sass_options']['cache_location'] = $this->cacheLocation; + } + + if ($this->noCache) { + $optionsConfig['sass_options']['no_cache'] = true; + } + + if ($this->httpPath) { + $optionsConfig['http_path'] = $this->httpPath; + } + + if ($this->httpImagesPath) { + $optionsConfig['http_images_path'] = $this->httpImagesPath; + } + + if ($this->httpFontsPath) { + $optionsConfig['http_fonts_path'] = $this->httpFontsPath; + } + + if ($this->httpGeneratedImagesPath) { + $optionsConfig['http_generated_images_path'] = $this->httpGeneratedImagesPath; + } + + if ($this->generatedImagesPath) { + $optionsConfig['generated_images_path'] = $this->generatedImagesPath; + } + + if ($this->httpJavascriptsPath) { + $optionsConfig['http_javascripts_path'] = $this->httpJavascriptsPath; + } + + if ($this->fontsDir) { + $optionsConfig['fonts_dir'] = $this->fontsDir; + } + + // options in configuration file + if (count($optionsConfig)) { + $config = array(); + foreach ($this->plugins as $plugin) { + $config[] = sprintf("require '%s'", addcslashes($plugin, '\\')); + } + foreach ($optionsConfig as $name => $value) { + if (!is_array($value)) { + $config[] = sprintf('%s = "%s"', $name, addcslashes($value, '\\')); + } elseif (!empty($value)) { + $config[] = sprintf('%s = %s', $name, $this->formatArrayToRuby($value)); + } + } + + $configFile = tempnam($tempDir, 'assetic_compass'); + file_put_contents($configFile, implode("\n", $config)."\n"); + $pb->add('--config')->add($configFile); + } + + $pb->add('--sass-dir')->add('')->add('--css-dir')->add(''); + + // compass choose the type (sass or scss from the filename) + if (null !== $this->scss) { + $type = $this->scss ? 'scss' : 'sass'; + } elseif ($path) { + // FIXME: what if the extension is something else? + $type = pathinfo($path, PATHINFO_EXTENSION); + } else { + $type = 'scss'; + } + + $tempName = tempnam($tempDir, 'assetic_compass'); + unlink($tempName); // FIXME: don't use tempnam() here + + // input + $input = $tempName.'.'.$type; + + // work-around for https://github.com/chriseppstein/compass/issues/748 + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $input = str_replace('\\', '/', $input); + } + + $pb->add($input); + file_put_contents($input, $asset->getContent()); + + // output + $output = $tempName.'.css'; + + if ($this->homeEnv) { + // it's not really usefull but... https://github.com/chriseppstein/compass/issues/376 + $pb->setEnv('HOME', sys_get_temp_dir()); + $this->mergeEnv($pb); + } + + $proc = $pb->getProcess(); + $code = $proc->run(); + + if (0 !== $code) { + unlink($input); + if (isset($configFile)) { + unlink($configFile); + } + + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent(file_get_contents($output)); + + unlink($input); + unlink($output); + if (isset($configFile)) { + unlink($configFile); + } + } + + public function filterDump(AssetInterface $asset) + { + } + + public function getChildren(AssetFactory $factory, $content, $loadPath = null) + { + // todo + return array(); + } + + private function formatArrayToRuby($array) + { + $output = array(); + + // does we have an associative array ? + if (count(array_filter(array_keys($array), "is_numeric")) != count($array)) { + foreach ($array as $name => $value) { + $output[] = sprintf(' :%s => "%s"', $name, addcslashes($value, '\\')); + } + $output = "{\n".implode(",\n", $output)."\n}"; + } else { + foreach ($array as $name => $value) { + $output[] = sprintf(' "%s"', addcslashes($value, '\\')); + } + $output = "[\n".implode(",\n", $output)."\n]"; + } + + return $output; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssEmbedFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssEmbedFilter.php new file mode 100644 index 0000000..e0c2c15 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssEmbedFilter.php @@ -0,0 +1,145 @@ + + */ +class CssEmbedFilter extends BaseProcessFilter implements DependencyExtractorInterface +{ + private $jarPath; + private $javaPath; + private $charset; + private $mhtml; // Enable MHTML mode. + private $mhtmlRoot; // Use as the MHTML root for the file. + private $root; // Prepends to all relative URLs. + private $skipMissing; // Don't throw an error for missing image files. + private $maxUriLength; // Maximum length for a data URI. Defaults to 32768. + private $maxImageSize; // Maximum image size (in bytes) to convert. + + public function __construct($jarPath, $javaPath = '/usr/bin/java') + { + $this->jarPath = $jarPath; + $this->javaPath = $javaPath; + } + + public function setCharset($charset) + { + $this->charset = $charset; + } + + public function setMhtml($mhtml) + { + $this->mhtml = $mhtml; + } + + public function setMhtmlRoot($mhtmlRoot) + { + $this->mhtmlRoot = $mhtmlRoot; + } + + public function setRoot($root) + { + $this->root = $root; + } + + public function setSkipMissing($skipMissing) + { + $this->skipMissing = $skipMissing; + } + + public function setMaxUriLength($maxUriLength) + { + $this->maxUriLength = $maxUriLength; + } + + public function setMaxImageSize($maxImageSize) + { + $this->maxImageSize = $maxImageSize; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $pb = $this->createProcessBuilder(array( + $this->javaPath, + '-jar', + $this->jarPath, + )); + + if (null !== $this->charset) { + $pb->add('--charset')->add($this->charset); + } + + if ($this->mhtml) { + $pb->add('--mhtml'); + } + + if (null !== $this->mhtmlRoot) { + $pb->add('--mhtmlroot')->add($this->mhtmlRoot); + } + + // automatically define root if not already defined + if (null === $this->root) { + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + if ($root && $path) { + $pb->add('--root')->add(dirname($root.'/'.$path)); + } + } else { + $pb->add('--root')->add($this->root); + } + + if ($this->skipMissing) { + $pb->add('--skip-missing'); + } + + if (null !== $this->maxUriLength) { + $pb->add('--max-uri-length')->add($this->maxUriLength); + } + + if (null !== $this->maxImageSize) { + $pb->add('--max-image-size')->add($this->maxImageSize); + } + + // input + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_cssembed')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 !== $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function getChildren(AssetFactory $factory, $content, $loadPath = null) + { + // todo + return array(); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssImportFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssImportFilter.php new file mode 100644 index 0000000..82ff209 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssImportFilter.php @@ -0,0 +1,113 @@ + + */ +class CssImportFilter extends BaseCssFilter implements DependencyExtractorInterface +{ + private $importFilter; + + /** + * Constructor. + * + * @param FilterInterface $importFilter Filter for each imported asset + */ + public function __construct(FilterInterface $importFilter = null) + { + $this->importFilter = $importFilter ?: new CssRewriteFilter(); + } + + public function filterLoad(AssetInterface $asset) + { + $importFilter = $this->importFilter; + $sourceRoot = $asset->getSourceRoot(); + $sourcePath = $asset->getSourcePath(); + + $callback = function($matches) use ($importFilter, $sourceRoot, $sourcePath) { + if (!$matches['url'] || null === $sourceRoot) { + return $matches[0]; + } + + $importRoot = $sourceRoot; + + if (false !== strpos($matches['url'], '://')) { + // absolute + list($importScheme, $tmp) = explode('://', $matches['url'], 2); + list($importHost, $importPath) = explode('/', $tmp, 2); + $importRoot = $importScheme.'://'.$importHost; + } elseif (0 === strpos($matches['url'], '//')) { + // protocol-relative + list($importHost, $importPath) = explode('/', substr($matches['url'], 2), 2); + $importHost = '//'.$importHost; + } elseif ('/' == $matches['url'][0]) { + // root-relative + $importPath = substr($matches['url'], 1); + } elseif (null !== $sourcePath) { + // document-relative + $importPath = $matches['url']; + if ('.' != $sourceDir = dirname($sourcePath)) { + $importPath = $sourceDir.'/'.$importPath; + } + } else { + return $matches[0]; + } + + // ignore other imports + if ('css' != pathinfo($importPath, PATHINFO_EXTENSION)) { + return $matches[0]; + } + + $importSource = $importRoot.'/'.$importPath; + if (false !== strpos($importSource, '://') || 0 === strpos($importSource, '//')) { + $import = new HttpAsset($importSource, array($importFilter), true); + } elseif (!file_exists($importSource)) { + // ignore not found imports + return $matches[0]; + } else { + $import = new FileAsset($importSource, array($importFilter), $importRoot, $importPath); + } + + $import->setTargetPath($sourcePath); + + return $import->dump(); + }; + + $content = $asset->getContent(); + $lastHash = md5($content); + + do { + $content = $this->filterImports($content, $callback); + $hash = md5($content); + } while ($lastHash != $hash && $lastHash = $hash); + + $asset->setContent($content); + } + + public function filterDump(AssetInterface $asset) + { + } + + public function getChildren(AssetFactory $factory, $content, $loadPath = null) + { + // todo + return array(); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssMinFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssMinFilter.php new file mode 100644 index 0000000..e633958 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssMinFilter.php @@ -0,0 +1,74 @@ + + */ +class CssMinFilter implements FilterInterface +{ + private $filters; + private $plugins; + + public function __construct() + { + $this->filters = array(); + $this->plugins = array(); + } + + public function setFilters(array $filters) + { + $this->filters = $filters; + } + + public function setFilter($name, $value) + { + $this->filters[$name] = $value; + } + + public function setPlugins(array $plugins) + { + $this->plugins = $plugins; + } + + public function setPlugin($name, $value) + { + $this->plugins[$name] = $value; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $filters = $this->filters; + $plugins = $this->plugins; + + if (isset($filters['ImportImports']) && true === $filters['ImportImports']) { + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + if ($root && $path) { + $filters['ImportImports'] = array('BasePath' => dirname($root.'/'.$path)); + } else { + unset($filters['ImportImports']); + } + } + + $asset->setContent(\CssMin::minify($asset->getContent(), $filters, $plugins)); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssRewriteFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssRewriteFilter.php new file mode 100644 index 0000000..8d612c1 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssRewriteFilter.php @@ -0,0 +1,102 @@ + + */ +class CssRewriteFilter extends BaseCssFilter +{ + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $sourceBase = $asset->getSourceRoot(); + $sourcePath = $asset->getSourcePath(); + $targetPath = $asset->getTargetPath(); + + if (null === $sourcePath || null === $targetPath || $sourcePath == $targetPath) { + return; + } + + // learn how to get from the target back to the source + if (false !== strpos($sourceBase, '://')) { + list($scheme, $url) = explode('://', $sourceBase.'/'.$sourcePath, 2); + list($host, $path) = explode('/', $url, 2); + + $host = $scheme.'://'.$host.'/'; + $path = false === strpos($path, '/') ? '' : dirname($path); + $path .= '/'; + } else { + // assume source and target are on the same host + $host = ''; + + // pop entries off the target until it fits in the source + if ('.' == dirname($sourcePath)) { + $path = str_repeat('../', substr_count($targetPath, '/')); + } elseif ('.' == $targetDir = dirname($targetPath)) { + $path = dirname($sourcePath).'/'; + } else { + $path = ''; + while (0 !== strpos($sourcePath, $targetDir)) { + if (false !== $pos = strrpos($targetDir, '/')) { + $targetDir = substr($targetDir, 0, $pos); + $path .= '../'; + } else { + $targetDir = ''; + $path .= '../'; + break; + } + } + $path .= ltrim(substr(dirname($sourcePath).'/', strlen($targetDir)), '/'); + } + } + + $content = $this->filterReferences($asset->getContent(), function($matches) use ($host, $path) { + if (false !== strpos($matches['url'], '://') || 0 === strpos($matches['url'], '//') || 0 === strpos($matches['url'], 'data:')) { + // absolute or protocol-relative or data uri + return $matches[0]; + } + + if (isset($matches['url'][0]) && '/' == $matches['url'][0]) { + // root relative + return str_replace($matches['url'], $host.$matches['url'], $matches[0]); + } + + // document relative + $url = $matches['url']; + while (0 === strpos($url, '../') && 2 <= substr_count($path, '/')) { + $path = substr($path, 0, strrpos(rtrim($path, '/'), '/') + 1); + $url = substr($url, 3); + } + + $parts = array(); + foreach (explode('/', $host.$path.$url) as $part) { + if ('..' === $part && count($parts) && '..' !== end($parts)) { + array_pop($parts); + } else { + $parts[] = $part; + } + } + + return str_replace($matches['url'], implode('/', $parts), $matches[0]); + }); + + $asset->setContent($content); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/DartFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/DartFilter.php new file mode 100644 index 0000000..9505581 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/DartFilter.php @@ -0,0 +1,67 @@ +dartBin = $dartBin; + } + + public function filterLoad(AssetInterface $asset) + { + $input = tempnam(sys_get_temp_dir(), 'assetic_dart'); + $output = tempnam(sys_get_temp_dir(), 'assetic_dart'); + + file_put_contents($input, $asset->getContent()); + + $pb = $this->createProcessBuilder() + ->add($this->dartBin) + ->add('-o'.$output) + ->add($input) + ; + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 !== $code) { + if (file_exists($output)) { + unlink($output); + } + + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + if (!file_exists($output)) { + throw new \RuntimeException('Error creating output file.'); + } + + $asset->setContent(file_get_contents($output)); + unlink($output); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/DependencyExtractorInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/DependencyExtractorInterface.php new file mode 100644 index 0000000..934371f --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/DependencyExtractorInterface.php @@ -0,0 +1,34 @@ + + */ +interface DependencyExtractorInterface extends FilterInterface +{ + /** + * Returns child assets. + * + * @param AssetFactory $factory The asset factory + * @param string $content The asset content + * @param string $loadPath An optional load path + * + * @return AssetInterface[] Child assets + */ + public function getChildren(AssetFactory $factory, $content, $loadPath = null); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/EmberPrecompileFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/EmberPrecompileFilter.php new file mode 100644 index 0000000..2467960 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/EmberPrecompileFilter.php @@ -0,0 +1,83 @@ + + */ +class EmberPrecompileFilter extends BaseNodeFilter +{ + private $emberBin; + private $nodeBin; + + public function __construct($handlebarsBin = '/usr/bin/ember-precompile', $nodeBin = null) + { + $this->emberBin = $handlebarsBin; + $this->nodeBin = $nodeBin; + } + + public function filterLoad(AssetInterface $asset) + { + $pb = $this->createProcessBuilder($this->nodeBin + ? array($this->nodeBin, $this->emberBin) + : array($this->emberBin)); + + $templateName = basename($asset->getSourcePath()); + + $inputDirPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.uniqid('input_dir'); + $inputPath = $inputDirPath.DIRECTORY_SEPARATOR.$templateName; + $outputPath = tempnam(sys_get_temp_dir(), 'output'); + + mkdir($inputDirPath); + file_put_contents($inputPath, $asset->getContent()); + + $pb->add($inputPath)->add('-f')->add($outputPath); + + $process = $pb->getProcess(); + $returnCode = $process->run(); + + unlink($inputPath); + rmdir($inputDirPath); + + if (127 === $returnCode) { + throw new \RuntimeException('Path to node executable could not be resolved.'); + } + + if (0 !== $returnCode) { + if (file_exists($outputPath)) { + unlink($outputPath); + } + throw FilterException::fromProcess($process)->setInput($asset->getContent()); + } + + if (!file_exists($outputPath)) { + throw new \RuntimeException('Error creating output file.'); + } + + $compiledJs = file_get_contents($outputPath); + unlink($outputPath); + + $asset->setContent($compiledJs); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterCollection.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterCollection.php new file mode 100644 index 0000000..9dc28cb --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterCollection.php @@ -0,0 +1,82 @@ + + */ +class FilterCollection implements FilterInterface, \IteratorAggregate, \Countable +{ + private $filters = array(); + + public function __construct($filters = array()) + { + foreach ($filters as $filter) { + $this->ensure($filter); + } + } + + /** + * Checks that the current collection contains the supplied filter. + * + * If the supplied filter is another filter collection, each of its + * filters will be checked. + */ + public function ensure(FilterInterface $filter) + { + if ($filter instanceof \Traversable) { + foreach ($filter as $f) { + $this->ensure($f); + } + } elseif (!in_array($filter, $this->filters, true)) { + $this->filters[] = $filter; + } + } + + public function all() + { + return $this->filters; + } + + public function clear() + { + $this->filters = array(); + } + + public function filterLoad(AssetInterface $asset) + { + foreach ($this->filters as $filter) { + $filter->filterLoad($asset); + } + } + + public function filterDump(AssetInterface $asset) + { + foreach ($this->filters as $filter) { + $filter->filterDump($asset); + } + } + + public function getIterator() + { + return new \ArrayIterator($this->filters); + } + + public function count() + { + return count($this->filters); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterInterface.php new file mode 100644 index 0000000..17ff87d --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterInterface.php @@ -0,0 +1,36 @@ + + */ +interface FilterInterface +{ + /** + * Filters an asset after it has been loaded. + * + * @param AssetInterface $asset An asset + */ + public function filterLoad(AssetInterface $asset); + + /** + * Filters an asset just before it's dumped. + * + * @param AssetInterface $asset An asset + */ + public function filterDump(AssetInterface $asset); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/BaseCompilerFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/BaseCompilerFilter.php new file mode 100644 index 0000000..59e062e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/BaseCompilerFilter.php @@ -0,0 +1,101 @@ + + */ +abstract class BaseCompilerFilter implements FilterInterface +{ + // compilation levels + const COMPILE_WHITESPACE_ONLY = 'WHITESPACE_ONLY'; + const COMPILE_SIMPLE_OPTIMIZATIONS = 'SIMPLE_OPTIMIZATIONS'; + const COMPILE_ADVANCED_OPTIMIZATIONS = 'ADVANCED_OPTIMIZATIONS'; + + // formatting modes + const FORMAT_PRETTY_PRINT = 'pretty_print'; + const FORMAT_PRINT_INPUT_DELIMITER = 'print_input_delimiter'; + + // warning levels + const LEVEL_QUIET = 'QUIET'; + const LEVEL_DEFAULT = 'DEFAULT'; + const LEVEL_VERBOSE = 'VERBOSE'; + + // languages + const LANGUAGE_ECMASCRIPT3 = 'ECMASCRIPT3'; + const LANGUAGE_ECMASCRIPT5 = 'ECMASCRIPT5'; + const LANGUAGE_ECMASCRIPT5_STRICT = 'ECMASCRIPT5_STRICT'; + + protected $timeout; + protected $compilationLevel; + protected $jsExterns; + protected $externsUrl; + protected $excludeDefaultExterns; + protected $formatting; + protected $useClosureLibrary; + protected $warningLevel; + protected $language; + + public function setTimeout($timeout) + { + $this->timeout = $timeout; + } + + public function setCompilationLevel($compilationLevel) + { + $this->compilationLevel = $compilationLevel; + } + + public function setJsExterns($jsExterns) + { + $this->jsExterns = $jsExterns; + } + + public function setExternsUrl($externsUrl) + { + $this->externsUrl = $externsUrl; + } + + public function setExcludeDefaultExterns($excludeDefaultExterns) + { + $this->excludeDefaultExterns = $excludeDefaultExterns; + } + + public function setFormatting($formatting) + { + $this->formatting = $formatting; + } + + public function setUseClosureLibrary($useClosureLibrary) + { + $this->useClosureLibrary = $useClosureLibrary; + } + + public function setWarningLevel($warningLevel) + { + $this->warningLevel = $warningLevel; + } + + public function setLanguage($language) + { + $this->language = $language; + } + + public function filterLoad(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerApiFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerApiFilter.php new file mode 100644 index 0000000..0b090c7 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerApiFilter.php @@ -0,0 +1,132 @@ + + */ +class CompilerApiFilter extends BaseCompilerFilter +{ + private $proxy; + private $proxyFullUri; + + public function setProxy($proxy) + { + $this->proxy = $proxy; + } + + public function setProxyFullUri($proxyFullUri) + { + $this->proxyFullUri = $proxyFullUri; + } + + public function filterDump(AssetInterface $asset) + { + $query = array( + 'js_code' => $asset->getContent(), + 'output_format' => 'json', + 'output_info' => 'compiled_code', + ); + + if (null !== $this->compilationLevel) { + $query['compilation_level'] = $this->compilationLevel; + } + + if (null !== $this->jsExterns) { + $query['js_externs'] = $this->jsExterns; + } + + if (null !== $this->externsUrl) { + $query['externs_url'] = $this->externsUrl; + } + + if (null !== $this->excludeDefaultExterns) { + $query['exclude_default_externs'] = $this->excludeDefaultExterns ? 'true' : 'false'; + } + + if (null !== $this->formatting) { + $query['formatting'] = $this->formatting; + } + + if (null !== $this->useClosureLibrary) { + $query['use_closure_library'] = $this->useClosureLibrary ? 'true' : 'false'; + } + + if (null !== $this->warningLevel) { + $query['warning_level'] = $this->warningLevel; + } + + if (null !== $this->language) { + $query['language'] = $this->language; + } + + if (preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'))) { + $contextOptions = array('http' => array( + 'method' => 'POST', + 'header' => 'Content-Type: application/x-www-form-urlencoded', + 'content' => http_build_query($query), + )); + if (null !== $this->timeout) { + $contextOptions['http']['timeout'] = $this->timeout; + } + if ($this->proxy) { + $contextOptions['http']['proxy'] = $this->proxy; + $contextOptions['http']['request_fulluri'] = (Boolean) $this->proxyFullUri; + } + $context = stream_context_create($contextOptions); + + $response = file_get_contents('http://closure-compiler.appspot.com/compile', false, $context); + $data = json_decode($response); + + } elseif (defined('CURLOPT_POST') && !in_array('curl_init', explode(',', ini_get('disable_functions')))) { + + $ch = curl_init('http://closure-compiler.appspot.com/compile'); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded')); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $query); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); + if (null !== $this->timeout) { + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + } + if ($this->proxy) { + curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, TRUE); + curl_setopt($ch, CURLOPT_PROXY, $this->proxy); + } + $response = curl_exec($ch); + curl_close($ch); + + $data = json_decode($response); + } else { + throw new \RuntimeException("There is no known way to contact closure compiler available"); + } + + if (isset($data->serverErrors) && 0 < count($data->serverErrors)) { + // @codeCoverageIgnoreStart + throw new \RuntimeException(sprintf('The Google Closure Compiler API threw some server errors: '.print_r($data->serverErrors, true))); + // @codeCoverageIgnoreEnd + } + + if (isset($data->errors) && 0 < count($data->errors)) { + // @codeCoverageIgnoreStart + throw new \RuntimeException(sprintf('The Google Closure Compiler API threw some errors: '.print_r($data->errors, true))); + // @codeCoverageIgnoreEnd + } + + $asset->setContent($data->compiledCode); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerJarFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerJarFilter.php new file mode 100644 index 0000000..d4a1e16 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerJarFilter.php @@ -0,0 +1,98 @@ + + */ +class CompilerJarFilter extends BaseCompilerFilter +{ + private $jarPath; + private $javaPath; + + public function __construct($jarPath, $javaPath = '/usr/bin/java') + { + $this->jarPath = $jarPath; + $this->javaPath = $javaPath; + } + + public function filterDump(AssetInterface $asset) + { + $cleanup = array(); + + $pb = new ProcessBuilder(array( + $this->javaPath, + '-jar', + $this->jarPath, + )); + + if (null !== $this->timeout) { + $pb->setTimeout($this->timeout); + } + + if (null !== $this->compilationLevel) { + $pb->add('--compilation_level')->add($this->compilationLevel); + } + + if (null !== $this->jsExterns) { + $cleanup[] = $externs = tempnam(sys_get_temp_dir(), 'assetic_google_closure_compiler'); + file_put_contents($externs, $this->jsExterns); + $pb->add('--externs')->add($externs); + } + + if (null !== $this->externsUrl) { + $cleanup[] = $externs = tempnam(sys_get_temp_dir(), 'assetic_google_closure_compiler'); + file_put_contents($externs, file_get_contents($this->externsUrl)); + $pb->add('--externs')->add($externs); + } + + if (null !== $this->excludeDefaultExterns) { + $pb->add('--use_only_custom_externs'); + } + + if (null !== $this->formatting) { + $pb->add('--formatting')->add($this->formatting); + } + + if (null !== $this->useClosureLibrary) { + $pb->add('--manage_closure_dependencies'); + } + + if (null !== $this->warningLevel) { + $pb->add('--warning_level')->add($this->warningLevel); + } + + if (null !== $this->language) { + $pb->add('--language_in')->add($this->language); + } + + $pb->add('--js')->add($cleanup[] = $input = tempnam(sys_get_temp_dir(), 'assetic_google_closure_compiler')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + array_map('unlink', $cleanup); + + if (0 !== $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/GssFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GssFilter.php new file mode 100644 index 0000000..3c0b84f --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GssFilter.php @@ -0,0 +1,141 @@ + + */ +class GssFilter extends BaseProcessFilter +{ + private $jarPath; + private $javaPath; + private $allowUnrecognizedFunctions; + private $allowedNonStandardFunctions; + private $copyrightNotice; + private $define; + private $gssFunctionMapProvider; + private $inputOrientation; + private $outputOrientation; + private $prettyPrint; + + public function __construct($jarPath, $javaPath = '/usr/bin/java') + { + $this->jarPath = $jarPath; + $this->javaPath = $javaPath; + } + + public function setAllowUnrecognizedFunctions($allowUnrecognizedFunctions) + { + $this->allowUnrecognizedFunctions = $allowUnrecognizedFunctions; + } + + public function setAllowedNonStandardFunctions($allowNonStandardFunctions) + { + $this->allowedNonStandardFunctions = $allowNonStandardFunctions; + } + + public function setCopyrightNotice($copyrightNotice) + { + $this->copyrightNotice = $copyrightNotice; + } + + public function setDefine($define) + { + $this->define = $define; + } + + public function setGssFunctionMapProvider($gssFunctionMapProvider) + { + $this->gssFunctionMapProvider = $gssFunctionMapProvider; + } + + public function setInputOrientation($inputOrientation) + { + $this->inputOrientation = $inputOrientation; + } + + public function setOutputOrientation($outputOrientation) + { + $this->outputOrientation = $outputOrientation; + } + + public function setPrettyPrint($prettyPrint) + { + $this->prettyPrint = $prettyPrint; + } + + public function filterLoad(AssetInterface $asset) + { + $cleanup = array(); + + $pb = $this->createProcessBuilder(array( + $this->javaPath, + '-jar', + $this->jarPath, + )); + + if (null !== $this->allowUnrecognizedFunctions) { + $pb->add('--allow-unrecognized-functions'); + } + + if (null !== $this->allowedNonStandardFunctions) { + $pb->add('--allowed_non_standard_functions')->add($this->allowedNonStandardFunctions); + } + + if (null !== $this->copyrightNotice) { + $pb->add('--copyright-notice')->add($this->copyrightNotice); + } + + if (null !== $this->define) { + $pb->add('--define')->add($this->define); + } + + if (null !== $this->gssFunctionMapProvider) { + $pb->add('--gss-function-map-provider')->add($this->gssFunctionMapProvider); + } + + if (null !== $this->inputOrientation) { + $pb->add('--input-orientation')->add($this->inputOrientation); + } + + if (null !== $this->outputOrientation) { + $pb->add('--output-orientation')->add($this->outputOrientation); + } + + if (null !== $this->prettyPrint) { + $pb->add('--pretty-print'); + } + + $pb->add($cleanup[] = $input = tempnam(sys_get_temp_dir(), 'assetic_google_closure_stylesheets_compiler')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + array_map('unlink', $cleanup); + + if (0 !== $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/HandlebarsFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/HandlebarsFilter.php new file mode 100644 index 0000000..e16e858 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/HandlebarsFilter.php @@ -0,0 +1,102 @@ + + */ +class HandlebarsFilter extends BaseNodeFilter +{ + private $handlebarsBin; + private $nodeBin; + + private $minimize = false; + private $simple = false; + + public function __construct($handlebarsBin = '/usr/bin/handlebars', $nodeBin = null) + { + $this->handlebarsBin = $handlebarsBin; + $this->nodeBin = $nodeBin; + } + + public function setMinimize($minimize) + { + $this->minimize = $minimize; + } + + public function setSimple($simple) + { + $this->simple = $simple; + } + + public function filterLoad(AssetInterface $asset) + { + $pb = $this->createProcessBuilder($this->nodeBin + ? array($this->nodeBin, $this->handlebarsBin) + : array($this->handlebarsBin)); + + $templateName = basename($asset->getSourcePath()); + + $inputDirPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.uniqid('input_dir'); + $inputPath = $inputDirPath.DIRECTORY_SEPARATOR.$templateName; + $outputPath = tempnam(sys_get_temp_dir(), 'output'); + + mkdir($inputDirPath); + file_put_contents($inputPath, $asset->getContent()); + + $pb->add($inputPath)->add('-f')->add($outputPath); + + if ($this->minimize) { + $pb->add('--min'); + } + + if ($this->simple) { + $pb->add('--simple'); + } + + $process = $pb->getProcess(); + $returnCode = $process->run(); + + unlink($inputPath); + rmdir($inputDirPath); + + if (127 === $returnCode) { + throw new \RuntimeException('Path to node executable could not be resolved.'); + } + + if (0 !== $returnCode) { + if (file_exists($outputPath)) { + unlink($outputPath); + } + throw FilterException::fromProcess($process)->setInput($asset->getContent()); + } + + if (!file_exists($outputPath)) { + throw new \RuntimeException('Error creating output file.'); + } + + $compiledJs = file_get_contents($outputPath); + unlink($outputPath); + + $asset->setContent($compiledJs); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/HashableInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/HashableInterface.php new file mode 100644 index 0000000..9442fdb --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/HashableInterface.php @@ -0,0 +1,27 @@ + + */ +interface HashableInterface +{ + /** + * Generates a hash for the object + * + * @return string Object hash + */ + public function hash(); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinFilter.php new file mode 100644 index 0000000..44c08af --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinFilter.php @@ -0,0 +1,34 @@ + + */ +class JSMinFilter implements FilterInterface +{ + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $asset->setContent(\JSMin::minify($asset->getContent())); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinPlusFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinPlusFilter.php new file mode 100644 index 0000000..21dc48e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinPlusFilter.php @@ -0,0 +1,34 @@ + + */ +class JSMinPlusFilter implements FilterInterface +{ + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $asset->setContent(\JSMinPlus::minify($asset->getContent())); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegoptimFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegoptimFilter.php new file mode 100644 index 0000000..68fac7c --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegoptimFilter.php @@ -0,0 +1,80 @@ + + */ +class JpegoptimFilter extends BaseProcessFilter +{ + private $jpegoptimBin; + private $stripAll; + private $max; + + /** + * Constructor. + * + * @param string $jpegoptimBin Path to the jpegoptim binary + */ + public function __construct($jpegoptimBin = '/usr/bin/jpegoptim') + { + $this->jpegoptimBin = $jpegoptimBin; + } + + public function setStripAll($stripAll) + { + $this->stripAll = $stripAll; + } + + public function setMax($max) + { + $this->max = $max; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $pb = $this->createProcessBuilder(array($this->jpegoptimBin)); + + if ($this->stripAll) { + $pb->add('--strip-all'); + } + + if ($this->max) { + $pb->add('--max='.$this->max); + } + + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_jpegoptim')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $proc->run(); + + if (false !== strpos($proc->getOutput(), 'ERROR')) { + unlink($input); + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent(file_get_contents($input)); + + unlink($input); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegtranFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegtranFilter.php new file mode 100644 index 0000000..c495ad1 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegtranFilter.php @@ -0,0 +1,102 @@ + + */ +class JpegtranFilter extends BaseProcessFilter +{ + const COPY_NONE = 'none'; + const COPY_COMMENTS = 'comments'; + const COPY_ALL = 'all'; + + private $jpegtranBin; + private $optimize; + private $copy; + private $progressive; + private $restart; + + /** + * Constructor. + * + * @param string $jpegtranBin Path to the jpegtran binary + */ + public function __construct($jpegtranBin = '/usr/bin/jpegtran') + { + $this->jpegtranBin = $jpegtranBin; + } + + public function setOptimize($optimize) + { + $this->optimize = $optimize; + } + + public function setCopy($copy) + { + $this->copy = $copy; + } + + public function setProgressive($progressive) + { + $this->progressive = $progressive; + } + + public function setRestart($restart) + { + $this->restart = $restart; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $pb = $this->createProcessBuilder(array($this->jpegtranBin)); + + if ($this->optimize) { + $pb->add('-optimize'); + } + + if ($this->copy) { + $pb->add('-copy')->add($this->copy); + } + + if ($this->progressive) { + $pb->add('-progressive'); + } + + if (null !== $this->restart) { + $pb->add('-restart')->add($this->restart); + } + + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_jpegtran')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 !== $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessFilter.php new file mode 100644 index 0000000..4acd38f --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessFilter.php @@ -0,0 +1,206 @@ + + */ +class LessFilter extends BaseNodeFilter implements DependencyExtractorInterface +{ + private $nodeBin; + + /** + * @var array + */ + private $treeOptions; + + /** + * @var array + */ + private $parserOptions; + + /** + * Load Paths + * + * A list of paths which less will search for includes. + * + * @var array + */ + protected $loadPaths = array(); + + /** + * Constructor. + * + * @param string $nodeBin The path to the node binary + * @param array $nodePaths An array of node paths + */ + public function __construct($nodeBin = '/usr/bin/node', array $nodePaths = array()) + { + $this->nodeBin = $nodeBin; + $this->setNodePaths($nodePaths); + $this->treeOptions = array(); + $this->parserOptions = array(); + } + + /** + * @param bool $compress + */ + public function setCompress($compress) + { + $this->addTreeOption('compress', $compress); + } + + public function setLoadPaths(array $loadPaths) + { + $this->loadPaths = $loadPaths; + } + + /** + * Adds a path where less will search for includes + * + * @param string $path Load path (absolute) + */ + public function addLoadPath($path) + { + $this->loadPaths[] = $path; + } + + /** + * @param string $code + * @param string $value + */ + public function addTreeOption($code, $value) + { + $this->treeOptions[$code] = $value; + } + + /** + * @param string $code + * @param string $value + */ + public function addParserOption($code, $value) + { + $this->parserOptions[$code] = $value; + } + + public function filterLoad(AssetInterface $asset) + { + static $format = <<<'EOF' +var less = require('less'); +var sys = require(process.binding('natives').util ? 'util' : 'sys'); + +new(less.Parser)(%s).parse(%s, function(e, tree) { + if (e) { + less.writeError(e); + process.exit(2); + } + + try { + sys.print(tree.toCSS(%s)); + } catch (e) { + less.writeError(e); + process.exit(3); + } +}); + +EOF; + + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + // parser options + $parserOptions = $this->parserOptions; + if ($root && $path) { + $parserOptions['paths'] = array(dirname($root.'/'.$path)); + $parserOptions['filename'] = basename($path); + } + + foreach ($this->loadPaths as $loadPath) { + $parserOptions['paths'][] = $loadPath; + } + + $pb = $this->createProcessBuilder(); + + $pb->add($this->nodeBin)->add($input = tempnam(sys_get_temp_dir(), 'assetic_less')); + file_put_contents($input, sprintf($format, + json_encode($parserOptions), + json_encode($asset->getContent()), + json_encode($this->treeOptions) + )); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 !== $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function filterDump(AssetInterface $asset) + { + } + + /** + * @todo support for @import-once + * @todo support for @import (less) "lib.css" + */ + public function getChildren(AssetFactory $factory, $content, $loadPath = null) + { + $loadPaths = $this->loadPaths; + if (null !== $loadPath) { + $loadPaths[] = $loadPath; + } + + if (empty($loadPaths)) { + return array(); + } + + $children = array(); + foreach (LessUtils::extractImports($content) as $reference) { + if ('.css' === substr($reference, -4)) { + // skip normal css imports + // todo: skip imports with media queries + continue; + } + + if ('.less' !== substr($reference, -5)) { + $reference .= '.less'; + } + + foreach ($loadPaths as $loadPath) { + if (file_exists($file = $loadPath.'/'.$reference)) { + $coll = $factory->createAsset($file, array(), array('root' => $loadPath)); + foreach ($coll as $leaf) { + $leaf->ensureFilter($this); + $children[] = $leaf; + goto next_reference; + } + } + } + + next_reference: + } + + return $children; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessphpFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessphpFilter.php new file mode 100644 index 0000000..fe82412 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessphpFilter.php @@ -0,0 +1,150 @@ + + * @author Kris Wallsmith + */ +class LessphpFilter implements DependencyExtractorInterface +{ + private $presets = array(); + private $formatter; + private $preserveComments; + + /** + * Lessphp Load Paths + * + * @var array + */ + protected $loadPaths = array(); + + /** + * Adds a load path to the paths used by lessphp + * + * @param string $path Load Path + */ + public function addLoadPath($path) + { + $this->loadPaths[] = $path; + } + + /** + * Sets load paths used by lessphp + * + * @param array $loadPaths Load paths + */ + public function setLoadPaths(array $loadPaths) + { + $this->loadPaths = $loadPaths; + } + + public function setPresets(array $presets) + { + $this->presets = $presets; + } + + /** + * @param string $formatter One of "lessjs", "compressed", or "classic". + */ + public function setFormatter($formatter) + { + $this->formatter = $formatter; + } + + /** + * @param boolean $preserveComments + */ + public function setPreserveComments($preserveComments) + { + $this->preserveComments = $preserveComments; + } + + public function filterLoad(AssetInterface $asset) + { + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + $lc = new \lessc(); + if ($root && $path) { + $lc->importDir = dirname($root.'/'.$path); + } + + foreach ($this->loadPaths as $loadPath) { + $lc->addImportDir($loadPath); + } + + if ($this->formatter) { + $lc->setFormatter($this->formatter); + } + + if (null !== $this->preserveComments) { + $lc->setPreserveComments($this->preserveComments); + } + + $asset->setContent($lc->parse($asset->getContent(), $this->presets)); + } + + public function filterDump(AssetInterface $asset) + { + } + + public function getChildren(AssetFactory $factory, $content, $loadPath = null) + { + $loadPaths = $this->loadPaths; + if (null !== $loadPath) { + $loadPaths[] = $loadPath; + } + + if (empty($loadPaths)) { + return array(); + } + + $children = array(); + foreach (LessUtils::extractImports($content) as $reference) { + if ('.css' === substr($reference, -4)) { + // skip normal css imports + // todo: skip imports with media queries + continue; + } + + if ('.less' !== substr($reference, -5)) { + $reference .= '.less'; + } + + foreach ($loadPaths as $loadPath) { + if (file_exists($file = $loadPath.'/'.$reference)) { + $coll = $factory->createAsset($file, array(), array('root' => $loadPath)); + foreach ($coll as $leaf) { + $leaf->ensureFilter($this); + $children[] = $leaf; + goto next_reference; + } + } + } + + next_reference: + } + + return $children; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/OptiPngFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/OptiPngFilter.php new file mode 100644 index 0000000..4f7abcf --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/OptiPngFilter.php @@ -0,0 +1,74 @@ + + */ +class OptiPngFilter extends BaseProcessFilter +{ + private $optipngBin; + private $level; + + /** + * Constructor. + * + * @param string $optipngBin Path to the optipng binary + */ + public function __construct($optipngBin = '/usr/bin/optipng') + { + $this->optipngBin = $optipngBin; + } + + public function setLevel($level) + { + $this->level = $level; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $pb = $this->createProcessBuilder(array($this->optipngBin)); + + if (null !== $this->level) { + $pb->add('-o')->add($this->level); + } + + $pb->add('-out')->add($output = tempnam(sys_get_temp_dir(), 'assetic_optipng')); + unlink($output); + + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_optipng')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + + if (0 !== $code) { + unlink($input); + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent(file_get_contents($output)); + + unlink($input); + unlink($output); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackagerFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackagerFilter.php new file mode 100644 index 0000000..6029833 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackagerFilter.php @@ -0,0 +1,64 @@ + + */ +class PackagerFilter implements FilterInterface +{ + private $packages; + + public function __construct(array $packages = array()) + { + $this->packages = $packages; + } + + public function addPackage($package) + { + $this->packages[] = $package; + } + + public function filterLoad(AssetInterface $asset) + { + static $manifest = <<getContent()); + + $packager = new \Packager(array_merge(array($package), $this->packages)); + $content = $packager->build(array(), array(), array('Application'.$hash)); + + unlink($package.'/package.yml'); + unlink($package.'/source.js'); + rmdir($package); + + $asset->setContent($content); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackerFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackerFilter.php new file mode 100644 index 0000000..3fd41ea --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackerFilter.php @@ -0,0 +1,56 @@ + + */ +class PackerFilter implements FilterInterface +{ + protected $encoding = 'None'; + + protected $fastDecode = true; + + protected $specialChars = false; + + public function setEncoding($encoding) + { + $this->encoding = $encoding; + } + + public function setFastDecode($fastDecode) + { + $this->fastDecode = (bool) $fastDecode; + } + + public function setSpecialChars($specialChars) + { + $this->specialChars = (bool) $specialChars; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $packer = new \JavaScriptPacker($asset->getContent(), $this->encoding, $this->fastDecode, $this->specialChars); + $asset->setContent($packer->pack()); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/PhpCssEmbedFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PhpCssEmbedFilter.php new file mode 100644 index 0000000..5df4423 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PhpCssEmbedFilter.php @@ -0,0 +1,55 @@ + + * @link https://github.com/krichprollsch/phpCssEmbed + */ +class PhpCssEmbedFilter implements DependencyExtractorInterface +{ + private $presets = array(); + + public function setPresets(array $presets) + { + $this->presets = $presets; + } + + public function filterLoad(AssetInterface $asset) + { + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + $pce = new CssEmbed(); + if ($root && $path) { + $pce->setRootDir(dirname($root.'/'.$path)); + } + + $asset->setContent($pce->embedString($asset->getContent())); + } + + public function filterDump(AssetInterface $asset) + { + } + + public function getChildren(AssetFactory $factory, $content, $loadPath = null) + { + // todo + return array(); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/PngoutFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PngoutFilter.php new file mode 100644 index 0000000..571945f --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PngoutFilter.php @@ -0,0 +1,127 @@ + + */ +class PngoutFilter extends BaseProcessFilter +{ + // -c# + const COLOR_GREY = '0'; + const COLOR_RGB = '2'; + const COLOR_PAL = '3'; + const COLOR_GRAY_ALPHA = '4'; + const COLOR_RGB_ALPHA = '6'; + + // -f# + const FILTER_NONE = '0'; + const FILTER_X = '1'; + const FILTER_Y = '2'; + const FILTER_X_Y = '3'; + const FILTER_PAETH = '4'; + const FILTER_MIXED = '5'; + + // -s# + const STRATEGY_XTREME = '0'; + const STRATEGY_INTENSE = '1'; + const STRATEGY_LONGEST_MATCH = '2'; + const STRATEGY_HUFFMAN_ONLY = '3'; + const STRATEGY_UNCOMPRESSED = '4'; + + private $pngoutBin; + private $color; + private $filter; + private $strategy; + private $blockSplitThreshold; + + /** + * Constructor. + * + * @param string $pngoutBin Path to the pngout binary + */ + public function __construct($pngoutBin = '/usr/bin/pngout') + { + $this->pngoutBin = $pngoutBin; + } + + public function setColor($color) + { + $this->color = $color; + } + + public function setFilter($filter) + { + $this->filter = $filter; + } + + public function setStrategy($strategy) + { + $this->strategy = $strategy; + } + + public function setBlockSplitThreshold($blockSplitThreshold) + { + $this->blockSplitThreshold = $blockSplitThreshold; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $pb = $this->createProcessBuilder(array($this->pngoutBin)); + + if (null !== $this->color) { + $pb->add('-c'.$this->color); + } + + if (null !== $this->filter) { + $pb->add('-f'.$this->filter); + } + + if (null !== $this->strategy) { + $pb->add('-s'.$this->strategy); + } + + if (null !== $this->blockSplitThreshold) { + $pb->add('-b'.$this->blockSplitThreshold); + } + + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_pngout')); + file_put_contents($input, $asset->getContent()); + + $output = tempnam(sys_get_temp_dir(), 'assetic_pngout'); + unlink($output); + $pb->add($output .= '.png'); + + $proc = $pb->getProcess(); + $code = $proc->run(); + + if (0 !== $code) { + unlink($input); + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent(file_get_contents($output)); + + unlink($input); + unlink($output); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/RooleFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/RooleFilter.php new file mode 100644 index 0000000..59585d3 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/RooleFilter.php @@ -0,0 +1,73 @@ + + */ +class RooleFilter extends BaseNodeFilter implements DependencyExtractorInterface +{ + private $rooleBin; + private $nodeBin; + + /** + * Constructor + * + * @param string $rooleBin The path to the roole binary + * @param string $nodeBin The path to the node binary + */ + public function __construct($rooleBin = '/usr/bin/roole', $nodeBin = null) + { + $this->rooleBin = $rooleBin; + $this->nodeBin = $nodeBin; + } + + public function filterLoad(AssetInterface $asset) + { + $input = tempnam(sys_get_temp_dir(), 'assetic_roole'); + file_put_contents($input, $asset->getContent()); + + $pb = $this->createProcessBuilder($this->nodeBin + ? array($this->nodeBin, $this->rooleBin) + : array($this->rooleBin)); + + $pb->add('-p'); + + $pb->add($input); + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 !== $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function filterDump(AssetInterface $asset) + { + } + + public function getChildren(AssetFactory $factory, $content, $loadPath = null) + { + // todo + return array(); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/SassFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/SassFilter.php new file mode 100644 index 0000000..24e618d --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/SassFilter.php @@ -0,0 +1,236 @@ + + */ +class SassFilter extends BaseProcessFilter implements DependencyExtractorInterface +{ + const STYLE_NESTED = 'nested'; + const STYLE_EXPANDED = 'expanded'; + const STYLE_COMPACT = 'compact'; + const STYLE_COMPRESSED = 'compressed'; + + private $sassPath; + private $rubyPath; + private $unixNewlines; + private $scss; + private $style; + private $quiet; + private $debugInfo; + private $lineNumbers; + private $loadPaths = array(); + private $cacheLocation; + private $noCache; + private $compass; + + public function __construct($sassPath = '/usr/bin/sass', $rubyPath = null) + { + $this->sassPath = $sassPath; + $this->rubyPath = $rubyPath; + $this->cacheLocation = realpath(sys_get_temp_dir()); + } + + public function setUnixNewlines($unixNewlines) + { + $this->unixNewlines = $unixNewlines; + } + + public function setScss($scss) + { + $this->scss = $scss; + } + + public function setStyle($style) + { + $this->style = $style; + } + + public function setQuiet($quiet) + { + $this->quiet = $quiet; + } + + public function setDebugInfo($debugInfo) + { + $this->debugInfo = $debugInfo; + } + + public function setLineNumbers($lineNumbers) + { + $this->lineNumbers = $lineNumbers; + } + + public function setLoadPaths(array $loadPaths) + { + $this->loadPaths = $loadPaths; + } + + public function addLoadPath($loadPath) + { + $this->loadPaths[] = $loadPath; + } + + public function setCacheLocation($cacheLocation) + { + $this->cacheLocation = $cacheLocation; + } + + public function setNoCache($noCache) + { + $this->noCache = $noCache; + } + + public function setCompass($compass) + { + $this->compass = $compass; + } + + public function filterLoad(AssetInterface $asset) + { + $sassProcessArgs = array($this->sassPath); + if (null !== $this->rubyPath) { + $sassProcessArgs = array_merge(explode(' ', $this->rubyPath), $sassProcessArgs); + } + + $pb = $this->createProcessBuilder($sassProcessArgs); + + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + if ($root && $path) { + $pb->add('--load-path')->add(dirname($root.'/'.$path)); + } + + if ($this->unixNewlines) { + $pb->add('--unix-newlines'); + } + + if (true === $this->scss || (null === $this->scss && 'scss' == pathinfo($path, PATHINFO_EXTENSION))) { + $pb->add('--scss'); + } + + if ($this->style) { + $pb->add('--style')->add($this->style); + } + + if ($this->quiet) { + $pb->add('--quiet'); + } + + if ($this->debugInfo) { + $pb->add('--debug-info'); + } + + if ($this->lineNumbers) { + $pb->add('--line-numbers'); + } + + foreach ($this->loadPaths as $loadPath) { + $pb->add('--load-path')->add($loadPath); + } + + if ($this->cacheLocation) { + $pb->add('--cache-location')->add($this->cacheLocation); + } + + if ($this->noCache) { + $pb->add('--no-cache'); + } + + if ($this->compass) { + $pb->add('--compass'); + } + + // input + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_sass')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 !== $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function filterDump(AssetInterface $asset) + { + } + + public function getChildren(AssetFactory $factory, $content, $loadPath = null) + { + $loadPaths = $this->loadPaths; + if ($loadPath) { + array_unshift($loadPaths, $loadPath); + } + + if (!$loadPaths) { + return array(); + } + + $children = array(); + foreach (CssUtils::extractImports($content) as $reference) { + if ('.css' === substr($reference, -4)) { + // skip normal css imports + // todo: skip imports with media queries + continue; + } + + // the reference may or may not have an extension or be a partial + if (pathinfo($reference, PATHINFO_EXTENSION)) { + $needles = array( + $reference, + '_'.$reference, + ); + } else { + $needles = array( + $reference.'.scss', + $reference.'.sass', + '_'.$reference.'.scss', + '_'.$reference.'.sass', + ); + } + + foreach ($loadPaths as $loadPath) { + foreach ($needles as $needle) { + if (file_exists($file = $loadPath.'/'.$needle)) { + $coll = $factory->createAsset($file, array(), array('root' => $loadPath)); + foreach ($coll as $leaf) { + $leaf->ensureFilter($this); + $children[] = $leaf; + goto next_reference; + } + } + } + } + + next_reference: + } + + return $children; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/ScssFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/ScssFilter.php new file mode 100644 index 0000000..3906bf5 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/ScssFilter.php @@ -0,0 +1,28 @@ + + */ +class ScssFilter extends SassFilter +{ + public function __construct($sassPath = '/usr/bin/sass', $rubyPath = null) + { + parent::__construct($sassPath, $rubyPath); + + $this->setScss(true); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/ScssphpFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/ScssphpFilter.php new file mode 100644 index 0000000..db5c068 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/ScssphpFilter.php @@ -0,0 +1,80 @@ + + */ +class ScssphpFilter implements DependencyExtractorInterface +{ + private $compass = false; + + private $importPaths = array(); + + public function enableCompass($enable = true) + { + $this->compass = (Boolean) $enable; + } + + public function isCompassEnabled() + { + return $this->compass; + } + + public function filterLoad(AssetInterface $asset) + { + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + $lc = new \scssc(); + if ($this->compass) { + new \scss_compass($lc); + } + if ($root && $path) { + $lc->addImportPath(dirname($root.'/'.$path)); + } + foreach ($this->importPaths as $path) { + $lc->addImportPath($path); + } + + $asset->setContent($lc->compile($asset->getContent())); + } + + public function setImportPaths(array $paths) + { + $this->importPaths = $paths; + } + + public function addImportPath($path) + { + $this->importPaths[] = $path; + } + + public function filterDump(AssetInterface $asset) + { + } + + public function getChildren(AssetFactory $factory, $content, $loadPath = null) + { + // todo + return array(); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/SprocketsFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/SprocketsFilter.php new file mode 100644 index 0000000..b305ad2 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/SprocketsFilter.php @@ -0,0 +1,154 @@ + + */ +class SprocketsFilter extends BaseProcessFilter implements DependencyExtractorInterface +{ + private $sprocketsLib; + private $rubyBin; + private $includeDirs; + private $assetRoot; + + /** + * Constructor. + * + * @param string $sprocketsLib Path to the Sprockets lib/ directory + * @param string $rubyBin Path to the ruby binary + */ + public function __construct($sprocketsLib = null, $rubyBin = '/usr/bin/ruby') + { + $this->sprocketsLib = $sprocketsLib; + $this->rubyBin = $rubyBin; + $this->includeDirs = array(); + } + + public function addIncludeDir($directory) + { + $this->includeDirs[] = $directory; + } + + public function setAssetRoot($assetRoot) + { + $this->assetRoot = $assetRoot; + } + + /** + * Hack around a bit, get the job done. + */ + public function filterLoad(AssetInterface $asset) + { + static $format = <<<'EOF' +#!/usr/bin/env ruby + +require %s +%s +options = { :load_path => [], + :source_files => [%s], + :expand_paths => false } + +%ssecretary = Sprockets::Secretary.new(options) +secretary.install_assets if options[:asset_root] +print secretary.concatenation + +EOF; + + $more = ''; + + foreach ($this->includeDirs as $directory) { + $more .= 'options[:load_path] << '.var_export($directory, true)."\n"; + } + + if (null !== $this->assetRoot) { + $more .= 'options[:asset_root] = '.var_export($this->assetRoot, true)."\n"; + } + + if ($more) { + $more .= "\n"; + } + + $tmpAsset = tempnam(sys_get_temp_dir(), 'assetic_sprockets'); + file_put_contents($tmpAsset, $asset->getContent()); + + $input = tempnam(sys_get_temp_dir(), 'assetic_sprockets'); + file_put_contents($input, sprintf($format, + $this->sprocketsLib + ? sprintf('File.join(%s, \'sprockets\')', var_export($this->sprocketsLib, true)) + : '\'sprockets\'', + $this->getHack($asset), + var_export($tmpAsset, true), + $more + )); + + $pb = $this->createProcessBuilder(array( + $this->rubyBin, + $input, + )); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($tmpAsset); + unlink($input); + + if (0 !== $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function filterDump(AssetInterface $asset) + { + } + + public function getChildren(AssetFactory $factory, $content, $loadPath = null) + { + // todo + return array(); + } + + private function getHack(AssetInterface $asset) + { + static $format = <<<'EOF' + +module Sprockets + class Preprocessor + protected + def pathname_for_relative_require_from(source_line) + Sprockets::Pathname.new(@environment, File.join(%s, location_from(source_line))) + end + end +end + +EOF; + + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + if ($root && $path) { + return sprintf($format, var_export(dirname($root.'/'.$path), true)); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/StylusFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/StylusFilter.php new file mode 100644 index 0000000..c7cc7b5 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/StylusFilter.php @@ -0,0 +1,116 @@ + + */ +class StylusFilter extends BaseNodeFilter implements DependencyExtractorInterface +{ + private $nodeBin; + private $compress; + + /** + * Constructs filter. + * + * @param string $nodeBin The path to the node binary + * @param array $nodePaths An array of node paths + */ + public function __construct($nodeBin = '/usr/bin/node', array $nodePaths = array()) + { + $this->nodeBin = $nodeBin; + $this->setNodePaths($nodePaths); + } + + /** + * Enable output compression. + * + * @param boolean $compress + */ + public function setCompress($compress) + { + $this->compress = $compress; + } + + /** + * {@inheritdoc} + */ + public function filterLoad(AssetInterface $asset) + { + static $format = <<<'EOF' +var stylus = require('stylus'); +var sys = require(process.binding('natives').util ? 'util' : 'sys'); + +stylus(%s, %s).render(function(e, css){ + if (e) { + throw e; + } + + sys.print(css); + process.exit(0); +}); + +EOF; + + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + // parser options + $parserOptions = array(); + if ($root && $path) { + $parserOptions['paths'] = array(dirname($root.'/'.$path)); + $parserOptions['filename'] = basename($path); + } + + if (null !== $this->compress) { + $parserOptions['compress'] = $this->compress; + } + + $pb = $this->createProcessBuilder(); + + $pb->add($this->nodeBin)->add($input = tempnam(sys_get_temp_dir(), 'assetic_stylus')); + file_put_contents($input, sprintf($format, + json_encode($asset->getContent()), + json_encode($parserOptions) + )); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 !== $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + /** + * {@inheritdoc} + */ + public function filterDump(AssetInterface $asset) + { + } + + public function getChildren(AssetFactory $factory, $content, $loadPath = null) + { + // todo + return array(); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/TypeScriptFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/TypeScriptFilter.php new file mode 100644 index 0000000..a2896d3 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/TypeScriptFilter.php @@ -0,0 +1,76 @@ + + */ +class TypeScriptFilter extends BaseNodeFilter +{ + private $tscBin; + private $nodeBin; + + public function __construct($tscBin = '/usr/bin/tsc', $nodeBin = null) + { + $this->tscBin = $tscBin; + $this->nodeBin = $nodeBin; + } + + public function filterLoad(AssetInterface $asset) + { + $pb = $this->createProcessBuilder($this->nodeBin + ? array($this->nodeBin, $this->tscBin) + : array($this->tscBin)); + + $templateName = basename($asset->getSourcePath()); + + $inputDirPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.uniqid('input_dir'); + $inputPath = $inputDirPath.DIRECTORY_SEPARATOR.$templateName.'.ts'; + $outputPath = tempnam(sys_get_temp_dir(), 'output'); + + mkdir($inputDirPath); + file_put_contents($inputPath, $asset->getContent()); + + $pb->add($inputPath)->add('--out')->add($outputPath); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($inputPath); + rmdir($inputDirPath); + + if (0 !== $code) { + if (file_exists($outputPath)) { + unlink($outputPath); + } + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + if (!file_exists($outputPath)) { + throw new \RuntimeException('Error creating output file.'); + } + + $compiledJs = file_get_contents($outputPath); + unlink($outputPath); + + $asset->setContent($compiledJs); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyCssFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyCssFilter.php new file mode 100644 index 0000000..3fd6c4a --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyCssFilter.php @@ -0,0 +1,119 @@ + + */ +class UglifyCssFilter extends BaseNodeFilter +{ + private $uglifycssBin; + private $nodeBin; + + private $expandVars; + private $uglyComments; + private $cuteComments; + + /** + * @param string $uglifycssBin Absolute path to the uglifycss executable + * @param string $nodeBin Absolute path to the folder containg node.js executable + */ + public function __construct($uglifycssBin = '/usr/bin/uglifycss', $nodeBin = null) + { + $this->uglifycssBin = $uglifycssBin; + $this->nodeBin = $nodeBin; + } + + /** + * Expand variables + * @param bool $expandVars True to enable + */ + public function setExpandVars($expandVars) + { + $this->expandVars = $expandVars; + } + + /** + * Remove newlines within preserved comments + * @param bool $uglyComments True to enable + */ + public function setUglyComments($uglyComments) + { + $this->uglyComments = $uglyComments; + } + + /** + * Preserve newlines within and around preserved comments + * @param bool $cuteComments True to enable + */ + public function setCuteComments($cuteComments) + { + $this->cuteComments = $cuteComments; + } + + /** + * @see Assetic\Filter\FilterInterface::filterLoad() + */ + public function filterLoad(AssetInterface $asset) + { + } + + /** + * Run the asset through UglifyJs + * + * @see Assetic\Filter\FilterInterface::filterDump() + */ + public function filterDump(AssetInterface $asset) + { + $pb = $this->createProcessBuilder($this->nodeBin + ? array($this->nodeBin, $this->uglifycssBin) + : array($this->uglifycssBin)); + + if ($this->expandVars) { + $pb->add('--expand-vars'); + } + + if ($this->uglyComments) { + $pb->add('--ugly-comments'); + } + + if ($this->cuteComments) { + $pb->add('--cute-comments'); + } + + // input and output files + $input = tempnam(sys_get_temp_dir(), 'input'); + + file_put_contents($input, $asset->getContent()); + $pb->add($input); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (127 === $code) { + throw new \RuntimeException('Path to node executable could not be resolved.'); + } + + if (0 !== $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJs2Filter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJs2Filter.php new file mode 100644 index 0000000..15d314e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJs2Filter.php @@ -0,0 +1,135 @@ + + */ +class UglifyJs2Filter extends BaseNodeFilter +{ + private $uglifyjsBin; + private $nodeBin; + private $compress; + private $beautify; + private $mangle; + private $screwIe8; + private $comments; + private $wrap; + + public function __construct($uglifyjsBin = '/usr/bin/uglifyjs', $nodeBin = null) + { + $this->uglifyjsBin = $uglifyjsBin; + $this->nodeBin = $nodeBin; + } + + public function setCompress($compress) + { + $this->compress = $compress; + } + + public function setBeautify($beautify) + { + $this->beautify = $beautify; + } + + public function setMangle($mangle) + { + $this->mangle = $mangle; + } + + public function setScrewIe8($screwIe8) + { + $this->screwIe8 = $screwIe8; + } + + public function setComments($comments) + { + $this->comments = $comments; + } + + public function setWrap($wrap) + { + $this->wrap = $wrap; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $pb = $this->createProcessBuilder($this->nodeBin + ? array($this->nodeBin, $this->uglifyjsBin) + : array($this->uglifyjsBin)); + + if ($this->compress) { + $pb->add('--compress'); + } + + if ($this->beautify) { + $pb->add('--beautify'); + } + + if ($this->mangle) { + $pb->add('--mangle'); + } + + if ($this->screwIe8) { + $pb->add('--screw-ie8'); + } + + if ($this->comments) { + $pb->add('--comments')->add(true === $this->comments ? 'all' : $this->comments); + } + + if ($this->wrap) { + $pb->add('--wrap')->add($this->wrap); + } + + // input and output files + $input = tempnam(sys_get_temp_dir(), 'input'); + $output = tempnam(sys_get_temp_dir(), 'output'); + + file_put_contents($input, $asset->getContent()); + $pb->add('-o')->add($output)->add($input); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 !== $code) { + if (file_exists($output)) { + unlink($output); + } + + if (127 === $code) { + throw new \RuntimeException('Path to node executable could not be resolved.'); + } + + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + if (!file_exists($output)) { + throw new \RuntimeException('Error creating output file.'); + } + + $asset->setContent(file_get_contents($output)); + + unlink($output); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJsFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJsFilter.php new file mode 100644 index 0000000..5a0ae33 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJsFilter.php @@ -0,0 +1,145 @@ + + */ +class UglifyJsFilter extends BaseNodeFilter +{ + private $uglifyjsBin; + private $nodeBin; + + private $noCopyright; + private $beautify; + private $unsafe; + private $mangle; + + /** + * @param string $uglifyjsBin Absolute path to the uglifyjs executable + * @param string $nodeBin Absolute path to the folder containg node.js executable + */ + public function __construct($uglifyjsBin = '/usr/bin/uglifyjs', $nodeBin = null) + { + $this->uglifyjsBin = $uglifyjsBin; + $this->nodeBin = $nodeBin; + } + + /** + * Removes the first block of comments as well + * @param bool $noCopyright True to enable + */ + public function setNoCopyright($noCopyright) + { + $this->noCopyright = $noCopyright; + } + + /** + * Output indented code + * @param bool $beautify True to enable + */ + public function setBeautify($beautify) + { + $this->beautify = $beautify; + } + + /** + * Enable additional optimizations that are known to be unsafe in some situations. + * @param bool $unsafe True to enable + */ + public function setUnsafe($unsafe) + { + $this->unsafe = $unsafe; + } + + /** + * Safely mangle variable and function names for greater file compress. + * @param bool $mangle True to enable + */ + public function setMangle($mangle) + { + $this->mangle = $mangle; + } + + /** + * @see Assetic\Filter\FilterInterface::filterLoad() + */ + public function filterLoad(AssetInterface $asset) + { + } + + /** + * Run the asset through UglifyJs + * + * @see Assetic\Filter\FilterInterface::filterDump() + */ + public function filterDump(AssetInterface $asset) + { + $pb = $this->createProcessBuilder($this->nodeBin + ? array($this->nodeBin, $this->uglifyjsBin) + : array($this->uglifyjsBin)); + + if ($this->noCopyright) { + $pb->add('--no-copyright'); + } + + if ($this->beautify) { + $pb->add('--beautify'); + } + + if ($this->unsafe) { + $pb->add('--unsafe'); + } + + if (false === $this->mangle) { + $pb->add('--no-mangle'); + } + + // input and output files + $input = tempnam(sys_get_temp_dir(), 'input'); + $output = tempnam(sys_get_temp_dir(), 'output'); + + file_put_contents($input, $asset->getContent()); + $pb->add('-o')->add($output)->add($input); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 !== $code) { + if (file_exists($output)) { + unlink($output); + } + + if (127 === $code) { + throw new \RuntimeException('Path to node executable could not be resolved.'); + } + + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + if (!file_exists($output)) { + throw new \RuntimeException('Error creating output file.'); + } + + $uglifiedJs = file_get_contents($output); + unlink($output); + + $asset->setContent($uglifiedJs); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/BaseCompressorFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/BaseCompressorFilter.php new file mode 100644 index 0000000..ba9c7bb --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/BaseCompressorFilter.php @@ -0,0 +1,116 @@ + + */ +abstract class BaseCompressorFilter extends BaseProcessFilter +{ + private $jarPath; + private $javaPath; + private $charset; + private $lineBreak; + private $stackSize; + + public function __construct($jarPath, $javaPath = '/usr/bin/java') + { + $this->jarPath = $jarPath; + $this->javaPath = $javaPath; + } + + public function setCharset($charset) + { + $this->charset = $charset; + } + + public function setLineBreak($lineBreak) + { + $this->lineBreak = $lineBreak; + } + + public function setStackSize($stackSize) + { + $this->stackSize = $stackSize; + } + + public function filterLoad(AssetInterface $asset) + { + } + + /** + * Compresses a string. + * + * @param string $content The content to compress + * @param string $type The type of content, either "js" or "css" + * @param array $options An indexed array of additional options + * + * @return string The compressed content + */ + protected function compress($content, $type, $options = array()) + { + $pb = $this->createProcessBuilder(array($this->javaPath)); + + if (null !== $this->stackSize) { + $pb->add('-Xss'.$this->stackSize); + } + + $pb->add('-jar')->add($this->jarPath); + + foreach ($options as $option) { + $pb->add($option); + } + + if (null !== $this->charset) { + $pb->add('--charset')->add($this->charset); + } + + if (null !== $this->lineBreak) { + $pb->add('--line-break')->add($this->lineBreak); + } + + // input and output files + $tempDir = realpath(sys_get_temp_dir()); + $input = tempnam($tempDir, 'YUI-IN-'); + $output = tempnam($tempDir, 'YUI-OUT-'); + file_put_contents($input, $content); + $pb->add('-o')->add($output)->add('--type')->add($type)->add($input); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 !== $code) { + if (file_exists($output)) { + unlink($output); + } + + throw FilterException::fromProcess($proc)->setInput($content); + } + + if (!file_exists($output)) { + throw new \RuntimeException('Error creating output file.'); + } + + $retval = file_get_contents($output); + unlink($output); + + return $retval; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/CssCompressorFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/CssCompressorFilter.php new file mode 100644 index 0000000..96d2739 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/CssCompressorFilter.php @@ -0,0 +1,28 @@ + + */ +class CssCompressorFilter extends BaseCompressorFilter +{ + public function filterDump(AssetInterface $asset) + { + $asset->setContent($this->compress($asset->getContent(), 'css')); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/JsCompressorFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/JsCompressorFilter.php new file mode 100644 index 0000000..2326f2e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/JsCompressorFilter.php @@ -0,0 +1,61 @@ + + */ +class JsCompressorFilter extends BaseCompressorFilter +{ + private $nomunge; + private $preserveSemi; + private $disableOptimizations; + + public function setNomunge($nomunge = true) + { + $this->nomunge = $nomunge; + } + + public function setPreserveSemi($preserveSemi) + { + $this->preserveSemi = $preserveSemi; + } + + public function setDisableOptimizations($disableOptimizations) + { + $this->disableOptimizations = $disableOptimizations; + } + + public function filterDump(AssetInterface $asset) + { + $options = array(); + + if ($this->nomunge) { + $options[] = '--nomunge'; + } + + if ($this->preserveSemi) { + $options[] = '--preserve-semi'; + } + + if ($this->disableOptimizations) { + $options[] = '--disable-optimizations'; + } + + $asset->setContent($this->compress($asset->getContent(), 'js', $options)); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/FilterManager.php b/vendor/kriswallsmith/assetic/src/Assetic/FilterManager.php new file mode 100644 index 0000000..48fe9fc --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/FilterManager.php @@ -0,0 +1,64 @@ + + */ +class FilterManager +{ + private $filters = array(); + + public function set($alias, FilterInterface $filter) + { + $this->checkName($alias); + + $this->filters[$alias] = $filter; + } + + public function get($alias) + { + if (!isset($this->filters[$alias])) { + throw new \InvalidArgumentException(sprintf('There is no "%s" filter.', $alias)); + } + + return $this->filters[$alias]; + } + + public function has($alias) + { + return isset($this->filters[$alias]); + } + + public function getNames() + { + return array_keys($this->filters); + } + + /** + * Checks that a name is valid. + * + * @param string $name An asset name candidate + * + * @throws \InvalidArgumentException If the asset name is invalid + */ + protected function checkName($name) + { + if (!ctype_alnum(str_replace('_', '', $name))) { + throw new \InvalidArgumentException(sprintf('The name "%s" is invalid.', $name)); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Util/CssUtils.php b/vendor/kriswallsmith/assetic/src/Assetic/Util/CssUtils.php new file mode 100644 index 0000000..00d658b --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Util/CssUtils.php @@ -0,0 +1,111 @@ + + */ +abstract class CssUtils +{ + const REGEX_URLS = '/url\((["\']?)(?P.*?)(\\1)\)/'; + const REGEX_IMPORTS = '/@import (?:url\()?(\'|"|)(?P[^\'"\)\n\r]*)\1\)?;?/'; + const REGEX_IMPORTS_NO_URLS = '/@import (?!url\()(\'|"|)(?P[^\'"\)\n\r]*)\1;?/'; + const REGEX_IE_FILTERS = '/src=(["\']?)(?P.*?)\\1/'; + + /** + * Filters all references -- url() and "@import" -- through a callable. + * + * @param string $content The CSS + * @param callable $callback A PHP callable + * @param integer $limit + * @param integer $count + * + * @return string The filtered CSS + */ + public static function filterReferences($content, $callback, $limit = -1, &$count = 0) + { + $content = static::filterUrls($content, $callback, $limit, $count); + $content = static::filterImports($content, $callback, $limit, $count, false); + $content = static::filterIEFilters($content, $callback, $limit, $count); + + return $content; + } + + /** + * Filters all CSS url()'s through a callable. + * + * @param string $content The CSS + * @param callable $callback A PHP callable + * @param integer $limit Limit the number of replacements + * @param integer $count Will be populated with the count + * + * @return string The filtered CSS + */ + public static function filterUrls($content, $callback, $limit = -1, &$count = 0) + { + return preg_replace_callback(static::REGEX_URLS, $callback, $content, $limit, $count); + } + + /** + * Filters all CSS imports through a callable. + * + * @param string $content The CSS + * @param callable $callback A PHP callable + * @param integer $limit Limit the number of replacements + * @param integer $count Will be populated with the count + * @param Boolean $includeUrl Whether to include url() in the pattern + * + * @return string The filtered CSS + */ + public static function filterImports($content, $callback, $limit = -1, &$count = 0, $includeUrl = true) + { + $pattern = $includeUrl ? static::REGEX_IMPORTS : static::REGEX_IMPORTS_NO_URLS; + + return preg_replace_callback($pattern, $callback, $content, $limit, $count); + } + + /** + * Filters all IE filters (AlphaImageLoader filter) through a callable. + * + * @param string $content The CSS + * @param callable $callback A PHP callable + * @param integer $limit Limit the number of replacements + * @param integer $count Will be populated with the count + * + * @return string The filtered CSS + */ + public static function filterIEFilters($content, $callback, $limit = -1, &$count = 0) + { + return preg_replace_callback(static::REGEX_IE_FILTERS, $callback, $content, $limit, $count); + } + + /** + * Extracts all references from the supplied CSS content. + * + * @param string $content The CSS content + * + * @return array An array of unique URLs + */ + public static function extractImports($content) + { + $imports = array(); + static::filterImports($content, function($matches) use(& $imports) { + $imports[] = $matches['url']; + }); + + return array_unique($imports); + } + + final private function __construct() { } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Util/LessUtils.php b/vendor/kriswallsmith/assetic/src/Assetic/Util/LessUtils.php new file mode 100644 index 0000000..291fb66 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Util/LessUtils.php @@ -0,0 +1,23 @@ + + */ +abstract class LessUtils extends CssUtils +{ + const REGEX_IMPORTS = '/@import(?:-once)? (?:url\()?(\'|"|)(?P[^\'"\)\n\r]*)\1\)?;?/'; + const REGEX_IMPORTS_NO_URLS = '/@import(?:-once)? (?!url\()(\'|"|)(?P[^\'"\)\n\r]*)\1;?/'; +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Util/TraversableString.php b/vendor/kriswallsmith/assetic/src/Assetic/Util/TraversableString.php new file mode 100644 index 0000000..4802a52 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Util/TraversableString.php @@ -0,0 +1,44 @@ + + */ +class TraversableString implements \IteratorAggregate, \Countable +{ + private $one; + private $many; + + public function __construct($one, array $many) + { + $this->one = $one; + $this->many = $many; + } + + public function getIterator() + { + return new \ArrayIterator($this->many); + } + + public function count() + { + return count($this->many); + } + + public function __toString() + { + return (string) $this->one; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Util/VarUtils.php b/vendor/kriswallsmith/assetic/src/Assetic/Util/VarUtils.php new file mode 100644 index 0000000..4cc9103 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Util/VarUtils.php @@ -0,0 +1,82 @@ + + */ +abstract class VarUtils +{ + /** + * Resolves variable placeholders. + * + * @param string $template A template string + * @param array $vars Variable names + * @param array $values Variable values + * + * @return string The resolved string + * + * @throws \InvalidArgumentException If there is a variable with no value + */ + public static function resolve($template, array $vars, array $values) + { + $map = array(); + foreach ($vars as $var) { + if (false === strpos($template, '{'.$var.'}')) { + continue; + } + + if (!isset($values[$var])) { + throw new \InvalidArgumentException(sprintf('The path "%s" contains the variable "%s", but was not given any value for it.', $template, $var)); + } + + $map['{'.$var.'}'] = $values[$var]; + } + + return strtr($template, $map); + } + + public static function getCombinations(array $vars, array $values) + { + if (!$vars) { + return array(array()); + } + + $combinations = array(); + $nbValues = array(); + foreach ($values as $var => $vals) { + if (!in_array($var, $vars, true)) { + continue; + } + + $nbValues[$var] = count($vals); + } + + for ($i = array_product($nbValues), $c = $i * 2; $i < $c; $i++) { + $k = $i; + $combination = array(); + + foreach ($vars as $var) { + $combination[$var] = $values[$var][$k % $nbValues[$var]]; + $k = intval($k / $nbValues[$var]); + } + + $combinations[] = $combination; + } + + return $combinations; + } + + final private function __construct() { } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/ValueSupplierInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/ValueSupplierInterface.php new file mode 100644 index 0000000..75c81a0 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/ValueSupplierInterface.php @@ -0,0 +1,29 @@ + + */ +interface ValueSupplierInterface +{ + /** + * Returns a map of values. + * + * @return array + */ + public function getValues(); +} diff --git a/vendor/kriswallsmith/assetic/src/functions.php b/vendor/kriswallsmith/assetic/src/functions.php new file mode 100644 index 0000000..75cfbf2 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/functions.php @@ -0,0 +1,121 @@ +factory = $factory; +} + +/** + * Returns an array of javascript URLs. + * + * @param array|string $inputs Input strings + * @param array|string $filters Filter names + * @param array $options An array of options + * + * @return array An array of javascript URLs + */ +function assetic_javascripts($inputs = array(), $filters = array(), array $options = array()) +{ + if (!isset($options['output'])) { + $options['output'] = 'js/*.js'; + } + + return _assetic_urls($inputs, $filters, $options); +} + +/** + * Returns an array of stylesheet URLs. + * + * @param array|string $inputs Input strings + * @param array|string $filters Filter names + * @param array $options An array of options + * + * @return array An array of stylesheet URLs + */ +function assetic_stylesheets($inputs = array(), $filters = array(), array $options = array()) +{ + if (!isset($options['output'])) { + $options['output'] = 'css/*.css'; + } + + return _assetic_urls($inputs, $filters, $options); +} + +/** + * Returns an image URL. + * + * @param string $input An input + * @param array|string $filters Filter names + * @param array $options An array of options + * + * @return string An image URL + */ +function assetic_image($input, $filters = array(), array $options = array()) +{ + if (!isset($options['output'])) { + $options['output'] = 'images/*'; + } + + $urls = _assetic_urls($input, $filters, $options); + + return current($urls); +} + +/** + * Returns an array of asset urls. + * + * @param array|string $inputs Input strings + * @param array|string $filters Filter names + * @param array $options An array of options + * + * @return array An array of URLs + */ +function _assetic_urls($inputs = array(), $filters = array(), array $options = array()) +{ + global $_assetic; + + if (!is_array($inputs)) { + $inputs = array_filter(array_map('trim', explode(',', $inputs))); + } + + if (!is_array($filters)) { + $filters = array_filter(array_map('trim', explode(',', $filters))); + } + + $coll = $_assetic->factory->createAsset($inputs, $filters, $options); + + $debug = isset($options['debug']) ? $options['debug'] : $_assetic->factory->isDebug(); + $combine = isset($options['combine']) ? $options['combine'] : !$debug; + + $one = $coll->getTargetPath(); + if ($combine) { + $many = array($one); + } else { + $many = array(); + foreach ($coll as $leaf) { + $many[] = $leaf->getTargetPath(); + } + } + + return new TraversableString($one, $many); +} diff --git a/vendor/monolog/monolog/CHANGELOG.mdown b/vendor/monolog/monolog/CHANGELOG.mdown new file mode 100644 index 0000000..2710fdc --- /dev/null +++ b/vendor/monolog/monolog/CHANGELOG.mdown @@ -0,0 +1,92 @@ +### 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..5df1c39 --- /dev/null +++ b/vendor/monolog/monolog/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 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..4c1b21e --- /dev/null +++ b/vendor/monolog/monolog/README.mdown @@ -0,0 +1,230 @@ +Monolog - Logging for PHP 5.3+ [![Build Status](https://secure.travis-ci.org/Seldaek/monolog.png)](http://travis-ci.org/Seldaek/monolog) +============================== + +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. + +Usage +----- + +```php +pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING)); + +// add records to the log +$log->addWarning('Foo'); +$log->addError('Bar'); +``` + +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 handled fully 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 true means that all records will propagate to the +`StreamHandler`, even the errors that are handled by the `MailHandler`. + +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 all 8 logging levels defined in +[RFC 5424](http://tools.ietf.org/html/rfc5424), but unless you specifically +need syslog compatibility, it is advised to only use DEBUG, INFO, WARNING, +ERROR, CRITICAL, ALERT. + +- **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. + +Docs +==== + +**See the `doc` directory for more detailed documentation. +The following is only a list of all parts that come with Monolog.** + +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. + +### Send alerts and emails + +- _NativeMailHandler_: 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. + +### 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](https://github.com/Seldaek/monolog/blob/master/doc/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. + +### 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. + +### 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. + +### 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. +- _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. +- _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. +- _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. +- _NormalizerFormatter_: Normalizes objects/resources down to strings so a record can easily be serialized/encoded. +- _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. +- _GelfFormatter_: 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/1.1.5/). + +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. + +About +===== + +Requirements +------------ + +- Any flavor of PHP 5.3 or above should do +- [optional] PHPUnit 3.5+ to execute the test suite (phpunit --version) + +Submitting bugs and feature requests +------------------------------------ + +Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/monolog/issues) + +Frameworks Integration +---------------------- + +- 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. +- [Laravel4](http://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. + +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..9453f38 --- /dev/null +++ b/vendor/monolog/monolog/composer.json @@ -0,0 +1,39 @@ +{ + "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": { + "mlehner/gelf-php": "1.0.*", + "raven/raven": "0.3.*", + "doctrine/couchdb": "dev-master" + }, + "suggest": { + "mlehner/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", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server" + }, + "autoload": { + "psr-0": {"Monolog": "src/"} + }, + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + } +} diff --git a/vendor/monolog/monolog/doc/extending.md b/vendor/monolog/monolog/doc/extending.md new file mode 100644 index 0000000..fcd7af2 --- /dev/null +++ b/vendor/monolog/monolog/doc/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']``). diff --git a/vendor/monolog/monolog/doc/sockets.md b/vendor/monolog/monolog/doc/sockets.md new file mode 100644 index 0000000..fad30a9 --- /dev/null +++ b/vendor/monolog/monolog/doc/sockets.md @@ -0,0 +1,37 @@ +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. + +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/doc/usage.md b/vendor/monolog/monolog/doc/usage.md new file mode 100644 index 0000000..07efa78 --- /dev/null +++ b/vendor/monolog/monolog/doc/usage.md @@ -0,0 +1,158 @@ +Using Monolog +============= + +Installation +------------ + +Monolog is available on Packagist ([monolog/monolog](http://packagist.org/packages/monolog/monolog)) +and as such installable via [Composer](http://getcomposer.org/). + +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. + +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. + +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 README file 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 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. 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/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..aa01f49 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.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 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); + $message = new Message(); + $message + ->setTimestamp($record['datetime']) + ->setShortMessage((string) $record['message']) + ->setFacility($record['channel']) + ->setHost($this->systemName) + ->setLine(isset($record['extra']['line']) ? $record['extra']['line'] : null) + ->setFile(isset($record['extra']['file']) ? $record['extra']['file'] : null) + ->setLevel($this->logLevels[$record['level']]); + + // Do not duplicate these values in the additional fields + unset($record['extra']['line']); + 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)); + } + + return $message; + } +} 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..822af0e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.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 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 +{ + /** + * {@inheritdoc} + */ + public function format(array $record) + { + return json_encode($record); + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + return json_encode($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..a96fb27 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.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\Formatter; + +/** + * 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; + + /** + * @param string $format The format of the message + * @param string $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct($format = null, $dateFormat = null) + { + $this->format = $format ?: static::SIMPLE_FORMAT; + parent::__construct($dateFormat); + } + + /** + * {@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->convertToString($val), $output); + unset($vars['extra'][$var]); + } + } + foreach ($vars as $var => $val) { + $output = str_replace('%'.$var.'%', $this->convertToString($val), $output); + } + + return $output; + } + + public function formatBatch(array $records) + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } + + protected function normalize($data) + { + if (is_bool($data) || is_null($data)) { + return var_export($data, true); + } + + if ($data instanceof \Exception) { + $previousText = ''; + if ($previous = $data->getPrevious()) { + do { + $previousText .= ', '.get_class($previous).': '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine(); + } while ($previous = $previous->getPrevious()); + } + + return '[object] ('.get_class($data).': '.$data->getMessage().' at '.$data->getFile().':'.$data->getLine().$previousText.')'; + } + + return parent::normalize($data); + } + + 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 $this->toJson($data); + } + + return str_replace('\\/', '/', json_encode($data)); + } +} 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..7aa8ad3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.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\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 +{ + /** + * @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; + + /** + * @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_ + */ + public function __construct($applicationName, $systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_') + { + //log stash requires a ISO 8601 format date + parent::__construct('c'); + + $this->systemName = $systemName ?: gethostname(); + $this->applicationName = $applicationName; + + $this->extraPrefix = $extraPrefix; + $this->contextPrefix = $contextPrefix; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $record = parent::format($record); + $message = array( + '@timestamp' => $record['datetime'], + '@message' => $record['message'], + '@tags' => array($record['channel']), + '@source' => $this->systemName + ); + + if ($this->applicationName) { + $message['@type'] = $this->applicationName; + } + $message['@fields'] = array(); + $message['@fields']['channel'] = $record['channel']; + $message['@fields']['level'] = $record['level']; + + if (isset($record['extra']['server'])) { + $message['@source_host'] = $record['extra']['server']; + } + if (isset($record['extra']['url'])) { + $message['@source_path'] = $record['extra']['url']; + } + foreach ($record['extra'] as $key => $val) { + $message['@fields'][$this->extraPrefix . $key] = $val; + } + + foreach ($record['context'] as $key => $val) { + $message['@fields'][$this->contextPrefix . $key] = $val; + } + + return json_encode($message) . "\n"; + } +} 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..c8b05fb --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.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 Monolog\Formatter; + +/** + * 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; + } + + /** + * {@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)) { + return $data; + } + + if (is_array($data) || $data instanceof \Traversable) { + $normalized = array(); + + foreach ($data as $key => $value) { + $normalized[$key] = $this->normalize($value); + } + + return $normalized; + } + + if ($data instanceof \DateTime) { + return $data->format($this->dateFormat); + } + + if (is_object($data)) { + return sprintf("[object] (%s: %s)", get_class($data), $this->toJson($data, true)); + } + + if (is_resource($data)) { + return '[resource]'; + } + + return '[unknown('.gettype($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', '>=')) { + return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + return json_encode($data); + } +} 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..b3e9b18 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.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\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 +{ + /** + * 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); + } + + // Create JSON object describing the appearance of the message in the console + $json = $this->toJson(array( + array( + 'Type' => $this->logLevels[$record['level']], + 'File' => $file, + 'Line' => $line, + 'Label' => $record['channel'], + ), + $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..2ea9f55 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php @@ -0,0 +1,174 @@ + + * + * 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 = false; + + /** + * @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->level = $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); + } + + /** + * {@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; + } + + /** + * {@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 + */ + public function setLevel($level) + { + $this->level = $level; + } + + /** + * 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 bubbling is not permitted. + * False means that this handler allows bubbling. + */ + public function setBubble($bubble) + { + $this->bubble = $bubble; + } + + /** + * Gets the bubbling behavior. + * + * @return Boolean True means that bubbling is not permitted. + * False means that this handler allows bubbling. + */ + 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..e1e5b89 --- /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 ($record['level'] < $this->level) { + 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/AmqpHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php new file mode 100644 index 0000000..0070343 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.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; +use Monolog\Formatter\JsonFormatter; + +class AmqpHandler extends AbstractProcessingHandler +{ + /** + * @var \AMQPExchange $exchange + */ + protected $exchange; + + /** + * @param \AMQPExchange $exchange AMQP exchange, 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(\AMQPExchange $exchange, $exchangeName = 'log', $level = Logger::DEBUG, $bubble = true) + { + $this->exchange = $exchange; + $this->exchange->setName($exchangeName); + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + $data = $record["formatted"]; + + $routingKey = sprintf( + '%s.%s', + substr($record['level_name'], 0, 4), + $record['channel'] + ); + + $this->exchange->publish( + $data, + strtolower($routingKey), + 0, + array( + 'delivery_mode' => 2, + 'Content-type' => 'application/json' + ) + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new JsonFormatter(); + } +} 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..e9a4dc3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.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; + +/** + * 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(); + + /** + * @param HandlerInterface $handler Handler. + * @param integer $bufferSize 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, $bufferSize = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false) + { + parent::__construct($level, $bubble); + $this->handler = $handler; + $this->bufferLimit = (int) $bufferSize; + $this->flushOnOverflow = $flushOnOverflow; + + // __destructor() doesn't get called on Fatal errors + register_shutdown_function(array($this, 'close')); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + + 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->bufferSize = 0; + $this->buffer = array(); + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->flush(); + } +} 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..91b8f8a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.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 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 = '3.0'; + + /** + * Header name + */ + const HEADER_NAME = 'X-ChromePhp-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; + + /** + * {@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) { + return; + } + + if (!self::$initialized) { + self::$sendHeaders = $this->headersAccepted(); + self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; + + self::$initialized = true; + } + + $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)); + } + + $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() + { + return !isset($_SERVER['HTTP_USER_AGENT']) + || 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..4877b34 --- /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(); + } +} 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..6ccff26 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.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 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 \LogicException('The sockets extension is needed 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.u')); + 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']; + + $this->{'write'.$this->scheme}(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.']')) + ); + + return curl_exec($this->httpConnection); + } +} 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/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/ErrorLevelActivationStrategy.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php new file mode 100644 index 0000000..7cd8ef1 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.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 Monolog\Handler\FingersCrossed; + +/** + * Error level based activation strategy. + * + * @author Johannes M. Schmitt + */ +class ErrorLevelActivationStrategy implements ActivationStrategyInterface +{ + private $actionLevel; + + public function __construct($actionLevel) + { + $this->actionLevel = $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..5ac6d77 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.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\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. + * + * @author Jordi Boggiano + */ +class FingersCrossedHandler extends AbstractHandler +{ + protected $handler; + protected $activationStrategy; + protected $buffering = true; + protected $bufferSize; + protected $buffer = array(); + protected $stopBuffering; + + /** + * @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) + */ + public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true) + { + if (null === $activationStrategy) { + $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); + } + if (!$activationStrategy instanceof ActivationStrategyInterface) { + $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); + } + + $this->handler = $handler; + $this->activationStrategy = $activationStrategy; + $this->bufferSize = $bufferSize; + $this->bubble = $bubble; + $this->stopBuffering = $stopBuffering; + } + + /** + * {@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) { + if (!is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + $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; + } + + /** + * Resets the state of the handler. Stops forwarding records to the wrapped handler. + */ + public function reset() + { + $this->buffering = true; + } +} 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..46a039a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.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\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) + { + // WildFire-specific headers must be sent prior to any messages + if (!self::$initialized) { + self::$sendHeaders = $this->headersAccepted(); + + foreach ($this->getInitHeaders() as $header => $content) { + $this->sendHeader($header, $content); + } + + self::$initialized = true; + } + + $header = $this->createRecordHeader($record); + $this->sendHeader(key($header), current($header)); + } + + /** + * Verifies if the headers are accepted by the current user agent + * + * @return Boolean + */ + protected function headersAccepted() + { + return !isset($_SERVER['HTTP_USER_AGENT']) + || preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT']) + || 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/GelfHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php new file mode 100644 index 0000000..34d48e7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.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; + +use Gelf\IMessagePublisher; +use Monolog\Logger; +use Monolog\Handler\AbstractProcessingHandler; +use Monolog\Formatter\GelfMessageFormatter; + +/** + * Handler to send messages to a Graylog2 (http://www.graylog2.org) server + * + * @author Matt Lehner + */ +class GelfHandler extends AbstractProcessingHandler +{ + /** + * @var Gelf\IMessagePublisher the publisher object that sends the message to the server + */ + protected $publisher; + + /** + * @param Gelf\IMessagePublisher $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(IMessagePublisher $publisher, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + $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..ac15d7d --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.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\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 + * + * @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 + */ + 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 + */ + public function setFormatter(FormatterInterface $formatter); + + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter(); +} 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..8629272 --- /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 + * @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/MissingExtensionException.php b/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php new file mode 100644 index 0000000..0cb21cd --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.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 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..5a59201 --- /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 +{ + private $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..c7ac63a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.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; + +/** + * NativeMailerHandler uses the mail() function to send the emails + * + * @author Christophe Coevoet + */ +class NativeMailerHandler extends MailHandler +{ + protected $to; + protected $subject; + protected $headers = array( + 'Content-type: text/plain; charset=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 + */ + public function __construct($to, $subject, $from, $level = Logger::ERROR, $bubble = true) + { + parent::__construct($level, $bubble); + $this->to = is_array($to) ? $to : array($to); + $this->subject = $subject; + $this->addHeader(sprintf('From: %s', $from)); + } + + /** + * @param string|array $headers Custom added headers + */ + 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; + } + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + $content = wordwrap($content, 70); + $headers = implode("\r\n", $this->headers) . "\r\n"; + foreach ($this->to as $to) { + mail($to, $this->subject, $content, $headers); + } + } +} 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/PushoverHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php new file mode 100644 index 0000000..0681839 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.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; + +/** + * 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 $user; + private $title; + + /** + * @param string $token Pushover api token + * @param string $user Pushover user id the message will be sent to + * @param string $title Title sent to 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. + */ + public function __construct($token, $user, $title = null, $level = Logger::CRITICAL, $bubble = true, $useSSL = true) + { + $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80'; + parent::__construct($connectionString, $level, $bubble); + + $this->token = $token; + $this->user = $user; + $this->title = $title ?: gethostname(); + } + + 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 = substr($record['message'], 0, $maxMessageLength); + $timestamp = $record['datetime']->getTimestamp(); + + $dataArray = array( + 'token' => $this->token, + 'user' => $this->user, + 'message' => $message, + 'title' => $this->title, + 'timestamp' => $timestamp + ); + + 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; + } + + public function write(array $record) + { + parent::write($record); + $this->closeSocket(); + } +} 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..a9ea4fa --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.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\Formatter\LineFormatter; +use Monolog\Logger; +use Monolog\Handler\AbstractProcessingHandler; +use Raven_Client; + +/** + * Handler to send messages to a Sentry (https://github.com/dcramer/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; + + /** + * @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} + */ + protected function write(array $record) + { + $level = $this->logLevels[$record['level']]; + + $options = array(); + $options['level'] = $level; + if (!empty($record['context'])) { + $options['extra']['context'] = $record['context']; + } + if (!empty($record['extra'])) { + $options['extra']['extra'] = $record['extra']; + } + + $this->ravenClient->captureMessage( + $record['formatted'], + array(), // $params - not used + version_compare(Raven_Client::VERSION, '0.1.0', '>') ? $options : $level, // $level or $options + false // $stack + ); + if ($record['level'] >= Logger::ERROR && isset($record['context']['exception'])) { + $this->ravenClient->captureException($record['context']['exception']); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter('[%channel%] %message%'); + } +} 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..51a8e7d --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +/** + * 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; + + # redis instance, key to use + public function __construct($redis, $key, $level = Logger::DEBUG, $bubble = true) + { + if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { + throw new \InvalidArgumentException('Predis\Client or Redis instance required'); + } + + $this->redisClient = $redis; + $this->redisKey = $key; + + parent::__construct($level, $bubble); + } + + protected function write(array $record) + { + $this->redisClient->rpush($this->redisKey, $record["formatted"]); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter(); + } +} 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..cfb0d5a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.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\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; + + /** + * @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 + */ + public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true) + { + $this->filename = $filename; + $this->maxFiles = (int) $maxFiles; + $this->nextRotation = new \DateTime('tomorrow'); + + parent::__construct($this->getTimedFilename(), $level, $bubble); + } + + /** + * {@inheritdoc} + */ + public function close() + { + parent::close(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + + /** + * {@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; + } + + $fileInfo = pathinfo($this->filename); + $glob = $fileInfo['dirname'].'/'.$fileInfo['filename'].'-*'; + if (!empty($fileInfo['extension'])) { + $glob .= '.'.$fileInfo['extension']; + } + $iterator = new \GlobIterator($glob); + $count = $iterator->count(); + if ($this->maxFiles >= $count) { + // no files to remove + return; + } + + // Sorting the files by name to remove the older ones + $array = iterator_to_array($iterator); + usort($array, function($a, $b) { + return strcmp($b->getFilename(), $a->getFilename()); + }); + + foreach (array_slice($array, $this->maxFiles) as $file) { + if ($file->isWritable()) { + unlink($file->getRealPath()); + } + } + } + + protected function getTimedFilename() + { + $fileInfo = pathinfo($this->filename); + $timedFilename = $fileInfo['dirname'].'/'.$fileInfo['filename'].'-'.date('Y-m-d'); + if (!empty($fileInfo['extension'])) { + $timedFilename .= '.'.$fileInfo['extension']; + } + + return $timedFilename; + } +} 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..4faa327 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.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 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 + */ + public 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 type $boolean + */ + public function setPersistent($boolean) + { + $this->persistent = (boolean) $boolean; + } + + /** + * 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..96ce7fc --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.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 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; + + /** + * @param 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 + */ + public function __construct($stream, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + if (is_resource($stream)) { + $this->stream = $stream; + } else { + $this->url = $stream; + } + } + + /** + * {@inheritdoc} + */ + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->stream = null; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + if (null === $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().'); + } + $errorMessage = null; + set_error_handler(function ($code, $msg) use (&$errorMessage) { + $errorMessage = preg_replace('{^fopen\(.*?\): }', '', $msg); + }); + $this->stream = fopen($this->url, 'a'); + restore_error_handler(); + if (!is_resource($this->stream)) { + $this->stream = null; + throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$errorMessage, $this->url)); + } + } + fwrite($this->stream, (string) $record['formatted']); + } +} 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..ca03cca --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.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; + +/** + * SwiftMailerHandler uses Swift_Mailer to send the emails + * + * @author Gyula Sallai + */ +class SwiftMailerHandler extends MailHandler +{ + protected $mailer; + protected $message; + + /** + * @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; + 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; + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + $message = clone $this->message; + $message->setBody($content); + + $this->mailer->send($message); + } +} 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..c4856cf --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +/** + * 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 AbstractProcessingHandler +{ + /** + * Translates Monolog log levels to syslog log priorities. + */ + private $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. + */ + private $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 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($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'); + } + + if (!openlog($ident, $logopts, $facility)) { + throw new \LogicException('Can\'t open syslog for ident "'.$ident.'" and facility "'.$facility.'"'); + } + } + + /** + * {@inheritdoc} + */ + public function close() + { + closelog(); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + syslog($this->logLevels[$record['level']], (string) $record['formatted']); + } + + /** + * {@inheritdoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%'); + } +} 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..085d9e1 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.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; + +/** + * Used for testing purposes. + * + * It records all records and gives you access to them for verification. + * + * @author Jordi Boggiano + */ +class TestHandler extends AbstractProcessingHandler +{ + protected $records = array(); + protected $recordsByLevel = array(); + + public function getRecords() + { + return $this->records; + } + + public function hasEmergency($record) + { + return $this->hasRecord($record, Logger::EMERGENCY); + } + + public function hasAlert($record) + { + return $this->hasRecord($record, Logger::ALERT); + } + + public function hasCritical($record) + { + return $this->hasRecord($record, Logger::CRITICAL); + } + + public function hasError($record) + { + return $this->hasRecord($record, Logger::ERROR); + } + + public function hasWarning($record) + { + return $this->hasRecord($record, Logger::WARNING); + } + + public function hasNotice($record) + { + return $this->hasRecord($record, Logger::NOTICE); + } + + public function hasInfo($record) + { + return $this->hasRecord($record, Logger::INFO); + } + + public function hasDebug($record) + { + return $this->hasRecord($record, Logger::DEBUG); + } + + public function hasEmergencyRecords() + { + return isset($this->recordsByLevel[Logger::EMERGENCY]); + } + + public function hasAlertRecords() + { + return isset($this->recordsByLevel[Logger::ALERT]); + } + + public function hasCriticalRecords() + { + return isset($this->recordsByLevel[Logger::CRITICAL]); + } + + public function hasErrorRecords() + { + return isset($this->recordsByLevel[Logger::ERROR]); + } + + public function hasWarningRecords() + { + return isset($this->recordsByLevel[Logger::WARNING]); + } + + public function hasNoticeRecords() + { + return isset($this->recordsByLevel[Logger::NOTICE]); + } + + public function hasInfoRecords() + { + return isset($this->recordsByLevel[Logger::INFO]); + } + + public function hasDebugRecords() + { + return isset($this->recordsByLevel[Logger::DEBUG]); + } + + protected function hasRecord($record, $level) + { + if (!isset($this->recordsByLevel[$level])) { + return false; + } + + if (is_array($record)) { + $record = $record['message']; + } + + foreach ($this->recordsByLevel[$level] as $rec) { + if ($rec['message'] === $record) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } +} 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..0d58e78 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Logger.php @@ -0,0 +1,564 @@ + + * + * 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; + + 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; + + protected $name; + + /** + * The handler stack + * + * @var array of Monolog\Handler\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 array of callables + */ + protected $processors; + + /** + * @param string $name The logging channel + * @param array $handlers Optional stack of handlers, the first one in the array is called first, etc. + * @param array $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 + */ + public function pushHandler(HandlerInterface $handler) + { + array_unshift($this->handlers, $handler); + } + + /** + * 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); + } + + /** + * Adds a processor on to the stack. + * + * @param callable $callback + */ + 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); + } + + /** + * 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); + } + + /** + * 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)); + } + + if (!static::$timezone) { + static::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC'); + } + + $record = array( + 'message' => (string) $message, + 'context' => $context, + 'level' => $level, + 'level_name' => static::getLevelName($level), + 'channel' => $this->name, + 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), static::$timezone)->setTimezone(static::$timezone), + 'extra' => array(), + ); + // check if any handler will handle this message + $handlerKey = null; + foreach ($this->handlers as $key => $handler) { + if ($handler->isHandling($record)) { + $handlerKey = $key; + break; + } + } + // none found + if (null === $handlerKey) { + return false; + } + + // found at least one, process message and dispatch it + 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]; + } + + /** + * 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()) + { + if (is_string($level) && defined(__CLASS__.'::'.strtoupper($level))) { + $level = constant(__CLASS__.'::'.strtoupper($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 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 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); + } +} 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..b126218 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.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 Monolog\Processor; + +/** + * 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 +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $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 (isset($trace[$i]['class']) && false !== strpos($trace[$i]['class'], 'Monolog\\')) { + $i++; + } + + // 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; + } +} 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..e48672b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.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 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 = self::formatBytes($bytes); + + $record['extra'] = array_merge( + $record['extra'], + array( + '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..7551043 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.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 Monolog\Processor; + +/** + * Some methods that are common for all memory processors + * + * @author Rob Jensen + */ +abstract class MemoryProcessor +{ + protected $realUsage; + + /** + * @param boolean $realUsage + */ + public function __construct($realUsage = true) + { + $this->realUsage = (boolean) $realUsage; + } + + /** + * Formats bytes into a human readable string + * + * @param int $bytes + * @return string + */ + protected static function formatBytes($bytes) + { + $bytes = (int) $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..2c4a807 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.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 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 = self::formatBytes($bytes); + + $record['extra'] = array_merge( + $record['extra'], + array( + '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..9bd5e5e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.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 Monolog\Processor; + +/** + * Adds value of getmypid into records + * + * @author Andreas Hörnicke + */ +class ProcessIdProcessor +{ + private static $pid; + + public function __construct() + { + if (null === self::$pid) { + self::$pid = getmypid(); + } + } + + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $record['extra']['process_id'] = self::$pid; + + 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..b63fccc --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.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 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) { + $replacements['{'.$key.'}'] = $val; + } + + $record['message'] = strtr($record['message'], $replacements); + + 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..80270d0 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.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 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; + } +} 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..9916cc0 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.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 Monolog\Processor; + +/** + * Injects url/method and remote IP of the current web request in all records + * + * @author Jordi Boggiano + */ +class WebProcessor +{ + protected $serverData; + + /** + * @param mixed $serverData array or object w/ ArrayAccess that provides access to the $_SERVER data + */ + public function __construct($serverData = 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.'); + } + } + + /** + * @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'] = array_merge( + $record['extra'], + array( + 'url' => $this->serverData['REQUEST_URI'], + 'ip' => isset($this->serverData['REMOTE_ADDR']) ? $this->serverData['REMOTE_ADDR'] : null, + 'http_method' => isset($this->serverData['REQUEST_METHOD']) ? $this->serverData['REQUEST_METHOD'] : null, + 'server' => isset($this->serverData['SERVER_NAME']) ? $this->serverData['SERVER_NAME'] : null, + 'referrer' => isset($this->serverData['HTTP_REFERER']) ? $this->serverData['HTTP_REFERER'] : null, + ) + ); + + return $record; + } +} 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/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php new file mode 100644 index 0000000..bb96d96 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php @@ -0,0 +1,231 @@ + + * + * 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\Process\Process; +use Symfony\Component\Process\PhpExecutableFinder; +use Composer\Script\CommandEvent; + +/** + * @author Jordi Boggiano + */ +class ScriptHandler +{ + /** + * 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 = self::getOptions($event); + $appDir = $options['symfony-app-dir']; + + if (!is_dir($appDir)) { + echo 'The symfony-app-dir ('.$appDir.') specified in composer.json was not found in '.getcwd().', can not build bootstrap file.'.PHP_EOL; + + return; + } + + static::executeBuildBootstrap($appDir, $options['process-timeout']); + } + + /** + * Clears the Symfony cache. + * + * @param $event CommandEvent A instance + */ + public static function clearCache(CommandEvent $event) + { + $options = self::getOptions($event); + $appDir = $options['symfony-app-dir']; + + if (!is_dir($appDir)) { + echo 'The symfony-app-dir ('.$appDir.') specified in composer.json was not found in '.getcwd().', can not clear the cache.'.PHP_EOL; + + return; + } + + static::executeCommand($event, $appDir, 'cache:clear --no-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 = self::getOptions($event); + $appDir = $options['symfony-app-dir']; + $webDir = $options['symfony-web-dir']; + + $symlink = ''; + if ($options['symfony-assets-install'] == 'symlink') { + $symlink = '--symlink '; + } elseif ($options['symfony-assets-install'] == 'relative') { + $symlink = '--symlink --relative '; + } + + if (!is_dir($webDir)) { + echo 'The symfony-web-dir ('.$webDir.') specified in composer.json was not found in '.getcwd().', can not install assets.'.PHP_EOL; + + return; + } + + static::executeCommand($event, $appDir, 'assets:install '.$symlink.escapeshellarg($webDir)); + } + + /** + * Updated the requirements file. + * + * @param $event CommandEvent A instance + */ + public static function installRequirementsFile(CommandEvent $event) + { + $options = self::getOptions($event); + $appDir = $options['symfony-app-dir']; + + if (!is_dir($appDir)) { + echo 'The symfony-app-dir ('.$appDir.') specified in composer.json was not found in '.getcwd().', can not install the requirements file.'.PHP_EOL; + + return; + } + + copy(__DIR__.'/../Resources/skeleton/app/SymfonyRequirements.php', $appDir.'/SymfonyRequirements.php'); + copy(__DIR__.'/../Resources/skeleton/app/check.php', $appDir.'/check.php'); + + $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 (is_file($webDir.'/config.php')) { + copy(__DIR__.'/../Resources/skeleton/web/config.php', $webDir.'/config.php'); + } + } + + public static function doBuildBootstrap($appDir) + { + $file = $appDir.'/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', + // Cannot be included because annotations will parse the big compiled class file + //'Symfony\\Component\\DependencyInjection\\ContainerAware', + '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 ($autoloaders[0][0]->findFile('Symfony\\Bundle\\FrameworkBundle\\HttpKernel')) { + $classes[] = 'Symfony\\Bundle\\FrameworkBundle\\HttpKernel'; + } else { + $classes[] = 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel'; + } + + ClassCollectionLoader::load($classes, dirname($file), basename($file, '.php.cache'), false, false, '.php.cache'); + + file_put_contents($file, sprintf("getIO()->isDecorated()) { + $console .= ' --ansi'; + } + + $process = new Process($php.' '.$console.' '.$cmd, null, null, null, $timeout); + $process->run(function ($type, $buffer) { echo $buffer; }); + if (!$process->isSuccessful()) { + throw new \RuntimeException(sprintf('An error occurred when executing the "%s" command.', escapeshellarg($cmd))); + } + } + + protected static function executeBuildBootstrap($appDir, $timeout = 300) + { + $php = escapeshellarg(self::getPhp()); + $cmd = escapeshellarg(__DIR__.'/../Resources/bin/build_bootstrap.php'); + $appDir = escapeshellarg($appDir); + + $process = new Process($php.' '.$cmd.' '.$appDir, null, null, null, $timeout); + $process->run(function ($type, $buffer) { echo $buffer; }); + if (!$process->isSuccessful()) { + throw new \RuntimeException('An error occurred when generating the bootstrap file.'); + } + } + + protected static function getOptions(CommandEvent $event) + { + $options = array_merge(array( + 'symfony-app-dir' => 'app', + 'symfony-web-dir' => 'web', + 'symfony-assets-install' => 'hard' + ), $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() + { + $phpFinder = new PhpExecutableFinder; + if (!$phpPath = $phpFinder->find()) { + throw new \RuntimeException('The php executable could not be found, add it to your PATH environment variable and try again'); + } + + return $phpPath; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Configurator.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Configurator.php new file mode 100644 index 0000000..8138398 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Configurator.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 Sensio\Bundle\DistributionBundle\Configurator; + +use Sensio\Bundle\DistributionBundle\Configurator\Step\StepInterface; +use Symfony\Component\Yaml\Yaml; + +/** + * Configurator. + * + * @author Marc Weistroff + */ +class Configurator +{ + protected $filename; + protected $steps; + protected $parameters; + + public function __construct($kernelDir) + { + $this->kernelDir = $kernelDir; + $this->filename = $kernelDir.'/config/parameters.yml'; + + $this->steps = array(); + $this->parameters = $this->read(); + } + + public function isFileWritable() + { + return is_writable($this->filename); + } + + public function clean() + { + if (file_exists($this->getCacheFilename())) { + @unlink($this->getCacheFilename()); + } + } + + /** + * @param StepInterface $step + */ + public function addStep(StepInterface $step) + { + $this->steps[] = $step; + } + + /** + * @param integer $index + * + * @return StepInterface + */ + public function getStep($index) + { + if (isset($this->steps[$index])) { + return $this->steps[$index]; + } + } + + /** + * @return array + */ + public function getSteps() + { + return $this->steps; + } + + /** + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * @return integer + */ + public function getStepCount() + { + return count($this->steps); + } + + /** + * @param array $parameters + */ + public function mergeParameters($parameters) + { + $this->parameters = array_merge($this->parameters, $parameters); + } + + /** + * @return array + */ + public function getRequirements() + { + $majors = array(); + foreach ($this->steps as $step) { + foreach ($step->checkRequirements() as $major) { + $majors[] = $major; + } + } + + return $majors; + } + + /** + * @return array + */ + public function getOptionalSettings() + { + $minors = array(); + foreach ($this->steps as $step) { + foreach ($step->checkOptionalSettings() as $minor) { + $minors[] = $minor; + } + } + + return $minors; + } + + /** + * Renders parameters as a string. + * + * @return string + */ + public function render() + { + return Yaml::dump(array('parameters' => $this->parameters)); + } + + /** + * Writes parameters to parameters.yml or temporary in the cache directory. + * + * @return boolean + */ + public function write() + { + $filename = $this->isFileWritable() ? $this->filename : $this->getCacheFilename(); + + return file_put_contents($filename, $this->render()); + } + + /** + * Reads parameters from file. + * + * @return array + */ + protected function read() + { + $filename = $this->filename; + if (!$this->isFileWritable() && file_exists($this->getCacheFilename())) { + $filename = $this->getCacheFilename(); + } + + $ret = Yaml::parse($filename); + if (false === $ret || array() === $ret) { + throw new \InvalidArgumentException(sprintf('The %s file is not valid.', $filename)); + } + + if (isset($ret['parameters']) && is_array($ret['parameters'])) { + return $ret['parameters']; + } else { + return array(); + } + } + + /** + * getCacheFilename + * + * @return string + */ + protected function getCacheFilename() + { + return $this->kernelDir.'/cache/parameters.yml'; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/DoctrineStepType.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/DoctrineStepType.php new file mode 100644 index 0000000..a3db1d0 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/DoctrineStepType.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\DistributionBundle\Configurator\Form; + +use Sensio\Bundle\DistributionBundle\Configurator\Step\DoctrineStep; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; + +/** + * Doctrine Form Type. + * + * @author Fabien Potencier + */ +class DoctrineStepType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('driver', 'choice', array('choices' => DoctrineStep::getDrivers())) + ->add('name', 'text', array('required' => false)) + ->add('host', 'text', array('required' => false)) + ->add('path', 'text', array('required' => false)) + ->add('port', 'text', array('required' => false)) + ->add('user', 'text', array('required' => false)) + ->add('password', 'repeated', array( + 'required' => false, + 'type' => 'password', + 'first_name' => 'password', + 'second_name' => 'password_again', + 'invalid_message' => 'The password fields must match.', + )) + ; + } + + public function getName() + { + return 'distributionbundle_doctrine_step'; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/SecretStepType.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/SecretStepType.php new file mode 100644 index 0000000..c62ebd1 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/SecretStepType.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\DistributionBundle\Configurator\Form; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; + +/** + * Secret Form Type. + * + * @author Marc Weistroff + */ +class SecretStepType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->add('secret', 'text'); + } + + public function getName() + { + return 'distributionbundle_secret_step'; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/DoctrineStep.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/DoctrineStep.php new file mode 100644 index 0000000..3ff6843 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/DoctrineStep.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 Sensio\Bundle\DistributionBundle\Configurator\Step; + +use Sensio\Bundle\DistributionBundle\Configurator\Form\DoctrineStepType; +use Symfony\Component\Validator\Constraints as Assert; + +/** + * Doctrine Step. + * + * @author Fabien Potencier + */ +class DoctrineStep implements StepInterface +{ + /** + * @Assert\Choice(callback="getDriverKeys") + */ + public $driver; + + /** + * @Assert\NotBlank + */ + public $host; + + /** + * @Assert\Range(min = "0") + */ + public $port; + + /** + * @Assert\NotBlank + */ + public $name; + + /** + * @Assert\NotBlank + */ + public $user; + + public $password; + + public $path; + + public function __construct(array $parameters) + { + foreach ($parameters as $key => $value) { + if (0 === strpos($key, 'database_')) { + $parameters[substr($key, 9)] = $value; + $key = substr($key, 9); + $this->$key = $value; + } + } + } + + /** + * @see StepInterface + */ + public function getFormType() + { + return new DoctrineStepType(); + } + + /** + * @see StepInterface + */ + public function checkRequirements() + { + $messages = array(); + + if (!class_exists('\PDO')) { + $messages[] = 'PDO extension is mandatory.'; + } else { + $drivers = \PDO::getAvailableDrivers(); + if (0 == count($drivers)) { + $messages[] = 'Please install PDO drivers.'; + } + } + + return $messages; + } + + /** + * @see StepInterface + */ + public function checkOptionalSettings() + { + return array(); + } + + /** + * @see StepInterface + */ + public function update(StepInterface $data) + { + $parameters = array(); + + foreach ($data as $key => $value) { + $parameters['database_'.$key] = $value; + } + + return $parameters; + } + + /** + * @see StepInterface + */ + public function getTemplate() + { + return 'SensioDistributionBundle:Configurator/Step:doctrine.html.twig'; + } + + /** + * @return array + */ + public static function getDriverKeys() + { + return array_keys(static::getDrivers()); + } + + /** + * @return array + */ + public static function getDrivers() + { + return array( + 'pdo_mysql' => 'MySQL (PDO)', + 'pdo_sqlite' => 'SQLite (PDO)', + 'pdo_pgsql' => 'PosgreSQL (PDO)', + 'oci8' => 'Oracle (native)', + 'ibm_db2' => 'IBM DB2 (native)', + 'pdo_oci' => 'Oracle (PDO)', + 'pdo_ibm' => 'IBM DB2 (PDO)', + 'pdo_sqlsrv' => 'SQLServer (PDO)', + ); + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/SecretStep.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/SecretStep.php new file mode 100644 index 0000000..0a5207a --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/SecretStep.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\DistributionBundle\Configurator\Step; + +use Sensio\Bundle\DistributionBundle\Configurator\Form\SecretStepType; +use Symfony\Component\Validator\Constraints as Assert; + +/** + * Secret Step. + * + * @author Fabien Potencier + */ +class SecretStep implements StepInterface +{ + /** + * @Assert\NotBlank + */ + public $secret; + + public function __construct(array $parameters) + { + if (array_key_exists('secret', $parameters)) { + $this->secret = $parameters['secret']; + + if ('ThisTokenIsNotSoSecretChangeIt' == $this->secret) { + $this->secret = $this->generateRandomSecret(); + } + } else { + $this->secret = $this->generateRandomSecret(); + } + + } + + private function generateRandomSecret() + { + return hash('sha1', uniqid(mt_rand())); + } + + /** + * @see StepInterface + */ + public function getFormType() + { + return new SecretStepType(); + } + + /** + * @see StepInterface + */ + public function checkRequirements() + { + return array(); + } + + /** + * checkOptionalSettings + */ + public function checkOptionalSettings() + { + return array(); + } + + /** + * @see StepInterface + */ + public function update(StepInterface $data) + { + return array('secret' => $data->secret); + } + + /** + * @see StepInterface + */ + public function getTemplate() + { + return 'SensioDistributionBundle:Configurator/Step:secret.html.twig'; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/StepInterface.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/StepInterface.php new file mode 100644 index 0000000..3efa1fd --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/StepInterface.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\DistributionBundle\Configurator\Step; + +use Symfony\Component\Form\Type\FormTypeInterface; + +/** + * StepInterface. + * + * @author Marc Weistroff + */ +interface StepInterface +{ + /** + * __construct + * + * @param array $parameters + */ + public function __construct(array $parameters); + + /** + * Returns the form used for configuration. + * + * @return FormTypeInterface + */ + public function getFormType(); + + /** + * Checks for requirements. + * + * @return array + */ + public function checkRequirements(); + + /** + * Checks for optional setting it could be nice to have. + * + * @return array + */ + public function checkOptionalSettings(); + + /** + * Returns the template to be renderer for this step. + * + * @return string + */ + public function getTemplate(); + + /** + * Updates form data parameters. + * + * @param array $parameters + * @return array + */ + public function update(StepInterface $data); +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Controller/ConfiguratorController.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Controller/ConfiguratorController.php new file mode 100644 index 0000000..cedd9c7 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Controller/ConfiguratorController.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\DistributionBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\HttpFoundation\RedirectResponse; + +/** + * ConfiguratorController. + * + * @author Fabien Potencier + */ +class ConfiguratorController extends ContainerAware +{ + /** + * @return Response A Response instance + */ + public function stepAction($index = 0) + { + $configurator = $this->container->get('sensio.distribution.webconfigurator'); + + $step = $configurator->getStep($index); + $form = $this->container->get('form.factory')->create($step->getFormType(), $step); + + $request = $this->container->get('request'); + if ('POST' === $request->getMethod()) { + $form->bind($request); + if ($form->isValid()) { + $configurator->mergeParameters($step->update($form->getData())); + $configurator->write(); + + $index++; + + if ($index < $configurator->getStepCount()) { + return new RedirectResponse($this->container->get('router')->generate('_configurator_step', array('index' => $index))); + } + + return new RedirectResponse($this->container->get('router')->generate('_configurator_final')); + } + } + + return $this->container->get('templating')->renderResponse($step->getTemplate(), array( + 'form' => $form->createView(), + 'index' => $index, + 'count' => $configurator->getStepCount(), + 'version' => $this->getVersion(), + )); + } + + public function checkAction() + { + $configurator = $this->container->get('sensio.distribution.webconfigurator'); + + // Trying to get as much requirements as possible + $majors = $configurator->getRequirements(); + $minors = $configurator->getOptionalSettings(); + + $url = $this->container->get('router')->generate('_configurator_step', array('index' => 0)); + + if (empty($majors) && empty($minors)) { + return new RedirectResponse($url); + } + + return $this->container->get('templating')->renderResponse('SensioDistributionBundle::Configurator/check.html.twig', array( + 'majors' => $majors, + 'minors' => $minors, + 'url' => $url, + 'version' => $this->getVersion(), + )); + } + + public function finalAction() + { + $configurator = $this->container->get('sensio.distribution.webconfigurator'); + $configurator->clean(); + + try { + $welcomeUrl = $this->container->get('router')->generate('_welcome'); + } catch (\Exception $e) { + $welcomeUrl = null; + } + + return $this->container->get('templating')->renderResponse('SensioDistributionBundle::Configurator/final.html.twig', array( + 'welcome_url' => $welcomeUrl, + 'parameters' => $configurator->render(), + 'yml_path' => $this->container->getParameter('kernel.root_dir').'/config/parameters.yml', + 'is_writable' => $configurator->isFileWritable(), + 'version' => $this->getVersion(), + )); + } + + public function getVersion() + { + $kernel = $this->container->get('kernel'); + + return $kernel::VERSION; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/DependencyInjection/SensioDistributionExtension.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/DependencyInjection/SensioDistributionExtension.php new file mode 100644 index 0000000..4363d57 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/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('webconfigurator.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/Sensio/Bundle/DistributionBundle/LICENSE b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/LICENSE new file mode 100644 index 0000000..ad32bc5 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/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/Sensio/Bundle/DistributionBundle/Resources/bin/build.sh b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build.sh new file mode 100644 index 0000000..d89fc68 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build.sh @@ -0,0 +1,104 @@ +#!/bin/sh + +# 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 branch 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 + +# Clone +cd /tmp/Symfony +git clone https://github.com/symfony/symfony-standard.git . +git reset --hard origin/$2 + +composer.phar update --prefer-dist -n + +# cleanup +sudo 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* 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 +cd $TARGET/doctrine/doctrine-bundle && rm -rf Doctrine/Bundle/DoctrineBundle/Tests Doctrine/Bundle/DoctrineBundle/Resources/doc + +# 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 +cd $TARGET/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle && rm -rf Tests Resources/doc +cd $TARGET/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle && rm -rf Tests Resources/doc +cd $TARGET/symfony/monolog-bundle/Symfony/Bundle/MonologBundle && rm -rf Tests Resources/doc + +# 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 + +# 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 - + +cd /tmp +tar zcpf $DIR/Symfony_Standard_Vendors_$VERSION.tgz Symfony +sudo 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 +sudo rm -f $DIR/Symfony_Standard_$VERSION.zip +zip -rq $DIR/Symfony_Standard_$VERSION.zip Symfony diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build_bootstrap.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build_bootstrap.php new file mode 100644 index 0000000..d76c916 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build_bootstrap.php @@ -0,0 +1,30 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (PHP_SAPI !== 'cli') { + echo 'Warning: '.__FILE__.' should be invoked via the CLI version of PHP, not the '.PHP_SAPI.' SAPI'.PHP_EOL; +} + +$argv = $_SERVER['argv']; + +// allow the base path to be passed as the first argument, or default +if (isset($argv[1])) { + $appDir = $argv[1]; +} else { + if (!$appDir = realpath(__DIR__.'/../../../../../../../../app')) { + exit('Looks like you don\'t have a standard layout.'); + } +} + +require_once $appDir.'/autoload.php'; + +\Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::doBuildBootstrap($appDir); diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/routing/webconfigurator.xml b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/routing/webconfigurator.xml new file mode 100644 index 0000000..6dae8b5 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/routing/webconfigurator.xml @@ -0,0 +1,18 @@ + + + + + + SensioDistributionBundle:Configurator:check + + + + SensioDistributionBundle:Configurator:step + + + + SensioDistributionBundle:Configurator:final + + diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/webconfigurator.xml b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/webconfigurator.xml new file mode 100644 index 0000000..fcdaaf6 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/webconfigurator.xml @@ -0,0 +1,16 @@ + + + + + + Sensio\Bundle\DistributionBundle\Configurator\Configurator + + + + + %kernel.root_dir% + + + diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/configurator.css b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/configurator.css new file mode 100644 index 0000000..2bdacde --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/configurator.css @@ -0,0 +1,73 @@ +@import url("install.css"); + +.step h1 { + margin-top: 10px; + margin-bottom: 30px; + font-size: 26px; +} +.step p { + line-height: 20px; + padding-bottom: 20px; +} +.step .symfony-block-steps span { + display: inline-block; + padding: 2px 3px; + font-size: 11px; + line-height: 15px; + color: #868686; + font-weight: bold; + text-transform: uppercase; +} +.step .symfony-block-steps span.selected { + background-color: #aacd4e; + color: #FFFFFF; +} +.step .symfony-form-row { + padding-bottom: 40px; +} +.step .symfony-form-column { + width: 430px; + float: left; +} +.step .symfony-form-footer { + padding-top: 20px; + clear: both; +} +.step .symfony-form-field { + height: 20px; +} +.step .symfony-form-row label { + display: block; + padding-bottom: 8px; +} +.step .symfony-form-field input[type=text], +.step .symfony-form-field input[type=password], +.step .symfony-form-field textarea, +.step .symfony-form-field select { + font-size: 13px; + color: #565656; + width: 200px; +} +.step .symfony-form-field input[type=text], +.step .symfony-form-field input[type=password], +.step .symfony-form-field textarea { + border: 1px solid #dadada; + background: #FFFFFF url(../../../framework/images/input_bg.gif) repeat-x left top; + width: 194px; + padding: 5px 6px; +} +.step .symfony-form-errors ul { + padding: 0; +} +.step .symfony-form-errors li { + background: url(../images/notification.gif) no-repeat left 6px; + font-size: 11px; + line-height: 16px; + color: #759e1a; + padding: 10px 25px; +} +.step .symfony-configuration { + margin: 10px 0; + width: 100%; + height: 240px; +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/install.css b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/install.css new file mode 100644 index 0000000..88babe1 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/install.css @@ -0,0 +1,74 @@ +body { + font-size: 14px; + font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; +} +.sf-reset h1.title { + font-size: 45px; + padding-bottom: 30px; +} +.sf-reset h2 { + font-weight: bold; + color: #FFFFFF; + /* Font is reset to sans-serif (like body) */ + font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + margin-bottom: 10px; + background-color: #aacd4e; + padding: 2px 4px; + display: inline-block; + text-transform: uppercase; +} +.sf-reset ul a, +.sf-reset ul a:hover { + background: url(../images/blue-arrow.png) no-repeat right 6px; + padding-right: 10px; +} +.sf-reset ul, ol { + padding-left: 20px; +} +.sf-reset li { + padding-bottom: 18px; +} +.sf-reset ol li { + list-style-type: decimal; +} +.sf-reset ul li { + list-style-type: none; +} +.sf-reset .symfony-blocks-install { + overflow: hidden; +} +.sf-reset .symfony-install-continue { + font-size: 0.95em; + padding-left: 0; +} +.sf-reset .symfony-install-continue li { + padding-bottom: 10px; +} +.sf-reset .ok { + color: #fff; + font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + background-color: #6d6; + padding: 10px; + margin-bottom: 20px; +} +.sf-reset .ko { + background-color: #d66; +} +.version { + text-align: right; + font-size: 10px; + margin-right: 20px; +} +.sf-reset a, +.sf-reset li a { + color: #08C; + text-decoration: none; +} +.sf-reset a:hover, +.sf-reset li a:hover { + color: #08C; + text-decoration: underline; +} +.sf-reset textarea { + padding: 7px; +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/blue-arrow.png b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/blue-arrow.png new file mode 100644 index 0000000..fa82d4b Binary files /dev/null and b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/blue-arrow.png differ diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/favicon.ico b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/favicon.ico new file mode 100644 index 0000000..8648036 Binary files /dev/null and b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/favicon.ico differ diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/notification.gif b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/notification.gif new file mode 100644 index 0000000..017fac8 Binary files /dev/null and b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/notification.gif differ diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/SymfonyRequirements.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/SymfonyRequirements.php new file mode 100644 index 0000000..939360a --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/SymfonyRequirements.php @@ -0,0 +1,669 @@ + + * + * 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 overriden 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 Boolean $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 Boolean $optional Whether this is only an optional recommendation not a mandatory requirement + */ + public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false) + { + $this->fulfilled = (Boolean) $fulfilled; + $this->testMessage = (string) $testMessage; + $this->helpHtml = (string) $helpHtml; + $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText; + $this->optional = (Boolean) $optional; + } + + /** + * Returns whether the requirement is fulfilled. + * + * @return Boolean 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 Boolean 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 Boolean|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 Boolean $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 Boolean $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 Boolean $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 Boolean $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 Boolean|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 Boolean $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 Boolean|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 Boolean $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 recommmendations. + * + * @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 Boolean 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.' + ); + + $baseDir = basename(__DIR__); + + $this->addRequirement( + is_writable(__DIR__.'/cache'), + "$baseDir/cache/ directory must be writable", + "Change the permissions of the \"$baseDir/cache/\" directory so that the web server can write into it." + ); + + $this->addRequirement( + is_writable(__DIR__.'/logs'), + "$baseDir/logs/ directory must be writable", + "Change the permissions of the \"$baseDir/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('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+).' + ); + + /* optional recommendations follow */ + + $this->addRecommendation( + file_get_contents(__FILE__) === file_get_contents(__DIR__.'/../vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/SymfonyRequirements.php'), + '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.' + ); + + 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-XML module should be installed', + 'Install and enable the PHP-XML module.' + ); + + $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.' + ); + + 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( + class_exists('Locale'), + 'intl extension should be available', + 'Install and enable the intl extension (used for validators).' + ); + + if (class_exists('Collator')) { + $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.' + ); + } + + if (class_exists('Locale')) { + 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+).' + ); + } + + $accelerator = + (function_exists('apc_store') && ini_get('apc.enabled')) + || + function_exists('eaccelerator_put') && ini_get('eaccelerator.enable') + || + function_exists('xcache_set') + ; + + $this->addRecommendation( + $accelerator, + 'a PHP accelerator should be installed', + 'Install and enable a PHP accelerator like APC (highly recommended).' + ); + + $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), + sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'), + 'Install PDO drivers (mandatory for Doctrine).' + ); + } + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/check.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/check.php new file mode 100644 index 0000000..91b826b --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/check.php @@ -0,0 +1,62 @@ +getPhpIniConfigPath(); + +echo "********************************\n"; +echo "* *\n"; +echo "* Symfony requirements check *\n"; +echo "* *\n"; +echo "********************************\n\n"; + +echo $iniPath ? sprintf("* Configuration file used by PHP: %s\n\n", $iniPath) : "* WARNING: No configuration file (php.ini) used by PHP!\n\n"; + +echo "** ATTENTION **\n"; +echo "* The PHP CLI can use a different php.ini file\n"; +echo "* than the one used with your web server.\n"; +if ('\\' == DIRECTORY_SEPARATOR) { + echo "* (especially on the Windows platform)\n"; +} +echo "* To be on the safe side, please also launch the requirements check\n"; +echo "* from your web server using the web/config.php script.\n"; + +echo_title('Mandatory requirements'); + +$checkPassed = true; +foreach ($symfonyRequirements->getRequirements() as $req) { + /** @var $req Requirement */ + echo_requirement($req); + if (!$req->isFulfilled()) { + $checkPassed = false; + } +} + +echo_title('Optional recommendations'); + +foreach ($symfonyRequirements->getRecommendations() as $req) { + echo_requirement($req); +} + +exit($checkPassed ? 0 : 1); + +/** + * Prints a Requirement instance + */ +function echo_requirement(Requirement $requirement) +{ + $result = $requirement->isFulfilled() ? 'OK' : ($requirement->isOptional() ? 'WARNING' : 'ERROR'); + echo ' ' . str_pad($result, 9); + echo $requirement->getTestMessage() . "\n"; + + if (!$requirement->isFulfilled()) { + echo sprintf(" %s\n\n", $requirement->getHelpText()); + } +} + +function echo_title($title) +{ + echo "\n** $title **\n\n"; +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/web/config.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/web/config.php new file mode 100644 index 0000000..162acfc --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/web/config.php @@ -0,0 +1,124 @@ +getFailedRequirements(); +$minorProblems = $symfonyRequirements->getFailedRecommendations(); + +?> + + + + + + Symfony Configuration + + + + + +
    +
    + + + +
    + +
    +
    +
    +

    Welcome!

    +

    Welcome to your new Symfony project.

    +

    + This script will guide you through the basic configuration of your project. + You can also do the same by editing the ‘app/config/parameters.yml’ file directly. +

    + + +

    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". + +

    + + + +

    Your configuration looks good to run Symfony.

    + + + +
    +
    +
    +
    Symfony Standard Edition
    +
    + + diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/doctrine.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/doctrine.html.twig new file mode 100644 index 0000000..ace02a8 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/doctrine.html.twig @@ -0,0 +1,44 @@ +{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %} + +{% block title %}Symfony - Configure database{% endblock %} + +{% block content %} + {% form_theme form "SensioDistributionBundle::Configurator/form.html.twig" %} + +
    + {% include "SensioDistributionBundle::Configurator/steps.html.twig" with { "index": index, "count": count } %} + +

    Configure your Database

    +

    If your website needs a database connection, please configure it here.

    + +
    + {{ form_errors(form) }} +
    +
    +
    + {{ form_row(form.driver) }} + {{ form_row(form.host) }} + {{ form_row(form.name) }} +
    +
    + {{ form_row(form.user) }} + {{ form_row(form.password) }} +
    + + {{ form_rest(form) }} + + +
    +
    +{% endblock %} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/secret.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/secret.html.twig new file mode 100644 index 0000000..0465697 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/secret.html.twig @@ -0,0 +1,63 @@ +{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %} + +{% block title %}Symfony - Configure global Secret{% endblock %} + +{% block content %} + {% form_theme form "SensioDistributionBundle::Configurator/form.html.twig" %} + +
    + {% include "SensioDistributionBundle::Configurator/steps.html.twig" with { "index": index, "count": count } %} + +

    Global Secret

    +

    Configure the global secret for your website (the secret is used for the CSRF protection among other things):

    + +
    + {{ form_errors(form) }} +
    +
    +
    + {{ form_label(form.secret) }} +
    + {{ form_widget(form.secret) }} + + + + Generate + + + +
    + {{ form_errors(form.secret) }} +
    +
    +
    + + {{ form_rest(form) }} + + + +
    + + +
    +{% endblock %} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/check.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/check.html.twig new file mode 100644 index 0000000..52a0648 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/check.html.twig @@ -0,0 +1,43 @@ +{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %} + +{% block content %} + {% if majors|length %} +

    Major Problems that need to be fixed now

    +

    + We have detected {{ majors|length }} major problems. + You must fix them before continuing: +

    +
      + {% for message in majors %} +
    1. {{ message }}
    2. + {% endfor %} +
    + {% endif %} + + {% if minors|length %} +

    Some Recommandations

    + +

    + {% if majors|length %} + Additionally, we + {% else %} + We + {% endif %} + have detected some minor problems that we recommend you to fix to have a better Symfony + experience: + +

      + {% for message in minors %} +
    1. {{ message }}
    2. + {% endfor %} +
    +

    + + {% endif %} + + {% if not majors|length %} + + {% endif %} +{% endblock %} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/final.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/final.html.twig new file mode 100644 index 0000000..ba6f8dd --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/final.html.twig @@ -0,0 +1,30 @@ +{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %} + +{% block content_class %}config_done{% endblock %} +{% block content %} +
    +

    Well done!

    + {% if is_writable %} +

    Your distribution is configured!

    + {% else %} +

    Your distribution is almost configured but...

    + {% endif %} +

    + + {% if is_writable %} + Your parameters.yml file has been overwritten with these parameters (in {{ yml_path }}): + {% else %} + Your parameters.yml file is not writeable! Here are the parameters you can copy and paste in {{ yml_path }}: + {% endif %} + +

    + + + + {% if welcome_url %} + + {% endif %} +
    +{% endblock %} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/form.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/form.html.twig new file mode 100644 index 0000000..c92b314 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/form.html.twig @@ -0,0 +1,34 @@ +{% extends "form_div_layout.html.twig" %} + +{% block form_rows %} +
    + {{ form_errors(form) }} +
    + {% for child in form %} + {{ form_row(child) }} + {% endfor %} +{% endblock %} + +{% block form_row %} +
    + {{ form_label(form) }} +
    + {{ form_widget(form) }} +
    + {{ form_errors(form) }} +
    +
    +
    +{% endblock %} + +{% block form_label %} + {% if label is empty %} + {% set label = name|humanize %} + {% endif %} + +{% endblock %} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/layout.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/layout.html.twig new file mode 100644 index 0000000..c36b936 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/layout.html.twig @@ -0,0 +1,14 @@ +{% extends "TwigBundle::layout.html.twig" %} + +{% block head %} + +{% endblock %} + +{% block title 'Web Configurator Bundle' %} + +{% block body %} +
    + {% block content %}{% endblock %} +
    +
    Symfony Standard Edition v.{{ version }}
    +{% endblock %} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/steps.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/steps.html.twig new file mode 100644 index 0000000..c228827 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/steps.html.twig @@ -0,0 +1,14 @@ +
    + {% for i in 1..count %} + + {% if i == index + 1 %} + Step {{ i }} + {% else %} + Step {{ i }} + {% endif %} + + {% if i != count %} + > + {% endif %} + {% endfor %} +
    diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/SensioDistributionBundle.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/SensioDistributionBundle.php new file mode 100644 index 0000000..88a2913 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/SensioDistributionBundle.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 Sensio\Bundle\DistributionBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Sensio\Bundle\DistributionBundle\Configurator\Step\DoctrineStep; +use Sensio\Bundle\DistributionBundle\Configurator\Step\SecretStep; + +/** + * SensioDistributionBundle. + * + * @author Fabien Potencier + * @author Marc Weistroff + */ +class SensioDistributionBundle extends Bundle +{ + public function boot() + { + $configurator = $this->container->get('sensio.distribution.webconfigurator'); + $configurator->addStep(new DoctrineStep($configurator->getParameters())); + $configurator->addStep(new SecretStep($configurator->getParameters())); + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/composer.json b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/composer.json new file mode 100644 index 0000000..7ba31cd --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/composer.json @@ -0,0 +1,26 @@ +{ + "name": "sensio/distribution-bundle", + "description": "The base bundle for the Symfony Distributions", + "keywords": ["distribution","configuration"], + "type": "symfony-bundle", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "symfony/framework-bundle": "~2.2" + }, + "autoload": { + "psr-0": { "Sensio\\Bundle\\DistributionBundle": "" } + }, + "target-dir": "Sensio/Bundle/DistributionBundle", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "minimum-stability": "dev" +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Cache.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Cache.php new file mode 100644 index 0000000..a18899a --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Cache.php @@ -0,0 +1,182 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * 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 integer + */ + protected $maxage; + + /** + * The number of seconds that the response is considered fresh by a public + * cache like a reverse proxy cache. + * + * @var integer + */ + protected $smaxage; + + /** + * Whether or not the response is public or not. + * + * @var integer + */ + protected $public; + + /** + * Additional "Vary:"-headers + * + * @var array + */ + protected $vary = array(); + + /** + * 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 integer $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 integer + */ + public function getMaxAge() + { + return $this->maxage; + } + + /** + * Sets the number of seconds for the s-maxage cache-control header field. + * + * @param integer $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 integer + */ + public function getSMaxAge() + { + return $this->smaxage; + } + + /** + * Returns whether or not a response is public. + * + * @return Boolean + */ + public function isPublic() + { + return (Boolean) $this->public; + } + + /** + * Sets a response public. + * + * @param Boolean $public A boolean value + */ + public function setPublic($public) + { + $this->public = (Boolean) $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; + } + + /** + * Returns the annotation alias name. + * + * @return string + * @see ConfigurationInterface + */ + public function getAliasName() + { + return 'cache'; + } + + /** + * Only one cache directive is allowed + * + * @return Boolean + * @see ConfigurationInterface + */ + public function allowArray() + { + return false; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ConfigurationAnnotation.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ConfigurationAnnotation.php new file mode 100644 index 0000000..678de1a --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ConfigurationAnnotation.php @@ -0,0 +1,22 @@ + + */ +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/Sensio/Bundle/FrameworkExtraBundle/Configuration/ConfigurationInterface.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ConfigurationInterface.php new file mode 100644 index 0000000..797c2c7 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/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. + */ + +/** + * ConfigurationInterface. + * + * @author Fabien Potencier + */ +interface ConfigurationInterface +{ + /** + * Returns the alias name for an annotated configuration. + * + * @return string + */ + function getAliasName(); + + /** + * Returns whether multiple annotations of this type are allowed + * + * @return Boolean + */ + function allowArray(); +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Method.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Method.php new file mode 100644 index 0000000..730457d --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Method.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * 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 cache directive is allowed + * + * @return Boolean + * @see ConfigurationInterface + */ + public function allowArray() + { + return false; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ParamConverter.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ParamConverter.php new file mode 100644 index 0000000..cecb191 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/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. + */ + +/** + * The ParamConverter class handles the @ParamConverter annotation parts. + * + * @ParamConverter("post", class="BlogBundle:Post") + * + * @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 Boolean + */ + 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 Boolean $optional Wether the parameter is optional + */ + public function setIsOptional($optional) + { + $this->optional = (Boolean) $optional; + } + + /** + * Returns whether or not the parameter is optional. + * + * @return Boolean + */ + 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 Boolean + * @see ConfigurationInterface + */ + public function allowArray() + { + return true; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Route.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Route.php new file mode 100644 index 0000000..4c77c4a --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Route.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * @author Kris Wallsmith + * @Annotation + */ +class Route extends BaseRoute +{ + protected $service; + + public function setService($service) + { + $this->service = $service; + } + + public function getService() + { + return $this->service; + } + + /** + * Multiple route annotations are allowed + * + * @return Boolean + * @see ConfigurationInterface + */ + public function allowArray() + { + return true; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Template.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Template.php new file mode 100644 index 0000000..4f24df1 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Template.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * 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 isnt specified + * + * @var string + */ + protected $engine = 'twig'; + + /** + * The associative array of template variables. + * + * @var array + */ + protected $vars = array(); + + /** + * Should the template be streamed? + * + * @var Boolean + */ + protected $streamable = false; + + /** + * Returns the array of templates variables. + * + * @return array + */ + public function getVars() + { + return $this->vars; + } + + /** + * @param Boolean $streamable + */ + public function setIsStreamable($streamable) + { + $this->streamable = $streamable; + } + + /** + * @return Boolean + */ + public function isStreamable() + { + return (Boolean) $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 Boolean + * @see ConfigurationInterface + */ + public function allowArray() + { + return false; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/Compiler/AddParamConverterPass.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/Compiler/AddParamConverterPass.php new file mode 100644 index 0000000..be39dae --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/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/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/Configuration.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..9f12c88 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/Configuration.php @@ -0,0 +1,57 @@ + + */ +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() + ->end() + ->end() + ->arrayNode('view') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('annotations')->defaultTrue()->end() + ->end() + ->end() + ->arrayNode('cache') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('annotations')->defaultTrue()->end() + ->end() + ->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/SensioFrameworkExtraExtension.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/SensioFrameworkExtraExtension.php new file mode 100644 index 0000000..535cb96 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/SensioFrameworkExtraExtension.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. + */ + +/** + * 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\\CacheListener', + )); + } + + if ($annotationsToLoad) { + // must be first + $loader->load('annotations.xml'); + + foreach ($annotationsToLoad as $config) { + $loader->load($config); + } + + $this->addClassesToCompile(array( + 'Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\ConfigurationAnnotation', + )); + } + } + + /** + * 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/Sensio/Bundle/FrameworkExtraBundle/EventListener/CacheListener.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/CacheListener.php new file mode 100644 index 0000000..bddaefc --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/CacheListener.php @@ -0,0 +1,74 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * The CacheListener class has the responsibility to modify the + * Response object when a controller uses the "@Cache" annotation. + * + * @author Fabien Potencier + */ +class CacheListener implements EventSubscriberInterface +{ + /** + * Modifies the response to apply HTTP expiration header fields. + * + * @param FilterResponseEvent $event The notified event + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$configuration = $event->getRequest()->attributes->get('_cache')) { + return; + } + + $response = $event->getResponse(); + + if (!$response->isSuccessful()) { + return; + } + + if (null !== $configuration->getSMaxAge()) { + $response->setSharedMaxAge($configuration->getSMaxAge()); + } + + if (null !== $configuration->getMaxAge()) { + $response->setMaxAge($configuration->getMaxAge()); + } + + 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(); + } + + $event->setResponse($response); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::RESPONSE => 'onKernelResponse', + ); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ControllerListener.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ControllerListener.php new file mode 100644 index 0000000..07716f1 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ControllerListener.php @@ -0,0 +1,111 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * The ControllerListener class parses annotation blocks located in + * controller classes. + * + * @author Fabien Potencier + */ +class ControllerListener implements EventSubscriberInterface +{ + /** + * @var \Doctrine\Common\Annotations\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; + } else { + $configurations['_'.$configuration->getAliasName()] = $configuration; + } + } + } + + return $configurations; + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::CONTROLLER => 'onKernelController', + ); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ParamConverterListener.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ParamConverterListener.php new file mode 100644 index 0000000..914bf50 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ParamConverterListener.php @@ -0,0 +1,96 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * The ParamConverterListener handles the @ParamConverter annotation. + * + * @author Fabien Potencier + */ +class ParamConverterListener implements EventSubscriberInterface +{ + /** + * @var Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager + */ + protected $manager; + + /** + * Constructor. + * + * @param ParamConverterManager $manager A ParamConverterManager instance + */ + public function __construct(ParamConverterManager $manager) + { + $this->manager = $manager; + } + + /** + * 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]); + } else { + $r = new \ReflectionFunction($controller); + } + + // automatically apply conversion for non-configured objects + 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()); + } + + $this->manager->apply($request, $configurations); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::CONTROLLER => 'onKernelController', + ); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/TemplateListener.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/TemplateListener.php new file mode 100644 index 0000000..0c3f761 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/TemplateListener.php @@ -0,0 +1,135 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * The TemplateListener class handles the @Template annotation. + * + * @author Fabien Potencier + */ +class TemplateListener implements EventSubscriberInterface +{ + /** + * @var Symfony\Component\DependencyInjection\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(); + $templating = $this->container->get('templating'); + + 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; + } + + 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/Sensio/Bundle/FrameworkExtraBundle/LICENSE b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/LICENSE new file mode 100644 index 0000000..ad32bc5 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/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/Sensio/Bundle/FrameworkExtraBundle/README.md b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/README.md new file mode 100644 index 0000000..00918ae --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/README.md @@ -0,0 +1,6 @@ +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). diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/DateTimeParamConverter.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/DateTimeParamConverter.php new file mode 100644 index 0000000..fe4a8c0 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/DateTimeParamConverter.php @@ -0,0 +1,66 @@ + + * + * 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\ConfigurationInterface; +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, ConfigurationInterface $configuration) + { + $param = $configuration->getName(); + + if (!$request->attributes->has($param)) { + return false; + } + + $options = $configuration->getOptions(); + $value = $request->attributes->get($param); + + $date = isset($options['format']) + ? DateTime::createFromFormat($options['format'], $value) + : new DateTime($value); + + if (!$date) { + throw new NotFoundHttpException('Invalid date given.'); + } + + $request->attributes->set($param, $date); + + return true; + } + + /** + * @{inheritdoc} + */ + public function supports(ConfigurationInterface $configuration) + { + if (null === $configuration->getClass()) { + return false; + } + + return "DateTime" === $configuration->getClass(); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/DoctrineParamConverter.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/DoctrineParamConverter.php new file mode 100644 index 0000000..f9a1e78 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/DoctrineParamConverter.php @@ -0,0 +1,210 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * 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, ConfigurationInterface $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'; + } + + return $this->getManager($options['entity_manager'], $class)->getRepository($class)->$method($id); + } + + 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')) { + 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; + } + + $criteria = array(); + $em = $this->getManager($options['entity_manager'], $class); + $metadata = $em->getClassMetadata($class); + + foreach ($options['mapping'] as $attribute => $field) { + if ($metadata->hasField($field) || ($metadata->hasAssociation($field) && $metadata->isSingleValuedAssociation($field))) { + $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'])) { + $method = $options['repository_method']; + } else { + $method = 'findOneBy'; + } + + return $em->getRepository($class)->$method($criteria); + } + + /** + * @{inheritdoc} + */ + public function supports(ConfigurationInterface $configuration) + { + if (!$configuration instanceof ParamConverter) { + return false; + } + + // 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(ConfigurationInterface $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/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterInterface.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterInterface.php new file mode 100644 index 0000000..52acfd0 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterInterface.php @@ -0,0 +1,43 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * 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 ConfigurationInterface $configuration Contains the name, class and options of the object + * + * @return boolean True if the object has been successfully set, else false + */ + function apply(Request $request, ConfigurationInterface $configuration); + + /** + * Checks if the object is supported. + * + * @param ConfigurationInterface $configuration Should be an instance of ParamConverter + * + * @return boolean True if the object is supported, else false + */ + function supports(ConfigurationInterface $configuration); +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterManager.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterManager.php new file mode 100644 index 0000000..ee0cb3d --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterManager.php @@ -0,0 +1,143 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * 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, $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 convertion 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 integer $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/Sensio/Bundle/FrameworkExtraBundle/Resources/config/annotations.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/annotations.xml new file mode 100644 index 0000000..4e0c5b4 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/annotations.xml @@ -0,0 +1,17 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/cache.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/cache.xml new file mode 100644 index 0000000..47e1050 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/cache.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/converters.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/converters.xml new file mode 100644 index 0000000..6f81295 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/converters.xml @@ -0,0 +1,31 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener + Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager + Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter + Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DateTimeParamConverter + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/routing.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/routing.xml new file mode 100644 index 0000000..b3b58cd --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/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/Sensio/Bundle/FrameworkExtraBundle/Resources/config/services.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/services.xml new file mode 100644 index 0000000..6c76e61 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/services.xml @@ -0,0 +1,17 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/view.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/view.xml new file mode 100644 index 0000000..350b6a1 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/view.xml @@ -0,0 +1,17 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Routing/AnnotatedRouteControllerLoader.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Routing/AnnotatedRouteControllerLoader.php new file mode 100644 index 0000000..cb2f763 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Routing/AnnotatedRouteControllerLoader.php @@ -0,0 +1,82 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * 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 _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->setRequirement('_method', implode('|', $configuration->getMethods())); + } elseif ($configuration instanceof FrameworkExtraBundleRoute && $configuration->getService()) { + throw new \LogicException('The service option can only be specified at class level.'); + } + } + } + + /** + * 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/Sensio/Bundle/FrameworkExtraBundle/SensioFrameworkExtraBundle.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/SensioFrameworkExtraBundle.php new file mode 100644 index 0000000..2574a09 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/SensioFrameworkExtraBundle.php @@ -0,0 +1,31 @@ + + * + * 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()); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Templating/TemplateGuesser.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Templating/TemplateGuesser.php new file mode 100644 index 0000000..4d5a2f9 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Templating/TemplateGuesser.php @@ -0,0 +1,101 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * The TemplateGuesser class handles the guessing of template name based on controller + * + * @author Fabien Potencier + */ +class TemplateGuesser +{ + /** + * @var Symfony\Component\HttpKernel\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); + while ($bundleName = $bundle->getName()) { + if (null === $parentBundleName = $bundle->getParent()) { + $bundleName = $bundle->getName(); + + break; + } + + $bundles = $this->kernel->getBundle($parentBundleName, false); + $bundle = array_pop($bundles); + } + + 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 + * @param Bundle $bundle A Bundle instance + * @throws \InvalidArgumentException + */ + 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); + + throw new \InvalidArgumentException(sprintf('The "%s" class does not belong to a registered bundle.', $class)); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/UPGRADE.md b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/UPGRADE.md new file mode 100644 index 0000000..692f1f1 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/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/Sensio/Bundle/FrameworkExtraBundle/composer.json b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/composer.json new file mode 100644 index 0000000..1551d5b --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/composer.json @@ -0,0 +1,27 @@ +{ + "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.2", + "doctrine/common": "~2.2" + }, + "autoload": { + "psr-0": { "Sensio\\Bundle\\FrameworkExtraBundle": "" } + }, + "target-dir": "Sensio/Bundle/FrameworkExtraBundle", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "minimum-stability": "dev" +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateBundleCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateBundleCommand.php new file mode 100644 index 0000000..d96a863 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateBundleCommand.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 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\Output\Output; +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 +{ + private $generator; + + /** + * @see Command + */ + protected function configure() + { + $this + ->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'), + 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('structure', '', InputOption::VALUE_NONE, 'Whether to generate the whole directory structure'), + )) + ->setDescription('Generates a bundle') + ->setHelp(<<generate:bundle 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 app/console generate:bundle --namespace=Acme/BlogBundle + +Note that you can use / instead of \\ for the namespace delimiter to avoid any +problem. + +If you want to disable any user interaction, use --no-interaction but don't forget to pass all needed options: + +php app/console generate:bundle --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction + +Note that the bundle namespace must end with "Bundle". +EOT + ) + ->setName('generate:bundle') + ; + } + + /** + * @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) + { + $dialog = $this->getDialogHelper(); + + if ($input->isInteractive()) { + if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) { + $output->writeln('Command aborted'); + + return 1; + } + } + + foreach (array('namespace', 'dir') as $option) { + if (null === $input->getOption($option)) { + throw new \RuntimeException(sprintf('The "%s" option must be provided.', $option)); + } + } + + $namespace = Validators::validateBundleNamespace($input->getOption('namespace')); + if (!$bundle = $input->getOption('bundle-name')) { + $bundle = strtr($namespace, array('\\' => '')); + } + $bundle = Validators::validateBundleName($bundle); + $dir = Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace); + if (null === $input->getOption('format')) { + $input->setOption('format', 'annotation'); + } + $format = Validators::validateFormat($input->getOption('format')); + $structure = $input->getOption('structure'); + + $dialog->writeSection($output, 'Bundle generation'); + + if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) { + $dir = getcwd().'/'.$dir; + } + + $generator = $this->getGenerator(); + $generator->generate($namespace, $bundle, $dir, $format, $structure); + + $output->writeln('Generating the bundle code: OK'); + + $errors = array(); + $runner = $dialog->getRunner($output, $errors); + + // check that the namespace is already autoloaded + $runner($this->checkAutoloader($output, $namespace, $bundle, $dir)); + + // register the bundle in the Kernel class + $runner($this->updateKernel($dialog, $input, $output, $this->getContainer()->get('kernel'), $namespace, $bundle)); + + // routing + $runner($this->updateRouting($dialog, $input, $output, $bundle, $format)); + + $dialog->writeGeneratorSummary($output, $errors); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + $dialog->writeSection($output, 'Welcome to the Symfony2 bundle generator'); + + // namespace + $namespace = null; + try { + $namespace = $input->getOption('namespace') ? Validators::validateBundleNamespace($input->getOption('namespace')) : null; + } catch (\Exception $error) { + $output->writeln($dialog->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); + } + + if (null === $namespace) { + $output->writeln(array( + '', + 'Your application code must be written in bundles. This command helps', + 'you generate them easily.', + '', + 'Each bundle is hosted under a namespace (like Acme/Bundle/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#index-1 for more', + 'details on bundle naming conventions.', + '', + 'Use / instead of \\ for the namespace delimiter to avoid any problem.', + '', + )); + + $namespace = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle namespace', $input->getOption('namespace')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleNamespace'), false, $input->getOption('namespace')); + $input->setOption('namespace', $namespace); + } + + // bundle name + $bundle = null; + try { + $bundle = $input->getOption('bundle-name') ? Validators::validateBundleName($input->getOption('bundle-name')) : null; + } catch (\Exception $error) { + $output->writeln($dialog->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); + } + + if (null === $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.'.', + '', + )); + $bundle = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle name', $bundle), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleName'), false, $bundle); + $input->setOption('bundle-name', $bundle); + } + + // target dir + $dir = null; + try { + $dir = $input->getOption('dir') ? Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace) : null; + } catch (\Exception $error) { + $output->writeln($dialog->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); + } + + if (null === $dir) { + $dir = dirname($this->getContainer()->getParameter('kernel.root_dir')).'/src'; + + $output->writeln(array( + '', + 'The bundle can be generated anywhere. The suggested default directory uses', + 'the standard conventions.', + '', + )); + $dir = $dialog->askAndValidate($output, $dialog->getQuestion('Target directory', $dir), function ($dir) use ($bundle, $namespace) { return Validators::validateTargetDir($dir, $bundle, $namespace); }, false, $dir); + $input->setOption('dir', $dir); + } + + // format + $format = null; + try { + $format = $input->getOption('format') ? Validators::validateFormat($input->getOption('format')) : null; + } catch (\Exception $error) { + $output->writeln($dialog->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); + } + + if (null === $format) { + $output->writeln(array( + '', + 'Determine the format to use for the generated configuration.', + '', + )); + $format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $input->getOption('format')); + $input->setOption('format', $format); + } + + // optional files to generate + $output->writeln(array( + '', + 'To help you get started faster, the command can generate some', + 'code snippets for you.', + '', + )); + + $structure = $input->getOption('structure'); + if (!$structure && $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate the whole directory structure', 'no', '?'), false)) { + $structure = true; + } + $input->setOption('structure', $structure); + + // 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\" bundle\nin \"%s\" using the \"%s\" format.", $namespace, $bundle, $dir, $format), + '', + )); + } + + protected function checkAutoloader(OutputInterface $output, $namespace, $bundle, $dir) + { + $output->write('Checking that the bundle is autoloaded: '); + if (!class_exists($namespace.'\\'.$bundle)) { + return array( + '- Edit the composer.json file and register the bundle', + ' namespace in the "autoload" section:', + '', + ); + } + } + + protected function updateKernel($dialog, InputInterface $input, OutputInterface $output, KernelInterface $kernel, $namespace, $bundle) + { + $auto = true; + if ($input->isInteractive()) { + $auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of your Kernel', 'yes', '?'), true); + } + + $output->write('Enabling the bundle inside the Kernel: '); + $manip = new KernelManipulator($kernel); + try { + $ret = $auto ? $manip->addBundle($namespace.'\\'.$bundle) : false; + + 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(),', $namespace.'\\'.$bundle), + '', + ); + } + } catch (\RuntimeException $e) { + return array( + sprintf('Bundle %s is already defined in AppKernel::registerBundles().', $namespace.'\\'.$bundle), + '', + ); + } + } + + protected function updateRouting($dialog, InputInterface $input, OutputInterface $output, $bundle, $format) + { + $auto = true; + if ($input->isInteractive()) { + $auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true); + } + + $output->write('Importing the bundle routing resource: '); + $routing = new RoutingManipulator($this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml'); + try { + $ret = $auto ? $routing->addResource($bundle, $format) : false; + if (!$ret) { + if ('annotation' === $format) { + $help = sprintf(" resource: \"@%s/Controller/\"\n type: annotation\n", $bundle); + } else { + $help = sprintf(" resource: \"@%s/Resources/config/routing.%s\"\n", $bundle, $format); + } + $help .= " prefix: /\n"; + + return array( + '- Import the bundle\'s routing resource in the app main routing file:', + '', + sprintf(' %s:', $bundle), + $help, + '', + ); + } + } catch (\RuntimeException $e) { + return array( + sprintf('Bundle %s is already imported.', $bundle), + '', + ); + } + } + + protected function createGenerator() + { + return new BundleGenerator($this->getContainer()->get('filesystem')); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateControllerCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateControllerCommand.php new file mode 100644 index 0000000..9c74a90 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateControllerCommand.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 Sensio\Bundle\GeneratorBundle\Command; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper; +use Sensio\Bundle\GeneratorBundle\Generator\ControllerGenerator; + +/** + * Generates controllers. + * + * @author Wouter J + */ +class GenerateControllerCommand extends GeneratorCommand +{ + private $generator; + + /** + * @see Command + */ + public function configure() + { + $this + ->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' + ), + )) + ->setDescription('Generates a controller') + ->setHelp(<<generate:controller 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 +(--bundle and --controller are the only +ones needed if you follow the conventions): + +php app/console generate:controller --controller=AcmeBlogBundle:Post + +If you want to disable any user interaction, use --no-interaction +but don't forget to pass all needed options: + +php app/console generate:controller --controller=AcmeBlogBundle:Post --no-interaction + +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/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 + ) + ->setName('generate:controller') + ; + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + + if ($input->isInteractive()) { + if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) { + $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 exists.', $bundle)); + } + } + + $dialog->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'); + + $dialog->writeGeneratorSummary($output, array()); + } + + public function interact(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + $dialog->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) { + $controller = $dialog->askAndValidate($output, $dialog->getQuestion('Controller name', $input->getOption('controller')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateControllerName'), false, $input->getOption('controller')); + 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 exists.', $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.', + '', + )); + $routeFormat = $dialog->askAndValidate($output, $dialog->getQuestion('Routing format (php, xml, yml, annotation)', $defaultFormat), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $defaultFormat); + $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.', + '', + )); + $templateFormat = $dialog->askAndValidate($output, $dialog->getQuestion('Template format (twig, php)', $defaultFormat), $validateTemplateFormat, false, $defaultFormat); + $input->setOption('template-format', $templateFormat); + + // actions + $input->setOption('actions', $this->addActions($input, $output, $dialog)); + + // 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, DialogHelper $dialog) + { + $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(''); + $actionName = $dialog->askAndValidate($output, $dialog->getQuestion('New action name (press to stop adding actions)', null), 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; + }); + if (!$actionName) { + break; + } + + // route + $route = $dialog->ask($output, $dialog->getQuestion('Action route', '/'.substr($actionName, 0, -6)), '/'.substr($actionName, 0, -6)); + $placeholders = $this->getPlaceholdersFromRoute($route); + + // template + $defaultTemplate = $input->getOption('controller').':'.substr($actionName, 0, -6).'.html.'.$input->getOption('template-format'); + $template = $dialog->askAndValidate($output, $dialog->getQuestion('Templatename (optional)', $defaultTemplate), $templateNameValidator, false, 'default'); + + // adding action + $actions[$actionName] = array( + 'name' => $actionName, + 'route' => $route, + 'placeholders' => $placeholders, + 'template' => $template, + ); + } + + return $actions; + } + + public function parseActions($actions) + { + if (is_array($actions)) { + return $actions; + } + + $newActions = array(); + + foreach (explode(' ', $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'; + + $newActions[$name] = array( + 'name' => $name, + 'route' => $route, + 'placeholders' => $placeholders, + 'template' => $template, + ); + } + + return $newActions; + } + + 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/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCommand.php new file mode 100644 index 0000000..3635505 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/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\MetadataFactory; + +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 MetadataFactory($this->getContainer()->get('doctrine')); + + return $factory->getClassMetadata($entity)->getMetadata(); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCrudCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCrudCommand.php new file mode 100644 index 0000000..9fb2c2f --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCrudCommand.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 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\Output\Output; +use Symfony\Component\Console\Command\Command; +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 $generator; + private $formGenerator; + + /** + * @see Command + */ + protected function configure() + { + $this + ->setDefinition(array( + 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, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation'), + new InputOption('overwrite', '', InputOption::VALUE_NONE, 'Do not stop the generation if crud controller already exist, thus overwriting all generated files'), + )) + ->setDescription('Generates a CRUD based on a Doctrine entity') + ->setHelp(<<doctrine:generate:crud command generates a CRUD based on a Doctrine entity. + +The default command only generates the list and show actions. + +php app/console doctrine:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin + +Using the --with-write option allows to generate the new, edit and delete actions. + +php app/console 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 overriden 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 + ) + ->setName('doctrine:generate:crud') + ->setAliases(array('generate:doctrine:crud')) + ; + } + + /** + * @see Command + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + + if ($input->isInteractive()) { + if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) { + $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'); + + $dialog->writeSection($output, 'CRUD generation'); + + $entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace($bundle).'\\'.$entity; + $metadata = $this->getEntityMetadata($entityClass); + $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 = $dialog->getRunner($output, $errors); + + // form + if ($withWrite) { + $this->generateForm($bundle, $entity, $metadata); + $output->writeln('Generating the Form code: OK'); + } + + // routing + if ('annotation' != $format) { + $runner($this->updateRouting($dialog, $input, $output, $bundle, $format, $entity, $prefix)); + } + + $dialog->writeGeneratorSummary($output, $errors); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + $dialog->writeSection($output, 'Welcome to the Doctrine2 CRUD generator'); + + // namespace + $output->writeln(array( + '', + 'This command helps you generate CRUD controllers and templates.', + '', + 'First, you need to give the entity for which you want to generate a CRUD.', + 'You can give an entity that does not exist yet and the wizard will help', + 'you defining it.', + '', + 'You must use the shortcut notation like AcmeBlogBundle:Post.', + '', + )); + + $entity = $dialog->askAndValidate($output, $dialog->getQuestion('The Entity shortcut name', $input->getOption('entity')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName'), false, $input->getOption('entity')); + $input->setOption('entity', $entity); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + + // Entity exists? + $entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace($bundle).'\\'.$entity; + $metadata = $this->getEntityMetadata($entityClass); + + // 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.', + '', + )); + $withWrite = $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate the "write" actions', $withWrite ? 'yes' : 'no', '?'), $withWrite); + $input->setOption('with-write', $withWrite); + + // format + $format = $input->getOption('format'); + $output->writeln(array( + '', + 'Determine the format to use for the generated CRUD.', + '', + )); + $format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $format), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $format); + $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 = $dialog->ask($output, $dialog->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) + { + try { + $this->getFormGenerator($bundle)->generate($bundle, $entity, $metadata[0]); + } catch (\RuntimeException $e ) { + // form already exists + } + } + + protected function updateRouting($dialog, InputInterface $input, OutputInterface $output, $bundle, $format, $entity, $prefix) + { + $auto = true; + if ($input->isInteractive()) { + $auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true); + } + + $output->write('Importing the CRUD routes: '); + $this->getContainer()->get('filesystem')->mkdir($bundle->getPath().'/Resources/config/'); + $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:', $bundle->getName().('' !== $prefix ? '_'.str_replace('/', '_', $prefix) : '')), + $help, + '', + ); + } + } + + 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')); + } + + 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/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineEntityCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineEntityCommand.php new file mode 100644 index 0000000..938513d --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineEntityCommand.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 Sensio\Bundle\GeneratorBundle\Command; + +use Sensio\Bundle\GeneratorBundle\Generator\DoctrineEntityGenerator; +use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\DependencyInjection\Container; +use Doctrine\DBAL\Types\Type; + +/** + * Initializes a Doctrine entity inside a bundle. + * + * @author Fabien Potencier + */ +class GenerateDoctrineEntityCommand extends GenerateDoctrineCommand +{ + private $generator; + + 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') + ->addOption('with-repository', null, InputOption::VALUE_NONE, 'Whether to generate the entity repository or not') + ->setHelp(<<doctrine:generate:entity task generates a new Doctrine +entity inside a bundle: + +php app/console doctrine:generate:entity --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 app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --fields="title:string(255) body:text" + +The command can also generate the corresponding entity repository class with the +--with-repository option: + +php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --with-repository + +By default, the command uses annotations for the mapping information; change it +with --format: + +php app/console doctrine:generate:entity --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 app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --format=annotation --fields="title:string(255) body:text" --with-repository --no-interaction +EOT + ); + } + + /** + * @throws \InvalidArgumentException When the bundle doesn't end with Bundle (Example: "Bundle/MySampleBundle") + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + + if ($input->isInteractive()) { + if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) { + $output->writeln('Command aborted'); + + return 1; + } + } + + $entity = Validators::validateEntityName($input->getOption('entity')); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + $format = Validators::validateFormat($input->getOption('format')); + $fields = $this->parseFields($input->getOption('fields')); + + $dialog->writeSection($output, 'Entity generation'); + + $bundle = $this->getContainer()->get('kernel')->getBundle($bundle); + + $generator = $this->getGenerator(); + $generator->generate($bundle, $entity, $format, array_values($fields), $input->getOption('with-repository')); + + $output->writeln('Generating the entity code: OK'); + + $dialog->writeGeneratorSummary($output, array()); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + $dialog->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) { + $entity = $dialog->askAndValidate($output, $dialog->getQuestion('The Entity shortcut name', $input->getOption('entity')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName'), false, $input->getOption('entity'), $bundleNames); + + 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'); + + $format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $input->getOption('format'), $formats); + $input->setOption('format', $format); + + // fields + $input->setOption('fields', $this->addFields($input, $output, $dialog)); + + // repository? + $output->writeln(''); + $withRepository = $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate an empty repository class', $input->getOption('with-repository') ? 'yes' : 'no', '?'), $input->getOption('with-repository')); + $input->setOption('with-repository', $withRepository); + + // 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\" Doctrine2 entity", $bundle, $entity), + sprintf("using the \"%s\" format.", $format), + '', + )); + } + + private function parseFields($input) + { + if (is_array($input)) { + return $input; + } + + $fields = array(); + foreach (explode(' ', $input) as $value) { + $elements = explode(':', $value); + $name = $elements[0]; + if (strlen($name)) { + $type = isset($elements[1]) ? $elements[1] : 'string'; + preg_match_all('/(.*)\((.*)\)/', $type, $matches); + $type = isset($matches[1][0]) ? $matches[1][0] : $type; + $length = isset($matches[2][0]) ? $matches[2][0] : null; + + $fields[$name] = array('fieldName' => $name, 'type' => $type, 'length' => $length); + } + } + + return $fields; + } + + private function addFields(InputInterface $input, OutputInterface $output, DialogHelper $dialog) + { + $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) { + // FIXME: take into account user-defined field 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; + }; + + while (true) { + $output->writeln(''); + $generator = $this->getGenerator(); + $columnName = $dialog->askAndValidate($output, $dialog->getQuestion('New field name (press to stop adding fields)', null), 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)); + } + + return $name; + }); + 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'; + } + + $type = $dialog->askAndValidate($output, $dialog->getQuestion('Field type', $defaultType), $fieldValidator, false, $defaultType, $types); + + $data = array('columnName' => $columnName, 'fieldName' => lcfirst(Container::camelize($columnName)), 'type' => $type); + + if ($type == 'string') { + $data['length'] = $dialog->askAndValidate($output, $dialog->getQuestion('Field length', 255), $lengthValidator, false, 255); + } + + $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/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineFormCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineFormCommand.php new file mode 100644 index 0000000..63219f0 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineFormCommand.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 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\Output\Output; +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 + ->setDefinition(array( + new InputArgument('entity', InputArgument::REQUIRED, 'The entity class name to initialize (shortcut notation)'), + )) + ->setDescription('Generates a form type class based on a Doctrine entity') + ->setHelp(<<doctrine:generate:form command generates a form class based on a Doctrine entity. + +php app/console doctrine:generate:form 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 + ) + ->setName('doctrine:generate:form') + ->setAliases(array('generate:doctrine:form')) + ; + } + + /** + * @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 = new DoctrineFormGenerator($this->getContainer()->get('filesystem')); + $generator->setSkeletonDirs($this->getSkeletonDirs($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/Sensio/Bundle/GeneratorBundle/Command/GeneratorCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GeneratorCommand.php new file mode 100644 index 0000000..f3d8f72 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GeneratorCommand.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 Sensio\Bundle\GeneratorBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper; +use Sensio\Bundle\GeneratorBundle\Generator\Generator; + +/** + * 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; + } + + protected abstract function createGenerator(); + + protected function getGenerator($bundle = null) + { + if (null === $this->generator) { + $this->generator = $this->createGenerator(); + $this->generator->setSkeletonDirs($this->getSkeletonDirs($bundle)); + } + + return $this->generator; + } + + protected function getSkeletonDirs($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 getDialogHelper() + { + $dialog = $this->getHelperSet()->get('dialog'); + if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') { + $this->getHelperSet()->set($dialog = new DialogHelper()); + } + + return $dialog; + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/Helper/DialogHelper.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/Helper/DialogHelper.php new file mode 100644 index 0000000..c0d8c10 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/Helper/DialogHelper.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\DialogHelper as BaseDialogHelper; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Generates bundles. + * + * @author Fabien Potencier + */ +class DialogHelper extends BaseDialogHelper +{ + public function writeGeneratorSummary(OutputInterface $output, $errors) + { + if (!$errors) { + $this->writeSection($output, 'You can now start using the generated code!'); + } else { + $this->writeSection($output, array( + 'The command was not able to configure everything automatically.', + 'You must do 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/Sensio/Bundle/GeneratorBundle/Command/Validators.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/Validators.php new file mode 100644 index 0000000..3ad11b7 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/Validators.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 Sensio\Bundle\GeneratorBundle\Command; + +/** + * Validator functions. + * + * @author Fabien Potencier + */ +class Validators +{ + public static function validateBundleNamespace($namespace) + { + 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 (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('The bundle name contains invalid characters.'); + } + + 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 validateTargetDir($dir, $bundle, $namespace) + { + // add trailing / if necessary + return '/' === substr($dir, -1, 1) ? $dir : $dir.'/'; + } + + public static function validateFormat($format) + { + $format = strtolower($format); + + if (!in_array($format, array('php', 'xml', 'yml', 'annotation'))) { + throw new \RuntimeException(sprintf('Format "%s" is not supported.', $format)); + } + + return $format; + } + + public static function validateEntityName($entity) + { + 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 $entity; + } + + public static function getReservedWords() + { + return array( + 'abstract', + 'and', + 'array', + 'as', + 'break', + 'case', + 'catch', + 'class', + 'clone', + 'const', + 'continue', + 'declare', + 'default', + 'do', + 'else', + 'elseif', + 'enddeclare', + 'endfor', + 'endforeach', + 'endif', + 'endswitch', + 'endwhile', + 'extends', + 'final', + 'for', + 'foreach', + 'function', + 'global', + 'goto', + 'if', + 'implements', + 'interface', + 'instanceof', + 'namespace', + 'new', + 'or', + 'private', + 'protected', + 'public', + 'static', + 'switch', + 'throw', + 'try', + 'use', + 'var', + 'while', + 'xor', + '__CLASS__', + '__DIR__', + '__FILE__', + '__LINE__', + '__FUNCTION__', + '__METHOD__', + '__NAMESPACE__', + 'die', + 'echo', + 'empty', + 'exit', + 'eval', + 'include', + 'include_once', + 'isset', + 'list', + 'require', + 'require_once', + 'return', + 'print', + 'unset', + ); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/BundleGenerator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/BundleGenerator.php new file mode 100644 index 0000000..bc40815 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/BundleGenerator.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 Sensio\Bundle\GeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\DependencyInjection\Container; + +/** + * Generates a bundle. + * + * @author Fabien Potencier + */ +class BundleGenerator extends Generator +{ + private $filesystem; + + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + public function generate($namespace, $bundle, $dir, $format, $structure) + { + $dir .= '/'.strtr($namespace, '\\', '/'); + 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))); + } + } + + $basename = substr($bundle, 0, -6); + $parameters = array( + 'namespace' => $namespace, + 'bundle' => $bundle, + 'format' => $format, + 'bundle_basename' => $basename, + 'extension_alias' => Container::underscore($basename), + ); + + $this->renderFile('bundle/Bundle.php.twig', $dir.'/'.$bundle.'.php', $parameters); + $this->renderFile('bundle/Extension.php.twig', $dir.'/DependencyInjection/'.$basename.'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); + + if ('xml' === $format || 'annotation' === $format) { + $this->renderFile('bundle/services.xml.twig', $dir.'/Resources/config/services.xml', $parameters); + } else { + $this->renderFile('bundle/services.'.$format.'.twig', $dir.'/Resources/config/services.'.$format, $parameters); + } + + if ('annotation' != $format) { + $this->renderFile('bundle/routing.'.$format.'.twig', $dir.'/Resources/config/routing.'.$format, $parameters); + } + + if ($structure) { + $this->renderFile('bundle/messages.fr.xlf', $dir.'/Resources/translations/messages.fr.xlf', $parameters); + + $this->filesystem->mkdir($dir.'/Resources/doc'); + $this->filesystem->touch($dir.'/Resources/doc/index.rst'); + $this->filesystem->mkdir($dir.'/Resources/translations'); + $this->filesystem->mkdir($dir.'/Resources/public/css'); + $this->filesystem->mkdir($dir.'/Resources/public/images'); + $this->filesystem->mkdir($dir.'/Resources/public/js'); + } + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/ControllerGenerator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/ControllerGenerator.php new file mode 100644 index 0000000..56ae45d --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/ControllerGenerator.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 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 actioname without the sufix Action (for the template logical name) + $actions[$i]['basename'] = $basename = substr($action['name'], 0, -6); + $params = $parameters; + $params['action'] = $actions[$i]; + + // create a template + $template = $actions[$i]['template']; + if ('default' == $template) { + $template = $bundle->getName().':'.$controller.':'.substr($action['name'], 0, -6).'.html.'.$templateFormat; + } + + if ('twig' == $templateFormat) { + $this->renderFile('controller/Template.html.twig', $dir.'/Resources/views/'.$this->parseTemplatePath($template), $params); + } else { + $this->renderFile('controller/Template.html.php', $dir.'/Resources/views/'.$this->parseTemplatePath($template), $params); + } + + $this->generateRouting($bundle, $controller, $actions[$i], $routeFormat); + } + + $parameters['actions'] = $actions; + + $this->renderFile('controller/Controller.php', $controllerFile, $parameters); + $this->renderFile('controller/ControllerTest.php', $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 pattern: %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('pattern', $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 = '') + { + $data = array(); + + list($data['bundle'], $data['controller'], $data['template']) = explode(':', $logicalname); + + return ($part ? $data[$part] : $data); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineCrudGenerator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineCrudGenerator.php new file mode 100644 index 0000000..9e28233 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineCrudGenerator.php @@ -0,0 +1,287 @@ + + * + * 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 CRUD controller. + * + * @author Fabien Potencier + */ +class DoctrineCrudGenerator extends Generator +{ + protected $filesystem; + protected $routePrefix; + protected $routeNamePrefix; + protected $bundle; + protected $entity; + protected $metadata; + protected $format; + protected $actions; + + /** + * Constructor. + * + * @param Filesystem $filesystem A Filesystem instance + */ + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + /** + * 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 Wether 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 = str_replace('/', '_', $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 primary keys.'); + } + + if (!in_array('id', $metadata->identifier)) { + throw new \RuntimeException('The CRUD generator expects the entity object has a primary key field named "id" with a getId() method.'); + } + + $this->entity = $entity; + $this->bundle = $bundle; + $this->metadata = $metadata; + $this->setFormat($format); + + $this->generateControllerClass($forceOverwrite); + + $dir = sprintf('%s/Resources/views/%s', $this->bundle->getPath(), str_replace('\\', '/', $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 + */ + private 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_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.'Type'), + )); + } + + /** + * 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, + '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, + '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, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'actions' => $this->actions, + )); + } + + /** + * 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, + 'entity' => $this->entity, + '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')); + }); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineEntityGenerator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineEntityGenerator.php new file mode 100644 index 0000000..bb7315a --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineEntityGenerator.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 Sensio\Bundle\GeneratorBundle\Generator; + +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; + +/** + * 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; + } + + public function generate(BundleInterface $bundle, $entity, $format, array $fields, $withRepository) + { + // 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); + if ($withRepository) { + $class->customRepositoryClassName = $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); + $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); + } + + $this->filesystem->mkdir(dirname($entityPath)); + file_put_contents($entityPath, $entityCode); + + if ($mappingPath) { + $this->filesystem->mkdir(dirname($mappingPath)); + file_put_contents($mappingPath, $mappingCode); + } + + if ($withRepository) { + $path = $bundle->getPath().str_repeat('/..', substr_count(get_class($bundle), '\\')); + $this->getRepositoryGenerator()->writeEntityRepositoryClass($class->customRepositoryClassName, $path); + } + } + + 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(); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineFormGenerator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineFormGenerator.php new file mode 100644 index 0000000..eb684a9 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineFormGenerator.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 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 if it does not exist. + * + * @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 + */ + public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata) + { + $parts = explode('\\', $entity); + $entityClass = array_pop($parts); + + $this->className = $entityClass.'Type'; + $dirPath = $bundle->getPath().'/Form'; + $this->classPath = $dirPath.'/'.str_replace('\\', '/', $entity).'Type.php'; + + if (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), + '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).'_'.$this->className), + )); + } + + /** + * 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/Sensio/Bundle/GeneratorBundle/Generator/Generator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/Generator.php new file mode 100644 index 0000000..0377a7b --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/Generator.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 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 = new \Twig_Environment(new \Twig_Loader_Filesystem($this->skeletonDirs), array( + 'debug' => true, + 'cache' => false, + 'strict_variables' => true, + 'autoescape' => false, + )); + + return $twig->render($template, $parameters); + } + + 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/Sensio/Bundle/GeneratorBundle/LICENSE b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/LICENSE new file mode 100644 index 0000000..5f23672 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/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/Sensio/Bundle/GeneratorBundle/Manipulator/KernelManipulator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/KernelManipulator.php new file mode 100644 index 0000000..c8cb420 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/KernelManipulator.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 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 Boolean true if it worked, false otherwise + * + * @throws \RuntimeException If bundle is already defined + */ + public function addBundle($bundle) + { + if (!$this->reflected->getFilename()) { + return false; + } + + $src = file($this->reflected->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 + $token = $this->next(); + if (T_ARRAY !== $token[0]) { + return false; + } + + // add the bundle at the end of the array + while ($token = $this->next()) { + // look for ); + if (')' !== $this->value($token)) { + continue; + } + + if (';' !== $this->value($this->peek())) { + continue; + } + + // ; + $this->next(); + + $lines = array_merge( + array_slice($src, 0, $this->line - 2), + // Appends a separator comma to the current last position of the array + array(rtrim(rtrim($src[$this->line - 2]), ',') . ",\n"), + array(sprintf(" new %s(),\n", $bundle)), + array_slice($src, $this->line - 1) + ); + + file_put_contents($this->reflected->getFilename(), implode('', $lines)); + + return true; + } + } + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/Manipulator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/Manipulator.php new file mode 100644 index 0000000..a97baa7 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/Manipulator.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 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 integer $line The start line of the code + */ + protected function setCode(array $tokens, $line = 0) + { + $this->tokens = $tokens; + $this->line = $line; + } + + /** + * Gets the next token. + * + * @param mixed A PHP token + */ + 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 mixed A PHP token + */ + 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 The token value + */ + protected function value($token) + { + return is_array($token) ? $token[1] : $token; + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/RoutingManipulator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/RoutingManipulator.php new file mode 100644 index 0000000..3863837 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/RoutingManipulator.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 Sensio\Bundle\GeneratorBundle\Manipulator; + +use Symfony\Component\DependencyInjection\Container; + +/** + * 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 Boolean true if it worked, false otherwise + * + * @throws \RuntimeException If bundle is already imported + */ + public function addResource($bundle, $format, $prefix = '/', $path = 'routing') + { + $current = ''; + 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); + } + + $code = sprintf("%s:\n", Container::underscore(substr($bundle, 0, -6)).('/' !== $prefix ? '_'.str_replace('/', '_', substr($prefix, 1)) : '')); + 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; + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/README.md b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/README.md new file mode 100644 index 0000000..8b033db --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/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/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/Bundle.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/Bundle.php.twig new file mode 100644 index 0000000..2f26597 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/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/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultController.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultController.php.twig new file mode 100644 index 0000000..7314060 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultController.php.twig @@ -0,0 +1,34 @@ +render('{{ bundle }}:Default:index.html.twig', array('name' => $name)); + {%- else -%} + return array('name' => $name); + {%- endif %} + + } +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultControllerTest.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultControllerTest.php.twig new file mode 100644 index 0000000..0e53159 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultControllerTest.php.twig @@ -0,0 +1,23 @@ +request('GET', '/hello/Fabien'); + + $this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0); + } +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/Extension.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/Extension.php.twig new file mode 100644 index 0000000..f825c22 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/Extension.php.twig @@ -0,0 +1,45 @@ +processConfiguration($configuration, $configs); + + {% if format == 'yml' -%} + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.yml'); + {%- elseif format == 'xml' or format == 'annotation' -%} + $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/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/index.html.twig.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/index.html.twig.twig new file mode 100644 index 0000000..e64fdb0 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/index.html.twig.twig @@ -0,0 +1 @@ +Hello {% raw %}{{ name }}{% endraw %}! diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/messages.fr.xlf b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/messages.fr.xlf new file mode 100644 index 0000000..fd59e6c --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/messages.fr.xlf @@ -0,0 +1,11 @@ + + + + + + Symfony2 is great + J'aime Symfony2 + + + + diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.php.twig new file mode 100644 index 0000000..8d06ec3 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.php.twig @@ -0,0 +1,20 @@ +add('{{ extension_alias }}_homepage', new Route('/hello/{name}', array( + '_controller' => '{{ bundle }}:Default:index', +))); +{% endblock body %} + +{% block return %} +return $collection; +{% endblock return %} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.xml.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.xml.twig new file mode 100644 index 0000000..4dd33e3 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.xml.twig @@ -0,0 +1,12 @@ + + + + +{% block body %} + + {{ bundle }}:Default:index + +{% endblock body %} + diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.yml.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.yml.twig new file mode 100644 index 0000000..c8fddb7 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.yml.twig @@ -0,0 +1,3 @@ +{{ extension_alias }}_homepage: + pattern: /hello/{name} + defaults: { _controller: {{ bundle }}:Default:index } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.php.twig new file mode 100644 index 0000000..ea69fc7 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/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 %} + +*/ \ No newline at end of file diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.xml.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.xml.twig new file mode 100644 index 0000000..aa52561 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.xml.twig @@ -0,0 +1,24 @@ + + + + + + diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.yml.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.yml.twig new file mode 100644 index 0000000..b340d5d --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.yml.twig @@ -0,0 +1,11 @@ +parameters: +{% block parameters %} +# {{ extension_alias }}.example.class: {{ namespace }}\Example +{% endblock parameters %} + +services: +{% block services %} +# {{ extension_alias }}.example: +# class: %{{ extension_alias }}.example.class% +# arguments: [@service_id, "plain_value", %parameter%] +{% endblock services %} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/Controller.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/Controller.php new file mode 100644 index 0000000..6caf83a --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/Controller.php @@ -0,0 +1,38 @@ + 0 -%} + ${{- action.placeholders|join(', $') -}} + {%- endif -%}) + { + } + +{% endfor -%} +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/ControllerTest.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/ControllerTest.php new file mode 100644 index 0000000..b824d7d --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/ControllerTest.php @@ -0,0 +1,24 @@ +request('GET', '{{ action.route }}'); + } + +{% endfor -%} +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/Template.html.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/Template.html.php new file mode 100644 index 0000000..0652eb5 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/Template.html.php @@ -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/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/Template.html.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/Template.html.twig new file mode 100644 index 0000000..03ced82 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/controller/Template.html.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/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/create.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/create.php.twig new file mode 100644 index 0000000..81bae36 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/create.php.twig @@ -0,0 +1,50 @@ + /** +{% block phpdoc_method_header %} + * Creates a new {{ entity }} entity. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/", name="{{ route_name_prefix }}_create") + * @Method("POST") + * @Template("{{ bundle }}:{{ entity }}:new.html.twig") +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function createAction(Request $request) +{% endblock method_definition %} + { +{% block method_body %} + $entity = new {{ entity_class }}(); + $form = $this->createForm(new {{ entity_class }}Type(), $entity); + $form->bind($request); + + if ($form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist($entity); + $em->flush(); + + {% if 'show' in actions -%} + return $this->redirect($this->generateUrl('{{ route_name_prefix }}_show', array('id' => $entity->getId()))); + {%- else -%} + return $this->redirect($this->generateUrl('{{ route_name_prefix }}')); + {%- endif %} + + } +{% endblock method_body %} + +{% block method_return %} +{% if 'annotation' == format %} + return array( + 'entity' => $entity, + 'form' => $form->createView(), + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:new.html.twig', array( + 'entity' => $entity, + 'form' => $form->createView(), + )); +{% endif %} +{% endblock method_return %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/delete.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/delete.php.twig new file mode 100644 index 0000000..7f2cf0a --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/delete.php.twig @@ -0,0 +1,54 @@ + /** +{% 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, $id) +{% endblock method_definition %} + { +{% block method_body %} + $form = $this->createDeleteForm($id); + $form->bind($request); + + if ($form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id); + + if (!$entity) { + throw $this->createNotFoundException('Unable to find {{ entity }} entity.'); + } + + $em->remove($entity); + $em->flush(); + } +{% endblock method_body %} + +{% block method_return %} + return $this->redirect($this->generateUrl('{{ route_name_prefix }}')); +{% endblock method_return %} + } + +{% block form %} + /** + * Creates a form to delete a {{ entity }} entity by id. + * + * @param mixed $id The entity id + * + * @return \Symfony\Component\Form\Form The form + */ + private function createDeleteForm($id) + { + return $this->createFormBuilder(array('id' => $id)) + ->add('id', 'hidden') + ->getForm() + ; + } +{% endblock form %} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/edit.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/edit.php.twig new file mode 100644 index 0000000..3eb536d --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/edit.php.twig @@ -0,0 +1,47 @@ + + /** +{% 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") + * @Template() +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function editAction($id) +{% endblock method_definition %} + { +{% block method_body %} + $em = $this->getDoctrine()->getManager(); + + $entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id); + + if (!$entity) { + throw $this->createNotFoundException('Unable to find {{ entity }} entity.'); + } + + $editForm = $this->createForm(new {{ entity_class }}Type(), $entity); + $deleteForm = $this->createDeleteForm($id); +{% endblock method_body %} + +{% block method_return %} +{% if 'annotation' == format %} + return array( + 'entity' => $entity, + 'edit_form' => $editForm->createView(), + 'delete_form' => $deleteForm->createView(), + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:edit.html.twig', array( + 'entity' => $entity, + 'edit_form' => $editForm->createView(), + 'delete_form' => $deleteForm->createView(), + )); +{% endif %} +{% endblock method_return %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/index.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/index.php.twig new file mode 100644 index 0000000..ef2b75a --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/index.php.twig @@ -0,0 +1,36 @@ + + /** +{% block phpdoc_method_header %} + * Lists all {{ entity }} entities. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/", name="{{ route_name_prefix }}") + * @Method("GET") + * @Template() +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function indexAction() +{% endblock method_definition %} + { +{% block method_body %} + $em = $this->getDoctrine()->getManager(); + + $entities = $em->getRepository('{{ bundle }}:{{ entity }}')->findAll(); +{% endblock method_body %} + +{% block method_return %} +{% if 'annotation' == format %} + return array( + 'entities' => $entities, + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:index.html.twig', array( + 'entities' => $entities, + )); +{% endif %} +{% endblock method_return %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/new.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/new.php.twig new file mode 100644 index 0000000..d2c78c8 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/new.php.twig @@ -0,0 +1,37 @@ + + /** +{% block phpdoc_method_header %} + * Displays a form to create a new {{ entity }} entity. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/new", name="{{ route_name_prefix }}_new") + * @Method("GET") + * @Template() +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function newAction() +{% endblock method_definition %} + { +{% block method_body %} + $entity = new {{ entity_class }}(); + $form = $this->createForm(new {{ entity_class }}Type(), $entity); +{% endblock method_body %} + +{% block method_return %} +{% if 'annotation' == format %} + return array( + 'entity' => $entity, + 'form' => $form->createView(), + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:new.html.twig', array( + 'entity' => $entity, + 'form' => $form->createView(), + )); +{% endif %} +{% endblock method_return %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/show.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/show.php.twig new file mode 100644 index 0000000..1e86e20 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/show.php.twig @@ -0,0 +1,50 @@ + + /** +{% 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") + * @Template() +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function showAction($id) +{% endblock method_definition %} + { +{% block method_body %} + $em = $this->getDoctrine()->getManager(); + + $entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id); + + if (!$entity) { + throw $this->createNotFoundException('Unable to find {{ entity }} entity.'); + } +{% if 'delete' in actions %} + + $deleteForm = $this->createDeleteForm($id); +{% endif %} +{% endblock method_body %} + +{% block method_return %} +{% if 'annotation' == format %} + return array( + 'entity' => $entity, +{% if 'delete' in actions %} + 'delete_form' => $deleteForm->createView(), +{% endif %} + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:show.html.twig', array( + 'entity' => $entity, +{% if 'delete' in actions %} + 'delete_form' => $deleteForm->createView(), +{%- endif %} + )); +{% endif %} +{% endblock method_return %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/update.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/update.php.twig new file mode 100644 index 0000000..4e36495 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/update.php.twig @@ -0,0 +1,55 @@ + + /** +{% block phpdoc_method_header %} + * Edits an existing {{ entity }} entity. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/{id}", name="{{ route_name_prefix }}_update") + * @Method("PUT") + * @Template("{{ bundle }}:{{ entity }}:edit.html.twig") +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function updateAction(Request $request, $id) +{% endblock method_definition %} + { +{% block method_body %} + $em = $this->getDoctrine()->getManager(); + + $entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id); + + if (!$entity) { + throw $this->createNotFoundException('Unable to find {{ entity }} entity.'); + } + + $deleteForm = $this->createDeleteForm($id); + $editForm = $this->createForm(new {{ entity_class }}Type(), $entity); + $editForm->bind($request); + + if ($editForm->isValid()) { + $em->persist($entity); + $em->flush(); + + return $this->redirect($this->generateUrl('{{ route_name_prefix }}_edit', array('id' => $id))); + } +{% endblock method_body %} + +{% block method_return %} +{% if 'annotation' == format %} + return array( + 'entity' => $entity, + 'edit_form' => $editForm->createView(), + 'delete_form' => $deleteForm->createView(), + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:edit.html.twig', array( + 'entity' => $entity, + 'edit_form' => $editForm->createView(), + 'delete_form' => $deleteForm->createView(), + )); +{% endif %} +{% endblock method_return %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.php.twig new file mode 100644 index 0000000..9906cf4 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.php.twig @@ -0,0 +1,60 @@ +add('{{ route_name_prefix }}', new Route('/', array( + '_controller' => '{{ bundle }}:{{ entity }}:index', +))); +{% endif %} + +{% if 'show' in actions %} +$collection->add('{{ route_name_prefix }}_show', new Route('/{id}/show', array( + '_controller' => '{{ bundle }}:{{ entity }}:show', +))); +{% endif %} + +{% if 'new' in actions %} +$collection->add('{{ route_name_prefix }}_new', new Route('/new', array( + '_controller' => '{{ bundle }}:{{ entity }}:new', +))); + +$collection->add('{{ route_name_prefix }}_create', new Route( + '/create', + array('_controller' => '{{ bundle }}:{{ entity }}:create'), + array('_method' => 'post') +)); +{% endif %} + +{% if 'edit' in actions %} +$collection->add('{{ route_name_prefix }}_edit', new Route('/{id}/edit', array( + '_controller' => '{{ bundle }}:{{ entity }}:edit', +))); + +$collection->add('{{ route_name_prefix }}_update', new Route( + '/{id}/update', + array('_controller' => '{{ bundle }}:{{ entity }}:update'), + array('_method' => 'post|put') +)); +{% endif %} + +{% if 'delete' in actions %} +$collection->add('{{ route_name_prefix }}_delete', new Route( + '/{id}/delete', + array('_controller' => '{{ bundle }}:{{ entity }}:delete'), + array('_method' => 'post|delete') +)); +{% endif %} +{% block body %} + +{% block return %} +return $collection; +{% endblock return %} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.xml.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.xml.twig new file mode 100644 index 0000000..f1538a1 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.xml.twig @@ -0,0 +1,46 @@ + + + + +{% block body %} + + {{ bundle }}:{{ entity }}:index + + + + {{ bundle }}:{{ entity }}:show + + +{% if 'new' in actions %} + + {{ bundle }}:{{ entity }}:new + + + + {{ bundle }}:{{ entity }}:create + post + +{% endif %} + +{% if 'edit' in actions %} + + {{ bundle }}:{{ entity }}:edit + + + + {{ bundle }}:{{ entity }}:update + post|put + +{% endif %} + +{% if 'delete' in actions %} + + {{ bundle }}:{{ entity }}:delete + post|delete + +{% endif %} +{% endblock body %} + + diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.yml.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.yml.twig new file mode 100644 index 0000000..a151b84 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.yml.twig @@ -0,0 +1,40 @@ +{% if 'index' in actions %} +{{ route_name_prefix }}: + pattern: / + defaults: { _controller: "{{ bundle }}:{{ entity }}:index" } +{% endif %} + +{% if 'show' in actions %} +{{ route_name_prefix }}_show: + pattern: /{id}/show + defaults: { _controller: "{{ bundle }}:{{ entity }}:show" } +{% endif %} + +{% if 'new' in actions %} +{{ route_name_prefix }}_new: + pattern: /new + defaults: { _controller: "{{ bundle }}:{{ entity }}:new" } + +{{ route_name_prefix }}_create: + pattern: /create + defaults: { _controller: "{{ bundle }}:{{ entity }}:create" } + requirements: { _method: post } +{% endif %} + +{% if 'edit' in actions %} +{{ route_name_prefix }}_edit: + pattern: /{id}/edit + defaults: { _controller: "{{ bundle }}:{{ entity }}:edit" } + +{{ route_name_prefix }}_update: + pattern: /{id}/update + defaults: { _controller: "{{ bundle }}:{{ entity }}:update" } + requirements: { _method: post|put } +{% endif %} + +{% if 'delete' in actions %} +{{ route_name_prefix }}_delete: + pattern: /{id}/delete + defaults: { _controller: "{{ bundle }}:{{ entity }}:delete" } + requirements: { _method: post|delete } +{% endif %} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/controller.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/controller.php.twig new file mode 100644 index 0000000..d11021a --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/controller.php.twig @@ -0,0 +1,61 @@ +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('Edit')->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/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/tests/others/short_scenario.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/tests/others/short_scenario.php.twig new file mode 100644 index 0000000..9e1b1a4 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/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/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/tests/test.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/tests/test.php.twig new file mode 100644 index 0000000..c07a8fc --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/tests/test.php.twig @@ -0,0 +1,24 @@ +{{ entity }} edit + +
    + + {{ "{{ form_widget(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/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/index.html.twig.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/index.html.twig.twig new file mode 100644 index 0000000..db5d238 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/index.html.twig.twig @@ -0,0 +1,64 @@ +{% block extends %} +{{ "{% extends '::base.html.twig' %}" }} +{% endblock extends %} + +{% block body %} +{{ "{% block body -%}" }} +

    {{ entity }} list

    + + + + + {%- for field, metadata in fields %} + + + + {%- endfor %} + + + + + + {{ '{% for entity in entities %}' }} + + + {%- for field, metadata in fields %} + {%- if loop.first and ('show' in actions) %} + + + + {%- elseif metadata.type in ['date', 'datetime'] %} + + + + {%- else %} + + + + {%- endif %} + + {%- if loop.last %} + + + + {%- endif %} + {%- endfor %} + + + {{ '{% endfor %}' }} + +
    {{ field|capitalize }}Actions
    {{ '{{ entity.' ~ field|replace({'_': ''}) ~ ' }}' }}{{ '{% if entity.' ~ field|replace({'_': ''}) ~ ' %}{{ entity.' ~ field|replace({'_': ''}) ~ '|date(\'Y-m-d H:i:s\') }}{% endif %}' }}{{ '{{ entity.' ~ field|replace({'_': ''}) ~ ' }}' }} + {%- include "crud/views/others/actions.html.twig.twig" %} +
    + + {% if 'new' in actions %} + + {% endif %} +{{ "{% endblock %}" }} +{% endblock body %} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/new.html.twig.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/new.html.twig.twig new file mode 100644 index 0000000..e82f7e1 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/new.html.twig.twig @@ -0,0 +1,19 @@ +{% block extends %} +{{ "{% extends '::base.html.twig' %}" }} +{% endblock extends %} + +{% block body %} +{{ "{% block body -%}" }} +

    {{ entity }} creation

    + +
    + {{ "{{ form_widget(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/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/actions.html.twig.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/actions.html.twig.twig new file mode 100644 index 0000000..2d242c8 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/actions.html.twig.twig @@ -0,0 +1,12 @@ + +
      + + {%- for action in record_actions %} + +
    • + {{ action }} +
    • + + {%- endfor %} + +
    diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/record_actions.html.twig.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/record_actions.html.twig.twig new file mode 100644 index 0000000..d937757 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/record_actions.html.twig.twig @@ -0,0 +1,23 @@ +
      +
    • + + Back to the list + +
    • +{% if ('edit' in actions) and (not hide_edit) %} +
    • + + Edit + +
    • +{% endif %} +{% if ('delete' in actions) and (not hide_delete) %} +
    • +
      + + {{ '{{ form_widget(delete_form) }}' }} + +
      +
    • +{% endif %} +
    diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/show.html.twig.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/show.html.twig.twig new file mode 100644 index 0000000..9e8aa02 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/show.html.twig.twig @@ -0,0 +1,36 @@ +{% block extends %} +{{ "{% extends '::base.html.twig' %}" }} +{% endblock extends %} + +{% block body %} +{{ "{% block body -%}" }} +

    {{ entity }}

    + + + + {%- for field, metadata in fields %} + + + + + {%- if metadata.type in ['date', 'datetime'] %} + + + + {%- else %} + + + + {%- endif %} + + + + {%- endfor %} + + +
    {{ field|capitalize }}{{ '{{ entity.' ~ field|replace({'_': ''}) ~ '|date(\'Y-m-d H:i:s\') }}' }}{{ '{{ entity.' ~ 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/Sensio/Bundle/GeneratorBundle/Resources/skeleton/form/FormType.php.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/form/FormType.php.twig new file mode 100644 index 0000000..c9a48ee --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/form/FormType.php.twig @@ -0,0 +1,40 @@ +add('{{ field }}') + + {%- endfor %} + + ; + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_class' => '{{ namespace }}\Entity{{ entity_namespace ? '\\' ~ entity_namespace : '' }}\{{ entity_class }}' + )); + } + + public function getName() + { + return '{{ form_type_name }}'; + } +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/SensioGeneratorBundle.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/SensioGeneratorBundle.php new file mode 100644 index 0000000..5c4e799 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/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/Sensio/Bundle/GeneratorBundle/composer.json b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/composer.json new file mode 100644 index 0000000..9d849b6 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/composer.json @@ -0,0 +1,32 @@ +{ + "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.2", + "symfony/console": "~2.0" + }, + "require-dev": { + "symfony/doctrine-bridge": "~2.2", + "doctrine/orm": "~2.2,>=2.2.3", + "twig/twig": "~1.11" + }, + "autoload": { + "psr-0": { "Sensio\\Bundle\\GeneratorBundle": "" } + }, + "target-dir": "Sensio/Bundle/GeneratorBundle", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/LICENSE b/vendor/swiftmailer/swiftmailer/LICENSE new file mode 100644 index 0000000..fc8a5de --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/vendor/swiftmailer/swiftmailer/VERSION b/vendor/swiftmailer/swiftmailer/VERSION new file mode 100644 index 0000000..eaecc91 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/VERSION @@ -0,0 +1 @@ +Swift-5.0.0 diff --git a/vendor/swiftmailer/swiftmailer/composer.json b/vendor/swiftmailer/swiftmailer/composer.json new file mode 100644 index 0000000..c77793a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/composer.json @@ -0,0 +1,28 @@ +{ + "name": "swiftmailer/swiftmailer", + "type": "library", + "description": "Swiftmailer, free feature-rich PHP mailer", + "keywords": ["mail","mailer"], + "homepage": "http://swiftmailer.org", + "license": "MIT", + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "php": ">=5.2.4" + }, + "autoload": { + "files": ["lib/swift_required.php"] + }, + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/doc/headers.rst b/vendor/swiftmailer/swiftmailer/doc/headers.rst new file mode 100644 index 0000000..5a7b539 --- /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..9820653 --- /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 +the 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..80bdb42 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/installing.rst @@ -0,0 +1,200 @@ +Installing the Library +====================== + +Installing Swift Mailer is trivial. Usually it's just a case of uploading the +extracted source files to your web server. + +Installing from PEAR +-------------------- + +If you want to install Swift Mailer globally on your machine, the easiest +installation method is using the PEAR channel. + +To install the Swift Mailer PEAR package: + +* Run the command ``pear channel-discover pear.swiftmailer.org``. + +* Then, run the command ``pear install swift/swift``. + +Installing from a Package +------------------------- + +Most users will download a package from the Swift Mailer website and install +Swift Mailer using this. + +If you downloaded Swift Mailer as a ``.tar.gz`` or +``.zip`` file installation is as simple as extracting the archive +and uploading it to your web server. + +Extracting the Library +~~~~~~~~~~~~~~~~~~~~~~ + +You extract the archive by using your favorite unarchiving tool such as +``tar`` or 7-Zip. + +You will need to have access to a program that can open uncompress the +archive. On Windows computers, 7-Zip will work. On Mac and Linux systems you +can use ``tar`` on the command line. + +To extract your downloaded package: + +* Use the "extract" facility of your archiving software. + +The source code will be placed into a directory with the same name as the +archive (e.g. Swift-4.0.0-b1). + +The following example shows the process on Mac OS X and Linux systems using +the ``tar`` command. + +.. code-block:: bash + + $ ls + Swift-4.0.0-dev.tar.gz + $ tar xvzf Swift-4.0.0-dev.tar.gz + Swift-4.0.0-dev/ + Swift-4.0.0-dev/lib/ + Swift-4.0.0-dev/lib/classes/ + Swift-4.0.0-dev/lib/classes/Swift/ + Swift-4.0.0-dev/lib/classes/Swift/ByteStream/ + Swift-4.0.0-dev/lib/classes/Swift/CharacterReader/ + Swift-4.0.0-dev/lib/classes/Swift/CharacterReaderFactory/ + Swift-4.0.0-dev/lib/classes/Swift/CharacterStream/ + Swift-4.0.0-dev/lib/classes/Swift/Encoder/ + + ... etc etc ... + + Swift-4.0.0-dev/tests/unit/Swift/Transport/LoadBalancedTransportTest.php + Swift-4.0.0-dev/tests/unit/Swift/Transport/SendmailTransportTest.php + Swift-4.0.0-dev/tests/unit/Swift/Transport/StreamBufferTest.php + $ cd Swift-4.0.0-dev + $ ls + CHANGES LICENSE ... + $ + +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 ... + $ + +Uploading to your Host +---------------------- + +You only need to upload the "lib/" directory to your web host for production +use. All other files and directories are support files not needed in +production. + +You will need FTP, ``rsync`` or similar software installed in order to upload +the "lib/" directory to your web host. + +To upload Swift Mailer: + +* Open your FTP program, or a command line if you prefer rsync/scp. + +* Upload the "lib/" directory to your hosting account. + +The files needed to use Swift Mailer should now be accessible to PHP on your +host. + +The following example shows show you can upload the files using +``rsync`` on Linux or OS X. + +.. note:: + + You do not need to place the files inside your web root. They only need to + be in a place where your PHP scripts can "include" them. + + .. code-block:: bash + + $ rsync -rvz lib d11wtq@swiftmailer.org:swiftmailer + building file list ... done + created directory swiftmailer + lib/ + lib/mime_types.php + lib/preferences.php + lib/swift_required.php + lib/classes/ + lib/classes/Swift/ + lib/classes/Swift/Attachment.php + lib/classes/Swift/CharacterReader.php + ... etc etc ... + lib/dependency_maps/ + lib/dependency_maps/cache_deps.php + lib/dependency_maps/mime_deps.php + lib/dependency_maps/transport_deps.php + + sent 151692 bytes received 2974 bytes 5836.45 bytes/sec + total size is 401405 speedup is 2.60 + $ + +.. _`github`: http://github.com/swiftmailer/swiftmailer + +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); + } diff --git a/vendor/swiftmailer/swiftmailer/doc/introduction.rst b/vendor/swiftmailer/swiftmailer/doc/introduction.rst new file mode 100644 index 0000000..39ab034 --- /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..4a5b54f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/messages.rst @@ -0,0 +1,1046 @@ +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. + +.. 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_SignedMessage::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_SignedMessage::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_SignedMessage::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..4a2efa9 --- /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' +``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..b3ba9af --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/sending.rst @@ -0,0 +1,592 @@ +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. + +.. 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..ecaf0ef --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php @@ -0,0 +1,81 @@ +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..87b6428 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php @@ -0,0 +1,183 @@ +_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 + * + * @return integer + * + * @throws Swift_IoException + */ + 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->_filter($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(); + } + } + + // -- Private methods + + /** 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..5c16248 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php @@ -0,0 +1,186 @@ +_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 integer $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 integer $byteOffset + * + * @return boolean + */ + 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..405f8b0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php @@ -0,0 +1,225 @@ +_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 integer $length + * + * @return string + * + * @throws Swift_IoException + */ + 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); + + return $bytes; + } else { + $this->_resetReadHandle(); + + return false; + } + } + + /** + * Move the internal read pointer to $byteOffset in the stream. + * + * @param integer $byteOffset + * + * @return boolean + */ + public function setReadPointer($byteOffset) + { + if (isset($this->_reader)) { + $this->_seekReadStreamToPosition($byteOffset); + } + $this->_offset = $byteOffset; + } + + // -- Private methods + + /** 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..f35f885 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php @@ -0,0 +1,44 @@ +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..df64d8a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php @@ -0,0 +1,69 @@ + + */ +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 integer $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return integer + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars); + + /** + * Returns the mapType, see constants. + * + * @return integer + */ + 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 integer $size + * + * @return integer + */ + 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 integer + */ + 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..49d7398 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php @@ -0,0 +1,99 @@ + + */ +class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterReader +{ + /** + * The number of bytes in a single character. + * + * @var integer + */ + private $_width; + + /** + * Creates a new GenericFixedWidthReader using $width bytes per character. + * + * @param integer $width + */ + public function __construct($width) + { + $this->_width = $width; + } + + /** + * Returns the complete character map. + * + * @param string $string + * @param integer $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return integer + */ + 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 integer + */ + 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 integer $size + * + * @return integer + */ + 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 integer + */ + 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..18f3b04 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php @@ -0,0 +1,85 @@ +"\x07F") { // Invalid char + $currentMap[$i+$startOffset]=$string[$i]; + } + } + + return $strlen; + } + + /** + * Returns mapType + * + * @return integer 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 integer $size + * + * @return integer + */ + 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 integer + */ + 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..dd3a60f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php @@ -0,0 +1,181 @@ + + */ +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 integer $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return integer + */ + 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 integer 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 integer $size + * + * @return integer + */ + 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 integer + */ + 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..d653b81 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php @@ -0,0 +1,28 @@ +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..2946200 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php @@ -0,0 +1,91 @@ +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 integer $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 integer $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 integer $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..98aabab --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php @@ -0,0 +1,277 @@ + + */ + +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 integer + */ + private $_datasSize = 0; + + /** + * Map. + * + * @var mixed + */ + private $_map; + + /** + * Map Type. + * + * @var integer + */ + private $_mapType = 0; + + /** + * Number of characters in the stream. + * + * @var integer + */ + private $_charCount = 0; + + /** + * Position in the stream. + * + * @var integer + */ + 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 integer $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 integer $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 integer $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..58d5275 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php @@ -0,0 +1,64 @@ + + * + * 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). + * + * @package Swift + * @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 integer $limit + */ + public function setMessageLimit($limit) + { + $this->_message_limit = (int) $limit; + } + + /** + * Gets the maximum number of messages to send per flush. + * + * @return integer The limit + */ + public function getMessageLimit() + { + return $this->_message_limit; + } + + /** + * Sets the time limit (in seconds) per flush. + * + * @param integer $limit The limit + */ + public function setTimeLimit($limit) + { + $this->_time_limit = (int) $limit; + } + + /** + * Gets the time limit (in seconds) per flush. + * + * @return integer 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..b717861 --- /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 boolean + */ + 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 + * + * @return mixed + * + * @throws Swift_DependencyException If the dependency is not found + */ + 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; + } + + // -- Private methods + + /** 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..b3f0170 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php @@ -0,0 +1,28 @@ +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..53e88b8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php @@ -0,0 +1,29 @@ += $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..61cf31b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php @@ -0,0 +1,286 @@ + '=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 integer $firstLineOffset, optional + * @param integer $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); + } + + // -- Protected methods + + /** + * Encode the given byte array into a verbatim QP form. + * + * @param integer[] $bytes + * @param integer $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 integer $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; + } +} 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..37e30c1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php @@ -0,0 +1,86 @@ +_charStream = $charStream; + } + + /** + * Takes an unencoded string and produces a string encoded according to + * RFC 2231 from it. + * + * @param string $string + * @param integer $firstLineOffset + * @param integer $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); + } +} 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..9639194 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php @@ -0,0 +1,66 @@ +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..fa4f444 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php @@ -0,0 +1,67 @@ +_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..6800904 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php @@ -0,0 +1,26 @@ +_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 boolean $cancel, optional + */ + public function cancelBubble($cancel = true) + { + $this->_bubbleCancelled = $cancel; + } + + /** + * Returns true if this Event will not bubble any further up the stack. + * + * @return boolean + */ + 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..6b9117c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php @@ -0,0 +1,68 @@ +_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 boolean + */ + 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..a39ba43 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php @@ -0,0 +1,26 @@ +_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 integer $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 integer + */ + 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..bc914f5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php @@ -0,0 +1,33 @@ +_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 boolean $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); + } + + // -- Private methods + + /** 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..d8b5316 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php @@ -0,0 +1,29 @@ +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..1555037 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php @@ -0,0 +1,47 @@ +_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..709abda --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php @@ -0,0 +1,26 @@ +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..e458c07 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages on the filesystem. + * + * @package Swift + * @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 boolean + */ + 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 integer $limit + */ + public function setRetryLimit($limit) + { + $this->_retryLimit=$limit; + } + + /** + * Queues a message. + * + * @param Swift_Mime_Message $message The message to store + * + * @return boolean + * + * @throws Swift_IoException + */ + 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 integer $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 integer The number of sent e-mail's + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) + { + if (!$transport->isStarted()) { + $transport->start(); + } + + $failedRecipients = (array) $failedRecipients; + $count = 0; + $time = time(); + foreach (new DirectoryIterator($this->_path) 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 integer $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..567633e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php @@ -0,0 +1,26 @@ +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..ae81e5d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php @@ -0,0 +1,77 @@ +_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 integer $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 integer $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 boolean + */ + 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]); + } + + // -- Private methods + + /** + * 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..740897a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php @@ -0,0 +1,328 @@ +_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 integer $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 integer $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 + * + * @return string + * + * @throws Swift_IoException + */ + 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 boolean + */ + 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]); + } + } + + // -- Private methods + + /** + * 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 integer $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..f4f8adb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php @@ -0,0 +1,53 @@ +_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..6eb3db7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php @@ -0,0 +1,47 @@ +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..6c57939 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php @@ -0,0 +1,47 @@ +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..b6703de --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php @@ -0,0 +1,115 @@ +_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 integer + */ + 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..37e98da --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php @@ -0,0 +1,57 @@ +_recipients = $recipients; + } + + /** + * Returns true only if there are more recipients to send to. + * + * @return boolean + */ + 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..073bce1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php @@ -0,0 +1,34 @@ + '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..764b5aa --- /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. + * + * @package Swift + * @author Fabien Potencier + */ +class Swift_MemorySpool implements Swift_Spool +{ + protected $messages = array(); + + /** + * Tests if this Transport mechanism has started. + * + * @return boolean + */ + 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 boolean Whether the operation has succeeded + */ + public function queueMessage(Swift_Mime_Message $message) + { + $this->messages[] = $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 integer 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..158ea25 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php @@ -0,0 +1,85 @@ +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 + )); + } + + public function __wakeup() + { + Swift_DependencyContainer::getInstance()->createDependenciesFor('mime.message'); + } +} 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..faf358f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php @@ -0,0 +1,155 @@ +setDisposition('attachment'); + $this->setContentType('application/octet-stream'); + $this->_mimeTypes = $mimeTypes; + } + + /** + * Get the nesting level used for this attachment. + * + * Always returns {@link LEVEL_MIXED}. + * + * @return integer + */ + 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 integer + */ + public function getSize() + { + return $this->_getHeaderParameter('Content-Disposition', 'size'); + } + + /** + * Set the file size of this attachment. + * + * @param integer $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..bfd41ed --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php @@ -0,0 +1,26 @@ += $maxLineLength || 76 < $maxLineLength) { + $maxLineLength = 76; + } + + $remainder = 0; + + while (false !== $bytes = $os->read(8190)) { + $encoded = base64_encode($bytes); + $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); + } + } + + /** + * 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..e6e9e52 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php @@ -0,0 +1,125 @@ +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 integer $firstLineOffset + * @param integer $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 integer $firstLineOffset if first line needs to be shorter + * @param integer $maxLineLength 0 indicates the default length for this encoding + * + * @return string + * + * @throws RuntimeException + */ + 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 integer $firstLineOffset ignored + * @param integer $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 integer $firstLineOffset ignored + * @param integer $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) + { + } + + // -- Private methods + + /** + * A safer (but weaker) wordwrap for unicode. + * + * @param string $string + * @param integer $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..059c53d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php @@ -0,0 +1,125 @@ +_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 integer $firstLineOffset + * @param integer $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..491409a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php @@ -0,0 +1,90 @@ + + */ +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; + } + + /** + * {@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..8f1f9b5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php @@ -0,0 +1,65 @@ + + */ +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..05e06e2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php @@ -0,0 +1,47 @@ +setDisposition('inline'); + $this->setId($this->getId()); + } + + /** + * Get the nesting level of this EmbeddedFile. + * + * Returns {@see LEVEL_RELATED}. + * + * @return integer + */ + 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..e7e6f20 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php @@ -0,0 +1,26 @@ +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..55d3ab8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php @@ -0,0 +1,95 @@ +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..c9bbe71 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php @@ -0,0 +1,67 @@ +_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 integer $firstLineOffset optional + * @param integer $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..7cab133 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php @@ -0,0 +1,80 @@ +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 integer $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. + * + * @return string + * + * @throws Swift_RfcComplianceException + */ + 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 boolean $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 boolean + */ + 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 integer $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 boolean $condition + */ + protected function clearCachedValueIf($condition) + { + if ($condition) { + $this->setCachedValue(null); + } + } + + // -- Private methods + + /** + * 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..9127cc2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php @@ -0,0 +1,127 @@ + + * + * + * + * @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 integer $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 integer $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..1d00015 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php @@ -0,0 +1,183 @@ +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() + * + * @return string + * + * @throws Swift_RfcComplianceException + */ + 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..0fda5ae --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php @@ -0,0 +1,358 @@ +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()} + * + * @return array + * + * @throws Swift_RfcComplianceException + */ + 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() + * + * @return string[] + * + * @throws Swift_RfcComplianceException + */ + 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() + * + * @return string + * + * @throws Swift_RfcComplianceException + */ + 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 boolean $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 + * + * @return string + * + * @throws Swift_RfcComplianceException + */ + 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 boolean + */ + protected function tokenNeedsEncoding($token) + { + return preg_match('/[,;]/', $token) || parent::tokenNeedsEncoding($token); + } + + // -- Private methods + + /** + * 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/ParameterizedHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php new file mode 100644 index 0000000..3ef628c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php @@ -0,0 +1,265 @@ +_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; + } + + // -- Protected methods + + /** + * 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; + } + + // -- Private methods + + /** + * 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 boolean $encoded + * @param boolean $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..bfecf3f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php @@ -0,0 +1,146 @@ +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..2de49b4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php @@ -0,0 +1,114 @@ +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..bce3af3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php @@ -0,0 +1,225 @@ + '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..bc9f2ad --- /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 boolean + */ + public function getDelSp() + { + return ($this->_getHeaderParameter('Content-Type', 'delsp') == 'yes') + ? true + : false; + } + + /** + * Turn delsp on or off for this entity. + * + * @param boolean $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); + } + + // -- Protected methods + + /** 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($charset, 'utf-8//TRANSLIT//IGNORE', $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..95172ec --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php @@ -0,0 +1,36 @@ +_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 integer|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); + } + + // -- Private methods + + /** 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..e06f936 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php @@ -0,0 +1,387 @@ +_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 integer $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 integer $index + * + * @return boolean + */ + 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 integer $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 integer $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 integer $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(); + } + + // -- Private methods + + /** 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); + } + } + } +} 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..b203644 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php @@ -0,0 +1,655 @@ +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 integer $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 integer + */ + 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 $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 string + */ + 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 and array should be used. + * + * 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 integer $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 integer + */ + 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); + } + } + + // -- Protected methods + + /** @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; + } + + // -- Private methods + + /** 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..36e10ff --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php @@ -0,0 +1,857 @@ + 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 integer + */ + 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() + { + return $this->_headers->has($this->_getIdField()) ? current((array) $this->_getHeaderFieldModel($this->_getIdField())) : $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 integer + */ + 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 integer $length + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setMaxLineLength($length) + { + $this->_maxLineLength = $length; + + return $this; + } + + /** + * Get all children added to this entity. + * + * @return array of Swift_Mime_Entity + */ + public function getChildren() + { + return $this->_children; + } + + /** + * Set all children of this entity. + * + * @param array $children Swift_Mime_Entity instances + * @param integer $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 + * + * @return Swift_Mime_SimpleMimeEntity + * + * @throws Swift_RfcComplianceException + */ + 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"); + } + } + + // -- Protected methods + + /** + * 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 methods + + private function _readStream(Swift_OutputByteStream $os) + { + $string = ''; + while (false !== $bytes = $os->read(8192)) { + $string .= $bytes; + } + + 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 . '>' + ); + } + } +} 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..10a4f3c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php @@ -0,0 +1,61 @@ +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..335c479 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php @@ -0,0 +1,40 @@ + + * + * 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. + * + * @package Swift + * @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..2ff7449 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php @@ -0,0 +1,48 @@ +setThreshold($threshold); + $this->setSleepTime($sleep); + $this->_sleeper = $sleeper; + } + + /** + * Set the number of emails to send before restarting. + * + * @param integer $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 integer $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 integer $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..8794aac --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php @@ -0,0 +1,166 @@ +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..3269c69 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php @@ -0,0 +1,33 @@ + + * $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()); + } + + // -- Private methods + + /** 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..1f1e443 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php @@ -0,0 +1,70 @@ +_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..81c1d9b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php @@ -0,0 +1,38 @@ +_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(); + $this->_logger->add(sprintf("!! %s", $message)); + $message .= PHP_EOL; + $message .= 'Log data:' . PHP_EOL; + $message .= $this->_logger->dump(); + $evt->cancelBubble(); + throw new Swift_TransportException($message); + } +} 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..eb362ef --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php @@ -0,0 +1,74 @@ +_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..c542169 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php @@ -0,0 +1,60 @@ +_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..35d5de5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php @@ -0,0 +1,77 @@ +messages = array(); + } + + /** + * Get the message list + * + * @return array + */ + public function getMessages() + { + return $this->messages; + } + + /** + * Get the message count + * + * @return integer 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..d241721 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php @@ -0,0 +1,33 @@ +_host = $host; + $this->_port = $port; + $this->_crypto = $crypto; + } + + /** + * Create a new PopBeforeSmtpPlugin for $host and $port. + * + * @param string $host + * @param integer $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 integer $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 Methods + + 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..a27db78 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php @@ -0,0 +1,204 @@ +_recipient = $recipient; + $this->_whitelist = $whitelist; + } + + /** + * Set the recipient of all messages. + * + * @param string $recipient + */ + public function setRecipient($recipient) + { + $this->_recipient = $recipient; + } + + /** + * Get the recipient of all messages. + * + * @return int + */ + 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()); + } + + // Add hard coded recipient + $message->addTo($this->_recipient); + + // Filter remaining headers against whitelist + $this->_filterHeaderSet($headers, 'To'); + $this->_filterHeaderSet($headers, 'Cc'); + $this->_filterHeaderSet($headers, 'Bcc'); + } + + /** + * 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 ($recipient === $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 methods + + 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'); + } + + 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..0dfa22d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php @@ -0,0 +1,34 @@ +_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..844e2a1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php @@ -0,0 +1,61 @@ +_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..7b8c188 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php @@ -0,0 +1,41 @@ +" . 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..c491f63 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php @@ -0,0 +1,26 @@ +_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 integer $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(); + } + } + + // -- Private methods + + /** + * Get a number of seconds to sleep for. + * + * @param integer $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 integer $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..12dd09b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php @@ -0,0 +1,26 @@ +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 boolean $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..4b6eed5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php @@ -0,0 +1,28 @@ +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..1a073e4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php @@ -0,0 +1,165 @@ + + */ +class Swift_SignedMessage extends Swift_Message +{ + /** + * @var Swift_Signers_HeaderSigner[] + */ + private $headerSigners = array(); + + /** + * @var Swift_Signers_BodySigner[] + */ + private $bodySigners = array(); + + /** + * @var array + */ + private $savedMessage = array(); + + /** + * Create a new Message. + * + * @param string $subject + * @param string $body + * @param string $contentType + * @param string $charset + * + * @return Swift_SignedMessage + */ + public static function newInstance($subject = null, $body = null, $contentType = null, $charset = null) + { + return new self($subject, $body, $contentType, $charset); + } + + /** + * Attach a new signature handler to the message. + * + * @param Swift_Signer $signer + * @return Swift_SignedMessage + */ + 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; + } + + /** + * Get this message as a complete string. + * + * @return string + */ + public function 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) + { + $this->saveMessage(); + $this->doSign(); + + parent::toByteStream($is); + $this->restoreMessage(); + } + + 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()); + } + } + + 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(''); + } + } + + 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); + } + } + } + + 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); + } + } + } + } + + protected function restoreMessage() + { + $this->setBody($this->savedMessage['body']); + $this->setChildren($this->savedMessage['children']); + + $this->restoreHeaders(); + $this->savedMessage = array(); + } +} 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..865f557 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php @@ -0,0 +1,22 @@ + + */ +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..3a653a3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php @@ -0,0 +1,35 @@ + + */ +interface Swift_Signers_BodySigner extends Swift_Signer +{ + /** + * Change the Swift_Signed_Message to apply the singing. + * + * @param Swift_Signed_Message $message + * + * @return Swift_Signers_BodySigner + */ + public function signMessage(Swift_SignedMessage $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..09a2ccd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php @@ -0,0 +1,669 @@ + + */ +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 boolean + */ + 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 boolean + */ + 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; + } + + 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 + * @return int + * @throws Swift_IoException + */ + 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 => $el) { + if ($el == $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 boolean $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; + } + + private function _getEncryptedHash() + { + $signature = ''; + switch ($this->_hashAlgorithm) { + case 'rsa-sha1': + $algorithm = 'sha1'; + break; + case 'rsa-sha256': + $algorithm = '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, $this->_privateKey, $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..f817e45 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php @@ -0,0 +1,505 @@ + + */ +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 unknown_type + */ + protected $_signerIdentity; + + /** + * Must we embed signed headers? + * + * @var boolean + */ + protected $_debugHeaders = false; + + // work variables + /** + * Headers used to generate hash + * + * @var array + */ + private $_signedHeaders = array(); + + /** + * If debugHeaders is set store debugDatas here + * + * @var string + */ + private $_debugHeadersData = ''; + + /** + * Stores the signature header + * + * @var Swift_Mime_Headers_ParameterizedHeader + */ + protected $_domainKeyHeader; + + /** + * Hash Handler + * + * @var hash_ressource + */ + 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; + } + + /** + * 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 + * @return int + * @throws Swift_IoException + * @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 => $el) { + if ($el == $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 boolean $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) + { + echo $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 = ''; + } + + 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, '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..47091db --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php @@ -0,0 +1,67 @@ + + */ +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/SMimeSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php new file mode 100644 index 0000000..47c0a3d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php @@ -0,0 +1,430 @@ + + */ +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; + + /** + * @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 static($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 integer $signOptions Bitwise operator options for openssl_pkcs7_sign() + * + * @return Swift_Signers_SMimeSigner + */ + public function setSignCertificate($certificate, $privateKey = null, $signOptions = PKCS7_DETACHED) + { + $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; + + 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 integer $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 Boolean + */ + public function isSignThenEncrypt() + { + return $this->signThenEncrypt; + } + + /** + * Resets internal states. + * + * @return Swift_Signers_SMimeSigner + */ + public function reset() + { + return $this; + } + + /** + * Change the Swift_SignedMessage to apply the singing. + * + * @param Swift_SignedMessage $message + * + * @return Swift_Signers_SMimeSigner + */ + public function signMessage(Swift_SignedMessage $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_SignedMessage $mimeEntity + */ + protected function toSMimeByteStream(Swift_InputByteStream $inputStream, Swift_SignedMessage $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_SignedMessage $message + * + * @return Swift_Message + */ + protected function createMessage(Swift_SignedMessage $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(); + + if (!openssl_pkcs7_sign($outputStream->getPath(), $signedMessageStream->getPath(), $this->signCertificate, $this->signPrivateKey, array(), $this->signOptions)) { + 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())) { + 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_SignedMessage. + * + * @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..4fc750a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php @@ -0,0 +1,53 @@ +createDependenciesFor('transport.smtp') + ); + + $this->setHost($host); + $this->setPort($port); + $this->setEncryption($security); + } + + /** + * Create a new SmtpTransport instance. + * + * @param string $host + * @param integer $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..981c178 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Interface for spools. + * + * @package Swift + * @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 boolean + */ + public function isStarted(); + + /** + * Queues a message. + * + * @param Swift_Mime_Message $message The message to store + * + * @return boolean 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 integer 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..8a135d4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in a queue. + * + * @package Swift + * @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..8b887bf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php @@ -0,0 +1,36 @@ +_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 boolean + */ + 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 integer $_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..ca7454e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php @@ -0,0 +1,67 @@ +_search = $search; + $this->_replace = $replace; + } + + /** + * Returns true if based on the buffer passed more bytes should be buffered. + * + * @param string $buffer + * + * @return boolean + */ + 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..bb4af77 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php @@ -0,0 +1,46 @@ +_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..f3bcbed --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php @@ -0,0 +1,28 @@ +_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 boolean + */ + 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)) { + throw new Swift_TransportException( + 'Cannot send message without a sender address' + ); + } + + $to = (array) $message->getTo(); + $cc = (array) $message->getCc(); + $bcc = (array) $message->getBcc(); + + $message->setBcc(array()); + + try { + $sent += $this->_sendTo($message, $reversePath, $to, $failedRecipients); + $sent += $this->_sendCc($message, $reversePath, $cc, $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; + } + + // -- Protected methods + + /** 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); + } + + return $response; + } + + // -- Private methods + + /** 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 the given Cc: recipients */ + private function _sendCc(Swift_Mime_Message $message, $reversePath, array $cc, array &$failedRecipients) + { + if (empty($cc)) { + return 0; + } + + return $this->_doMailTransaction($message, $reversePath, array_keys($cc), + $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..80b4712 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php @@ -0,0 +1,83 @@ +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..deca3a5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php @@ -0,0 +1,53 @@ +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/PlainAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php new file mode 100644 index 0000000..ffa9af3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php @@ -0,0 +1,52 @@ +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/AuthHandler.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php new file mode 100644 index 0000000..40b0908 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php @@ -0,0 +1,268 @@ +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 boolean + */ + 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() + { + } + + // -- Protected methods + + /** + * 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..0c6dc2e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php @@ -0,0 +1,37 @@ +. + * + * @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 boolean $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..19e2d77 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php @@ -0,0 +1,393 @@ + '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 integer $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 integer $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 $this->_params['sourceIp']; + } + + /** + * 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); + } + + // -- Protected methods + + /** 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) + ); + } + + // -- Private methods + + /** 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..d0d3f69 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php @@ -0,0 +1,90 @@ +_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 methods + + 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..7559ebf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php @@ -0,0 +1,69 @@ +_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 boolean + */ + 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); + } + } + + // -- Protected methods + + /** + * 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..a9bff69 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php @@ -0,0 +1,34 @@ +_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) { + throw 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); + } + + // -- Private methods + + /** 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..ce136d3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php @@ -0,0 +1,88 @@ + + * + * 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. + * + * @package Swift + * @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 boolean + */ + 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 integer 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'); + } + + return 0; + } + + /** + * 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..95c2e4a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php @@ -0,0 +1,163 @@ + 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' . $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; + } + + // -- Protected methods + + /** 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..91157e5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in a queue. + * + * @package Swift + * @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 boolean + */ + 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 integer 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_SUCCESS : 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..c42ee00 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php @@ -0,0 +1,315 @@ +_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 integer $sequence of last write to scan from + * + * @return string + * + * @throws Swift_IoException + */ + 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 integer $length + * + * @return string|boolean + * + * @throws Swift_IoException + */ + 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) + { + } + + // -- Protected methods + + /** 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) + && fwrite($this->_in, $bytes)) + { + return ++$this->_sequence; + } + } + + // -- Private methods + + /** + * 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..48e41d1 --- /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 boolean + */ + public static function email($email) + { + if (self::$grammar===null) { + self::$grammar = Swift_DependencyContainer::getInstance() + ->lookup('mime.grammar'); + } + + return 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..a13472e --- /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..f5605a0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php @@ -0,0 +1,68 @@ +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' + ) + )) + + ->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.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..3baebb2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/mime_types.php @@ -0,0 +1,76 @@ + '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', + 'gif' => 'image/gif', + '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', + 'png' => 'image/png', + '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', + 'txt' => 'text/plain', + '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', + 'zip' => 'application/zip' +); diff --git a/vendor/swiftmailer/swiftmailer/lib/preferences.php b/vendor/swiftmailer/swiftmailer/lib/preferences.php new file mode 100644 index 0000000..4223943 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/preferences.php @@ -0,0 +1,35 @@ +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. + +// The @ operator in front of is_writable calls is to avoid PHP warnings +// when using open_basedir +$tmp = getenv('TMPDIR'); +if ($tmp && @is_writable($tmp)) { + $preferences + ->setTempDir($tmp) + ->setCacheType('disk'); +} elseif (function_exists('sys_get_temp_dir') && @is_writable(sys_get_temp_dir())) { + $preferences + ->setTempDir(sys_get_temp_dir()) + ->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..5897e6f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/swift_init.php @@ -0,0 +1,28 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle; + +use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Assetic integration. + * + * @author Kris Wallsmith + */ +class AsseticBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + $container->addCompilerPass(new Compiler\TemplateResourcesPass()); + $container->addCompilerPass(new Compiler\CheckClosureFilterPass()); + $container->addCompilerPass(new Compiler\CheckCssEmbedFilterPass()); + $container->addCompilerPass(new Compiler\CheckYuiFilterPass()); + $container->addCompilerPass(new Compiler\SprocketsFilterPass()); + $container->addCompilerPass(new Compiler\TemplatingPass()); + $container->addCompilerPass(new Compiler\AssetFactoryPass()); + $container->addCompilerPass(new Compiler\AssetManagerPass()); + $container->addCompilerPass(new Compiler\FilterManagerPass()); + $container->addCompilerPass(new Compiler\RouterResourcePass()); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/CacheWarmer/AssetManagerCacheWarmer.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/CacheWarmer/AssetManagerCacheWarmer.php new file mode 100644 index 0000000..e9858bf --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/CacheWarmer/AssetManagerCacheWarmer.php @@ -0,0 +1,41 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\CacheWarmer; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; + +/** + * The AssetManagerCacheWarmer warms up the formula loader. + * + * @author Kris Wallsmith + */ +class AssetManagerCacheWarmer implements CacheWarmerInterface +{ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function warmUp($cacheDir) + { + $am = $this->container->get('assetic.asset_manager'); + $am->load(); + } + + public function isOptional() + { + return true; + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Command/DumpCommand.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Command/DumpCommand.php new file mode 100644 index 0000000..2d543cd --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Command/DumpCommand.php @@ -0,0 +1,235 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Command; + +use Assetic\Asset\AssetCollectionInterface; +use Assetic\Asset\AssetInterface; +use Assetic\Util\VarUtils; +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; + +/** + * Dumps assets to the filesystem. + * + * @author Kris Wallsmith + */ +class DumpCommand extends ContainerAwareCommand +{ + private $basePath; + private $verbose; + private $am; + + protected function configure() + { + $this + ->setName('assetic:dump') + ->setDescription('Dumps all assets to the filesystem') + ->addArgument('write_to', InputArgument::OPTIONAL, 'Override the configured asset root') + ->addOption('watch', null, InputOption::VALUE_NONE, 'Check for changes every second, debug mode only') + ->addOption('force', null, InputOption::VALUE_NONE, 'Force an initial generation of all assets (used with --watch)') + ->addOption('period', null, InputOption::VALUE_REQUIRED, 'Set the polling period in seconds (used with --watch)', 1) + ; + } + + protected function initialize(InputInterface $input, OutputInterface $output) + { + $this->basePath = $input->getArgument('write_to') ?: $this->getContainer()->getParameter('assetic.write_to'); + $this->verbose = $input->getOption('verbose'); + $this->am = $this->getContainer()->get('assetic.asset_manager'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln(sprintf('Dumping all %s assets.', $input->getOption('env'))); + $output->writeln(sprintf('Debug mode is %s.', $this->am->isDebug() ? 'on' : 'off')); + $output->writeln(''); + + if (!$input->getOption('watch')) { + foreach ($this->am->getNames() as $name) { + $this->dumpAsset($name, $output); + } + } elseif (!$this->am->isDebug()) { + throw new \RuntimeException('The --watch option is only available in debug mode.'); + } else { + $this->watch($input, $output); + } + } + + /** + * Watches the asset manager for changes. + * + * This method includes an infinite loop the continuously polls the asset + * manager for changes. + * + * @param InputInterface $input The command input + * @param OutputInterface $output The command output + */ + private function watch(InputInterface $input, OutputInterface $output) + { + $cache = sys_get_temp_dir().'/assetic_watch_'.substr(sha1($this->basePath), 0, 7); + if ($input->getOption('force') || !file_exists($cache)) { + $previously = array(); + } else { + $previously = unserialize(file_get_contents($cache)); + if (!is_array($previously)) { + $previously = array(); + } + } + + $error = ''; + while (true) { + try { + foreach ($this->am->getNames() as $name) { + if ($this->checkAsset($name, $previously)) { + $this->dumpAsset($name, $output); + } + } + + // reset the asset manager + $this->am->clear(); + $this->am->load(); + + file_put_contents($cache, serialize($previously)); + $error = ''; + } catch (\Exception $e) { + if ($error != $msg = $e->getMessage()) { + $output->writeln('[error] '.$msg); + $error = $msg; + } + } + + sleep($input->getOption('period')); + } + } + + /** + * Checks if an asset should be dumped. + * + * @param string $name The asset name + * @param array &$previously An array of previous visits + * + * @return Boolean Whether the asset should be dumped + */ + private function checkAsset($name, array &$previously) + { + $formula = $this->am->hasFormula($name) ? serialize($this->am->getFormula($name)) : null; + $asset = $this->am->get($name); + + $mtime = 0; + foreach ($this->getAssetVarCombinations($asset) as $combination) { + $asset->setValues($combination); + $mtime = max($mtime, $this->am->getLastModified($asset)); + } + + if (isset($previously[$name])) { + $changed = $previously[$name]['mtime'] != $mtime || $previously[$name]['formula'] != $formula; + } else { + $changed = true; + } + + $previously[$name] = array('mtime' => $mtime, 'formula' => $formula); + + return $changed; + } + + /** + * Writes an asset. + * + * If the application or asset is in debug mode, each leaf asset will be + * dumped as well. + * + * @param string $name An asset name + * @param OutputInterface $output The command output + */ + private function dumpAsset($name, OutputInterface $output) + { + $asset = $this->am->get($name); + $formula = $this->am->getFormula($name); + + // start by dumping the main asset + $this->doDump($asset, $output); + + // dump each leaf if debug + if (isset($formula[2]['debug']) ? $formula[2]['debug'] : $this->am->isDebug()) { + foreach ($asset as $leaf) { + $this->doDump($leaf, $output); + } + } + } + + /** + * Performs the asset dump. + * + * @param AssetInterface $asset An asset + * @param OutputInterface $output The command output + * + * @throws RuntimeException If there is a problem writing the asset + */ + private function doDump(AssetInterface $asset, OutputInterface $output) + { + foreach ($this->getAssetVarCombinations($asset) as $combination) { + $asset->setValues($combination); + + // resolve the target path + $target = rtrim($this->basePath, '/').'/'.$asset->getTargetPath(); + $target = str_replace('_controller/', '', $target); + $target = VarUtils::resolve($target, $asset->getVars(), $asset->getValues()); + + if (!is_dir($dir = dirname($target))) { + $output->writeln(sprintf( + '%s [dir+] %s', + date('H:i:s'), + $dir + )); + + if (false === @mkdir($dir, 0777, true)) { + throw new \RuntimeException('Unable to create directory '.$dir); + } + } + + $output->writeln(sprintf( + '%s [file+] %s', + date('H:i:s'), + $target + )); + + if ($this->verbose) { + if ($asset instanceof AssetCollectionInterface) { + foreach ($asset as $leaf) { + $root = $leaf->getSourceRoot(); + $path = $leaf->getSourcePath(); + $output->writeln(sprintf(' %s/%s', $root ?: '[unknown root]', $path ?: '[unknown path]')); + } + } else { + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + $output->writeln(sprintf(' %s/%s', $root ?: '[unknown root]', $path ?: '[unknown path]')); + } + } + + if (false === @file_put_contents($target, $asset->dump())) { + throw new \RuntimeException('Unable to write file '.$target); + } + } + } + + private function getAssetVarCombinations(AssetInterface $asset) + { + return VarUtils::getCombinations( + $asset->getVars(), + $this->getContainer()->getParameter('assetic.variables') + ); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Config/AsseticResource.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Config/AsseticResource.php new file mode 100644 index 0000000..3e1db07 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Config/AsseticResource.php @@ -0,0 +1,75 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Config; + +use Assetic\Factory\Resource\ResourceInterface as AsseticResourceInterface; +use Symfony\Component\Config\Resource\ResourceInterface as SymfonyResourceInterface; + +/** + * Turns an Assetic resource into a Symfony one. + * + * @author Kris Wallsmith + */ +class AsseticResource implements SymfonyResourceInterface +{ + private $resource; + + public function __construct(AsseticResourceInterface $resource) + { + $this->resource = $resource; + } + + public function __toString() + { + return (string) $this->resource; + } + + public function isFresh($timestamp) + { + return $this->resource->isFresh($timestamp); + } + + /** + * Returns the Assetic resource. + * + * @return AsseticResourceInterface The wrapped Assetic resource + */ + public function getResource() + { + return $this->resource; + } + + public function exists() + { + return true; + } + + public function getId() + { + return md5('assetic'.$this->resource); + } + + public function getModificationTime() + { + return -1; + } + + public function serialize() + { + return serialize($this->resource); + } + + public function unserialize($serialized) + { + $this->resource = unserialize($serialized); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Controller/AsseticController.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Controller/AsseticController.php new file mode 100644 index 0000000..3d86a53 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Controller/AsseticController.php @@ -0,0 +1,120 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Controller; + +use Assetic\Asset\AssetCache; +use Assetic\Asset\AssetInterface; +use Assetic\Cache\CacheInterface; +use Assetic\Factory\LazyAssetManager; +use Assetic\ValueSupplierInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Profiler\Profiler; + +/** + * Serves assets. + * + * @author Kris Wallsmith + */ +class AsseticController +{ + protected $request; + protected $am; + protected $cache; + protected $enableProfiler; + protected $profiler; + protected $valueSupplier; + + public function __construct(Request $request, LazyAssetManager $am, CacheInterface $cache, $enableProfiler = false, Profiler $profiler = null) + { + $this->request = $request; + $this->am = $am; + $this->cache = $cache; + $this->enableProfiler = (boolean) $enableProfiler; + $this->profiler = $profiler; + } + + public function setValueSupplier(ValueSupplierInterface $supplier) + { + $this->valueSupplier = $supplier; + } + + public function render($name, $pos = null) + { + if (!$this->enableProfiler && null !== $this->profiler) { + $this->profiler->disable(); + } + + if (!$this->am->has($name)) { + throw new NotFoundHttpException(sprintf('The "%s" asset could not be found.', $name)); + } + + $asset = $this->am->get($name); + if (null !== $pos && !$asset = $this->findAssetLeaf($asset, $pos)) { + throw new NotFoundHttpException(sprintf('The "%s" asset does not include a leaf at position %d.', $name, $pos)); + } + + $response = $this->createResponse(); + $response->setExpires(new \DateTime()); + + // last-modified + if (null !== $lastModified = $this->am->getLastModified($asset)) { + $date = new \DateTime(); + $date->setTimestamp($lastModified); + $response->setLastModified($date); + } + + // etag + if ($this->am->hasFormula($name)) { + $formula = $this->am->getFormula($name); + $formula['last_modified'] = $lastModified; + $response->setETag(md5(serialize($formula))); + } + + if ($response->isNotModified($this->request)) { + return $response; + } + + $response->setContent($this->cachifyAsset($asset)->dump()); + + return $response; + } + + protected function createResponse() + { + return new Response(); + } + + protected function cachifyAsset(AssetInterface $asset) + { + if ($vars = $asset->getVars()) { + if (null === $this->valueSupplier) { + throw new \RuntimeException(sprintf('You must configure a value supplier if you have assets with variables.')); + } + + $asset->setValues(array_intersect_key($this->valueSupplier->getValues(), array_flip($vars))); + } + + return new AssetCache($asset, $this->cache); + } + + private function findAssetLeaf(\Traversable $asset, $pos) + { + $i = 0; + foreach ($asset as $leaf) { + if ($pos == $i++) { + return $leaf; + } + } + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DefaultValueSupplier.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DefaultValueSupplier.php new file mode 100644 index 0000000..091a55d --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DefaultValueSupplier.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle; + +use Assetic\ValueSupplierInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Default Value Supplier. + * + * @author Johannes M. Schmitt + */ +class DefaultValueSupplier implements ValueSupplierInterface +{ + protected $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function getValues() + { + if (!$this->container->isScopeActive('request')) { + return array(); + } + + $request = $this->container->get('request'); + + return array( + 'locale' => $request->getLocale(), + 'env' => $this->container->getParameter('kernel.environment'), + ); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/AsseticExtension.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/AsseticExtension.php new file mode 100644 index 0000000..eca661c --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/AsseticExtension.php @@ -0,0 +1,155 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +/** + * Semantic asset configuration. + * + * @author Kris Wallsmith + */ +class AsseticExtension extends Extension +{ + /** + * Loads the configuration. + * + * @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('assetic.xml'); + $loader->load('templating_twig.xml'); + $loader->load('templating_php.xml'); + + $processor = new Processor(); + $configuration = $this->getConfiguration($configs, $container); + $config = $processor->processConfiguration($configuration, $configs); + + $container->setParameter('assetic.debug', $config['debug']); + $container->setParameter('assetic.use_controller', $config['use_controller']['enabled']); + $container->setParameter('assetic.enable_profiler', $config['use_controller']['profiler']); + $container->setParameter('assetic.read_from', $config['read_from']); + $container->setParameter('assetic.write_to', $config['write_to']); + $container->setParameter('assetic.variables', $config['variables']); + + $container->setParameter('assetic.java.bin', $config['java']); + $container->setParameter('assetic.node.bin', $config['node']); + $container->setParameter('assetic.node.paths', $config['node_paths']); + $container->setParameter('assetic.ruby.bin', $config['ruby']); + $container->setParameter('assetic.sass.bin', $config['sass']); + + // register formulae + $formulae = array(); + foreach ($config['assets'] as $name => $formula) { + $formulae[$name] = array($formula['inputs'], $formula['filters'], $formula['options']); + } + + if ($formulae) { + $container->getDefinition('assetic.config_resource')->replaceArgument(0, $formulae); + } else { + $container->removeDefinition('assetic.config_loader'); + $container->removeDefinition('assetic.config_resource'); + } + + // register filters + foreach ($config['filters'] as $name => $filter) { + if (isset($filter['resource'])) { + $loader->load($container->getParameterBag()->resolveValue($filter['resource'])); + unset($filter['resource']); + } else { + $loader->load('filters/'.$name.'.xml'); + } + + if (isset($filter['file'])) { + $container->getDefinition('assetic.filter.'.$name)->setFile($filter['file']); + unset($filter['file']); + } + + if (isset($filter['apply_to'])) { + if (!is_array($filter['apply_to'])) { + $filter['apply_to'] = array($filter['apply_to']); + } + + foreach ($filter['apply_to'] as $i => $pattern) { + $worker = new DefinitionDecorator('assetic.worker.ensure_filter'); + $worker->replaceArgument(0, '/'.$pattern.'/'); + $worker->replaceArgument(1, new Reference('assetic.filter.'.$name)); + $worker->addTag('assetic.factory_worker'); + + $container->setDefinition('assetic.filter.'.$name.'.worker'.$i, $worker); + } + + unset($filter['apply_to']); + } + + foreach ($filter as $key => $value) { + $container->setParameter('assetic.filter.'.$name.'.'.$key, $value); + } + } + + // twig functions + $container->setParameter('assetic.twig_extension.functions', $config['twig']['functions']); + + // choose dynamic or static + if ($useController = $container->getParameterBag()->resolveValue($container->getParameterBag()->get('assetic.use_controller'))) { + $loader->load('controller.xml'); + $container->getDefinition('assetic.helper.dynamic')->addTag('templating.helper', array('alias' => 'assetic')); + $container->removeDefinition('assetic.helper.static'); + } else { + $container->getDefinition('assetic.helper.static')->addTag('templating.helper', array('alias' => 'assetic')); + $container->removeDefinition('assetic.helper.dynamic'); + } + + $container->setParameter('assetic.bundles', $config['bundles']); + + $this->addClassesToCompile(array( + 'Symfony\\Bundle\\AsseticBundle\\DefaultValueSupplier', + 'Symfony\\Bundle\\AsseticBundle\\Factory\\AssetFactory', + /* This will introduce hard dependency on Twig + 'Assetic\\Extension\\Twig\\AsseticExtension', + 'Assetic\\Extension\\Twig\\ValueContainer', + 'Symfony\\Bundle\\AsseticBundle\\Twig\\AsseticExtension', + */ + )); + } + + /** + * 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/assetic'; + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + $bundles = $container->getParameter('kernel.bundles'); + + return new Configuration(array_keys($bundles)); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/AssetFactoryPass.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/AssetFactoryPass.php new file mode 100644 index 0000000..6f396b3 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/AssetFactoryPass.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds services tagged as workers to the asset factory. + * + * @author Kris Wallsmith + */ +class AssetFactoryPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('assetic.asset_factory')) { + return; + } + + $factory = $container->getDefinition('assetic.asset_factory'); + foreach ($container->findTaggedServiceIds('assetic.factory_worker') as $id => $attr) { + $factory->addMethodCall('addWorker', array(new Reference($id))); + } + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/AssetManagerPass.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/AssetManagerPass.php new file mode 100644 index 0000000..39f7a50 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/AssetManagerPass.php @@ -0,0 +1,62 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds services tagged as assets to the asset manager. + * + * @author Kris Wallsmith + */ +class AssetManagerPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('assetic.asset_manager')) { + return; + } + + $am = $container->getDefinition('assetic.asset_manager'); + + // add assets + foreach ($container->findTaggedServiceIds('assetic.asset') as $id => $attributes) { + foreach ($attributes as $attr) { + if (isset($attr['alias'])) { + $am->addMethodCall('set', array($attr['alias'], new Reference($id))); + } + } + } + + // add loaders + $loaders = array(); + foreach ($container->findTaggedServiceIds('assetic.formula_loader') as $id => $attributes) { + foreach ($attributes as $attr) { + if (isset($attr['alias'])) { + $loaders[$attr['alias']] = new Reference($id); + } + } + } + $am->replaceArgument(1, $loaders); + + // add resources + foreach ($container->findTaggedServiceIds('assetic.formula_resource') as $id => $attributes) { + foreach ($attributes as $attr) { + if (isset($attr['loader'])) { + $am->addMethodCall('addResource', array(new Reference($id), $attr['loader'])); + } + } + } + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/CheckClosureFilterPass.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/CheckClosureFilterPass.php new file mode 100644 index 0000000..d407909 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/CheckClosureFilterPass.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Tags either the closure JAR or API filter for the filter manager. + * + * @author Kris Wallsmith + */ +class CheckClosureFilterPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if ($container->hasDefinition('assetic.filter.closure.jar') + && $container->hasParameter('assetic.filter.closure.jar') + && $container->getParameterBag()->resolveValue($container->getParameter('assetic.filter.closure.jar'))) { + $container->removeDefinition('assetic.filter.closure.api'); + $container->setAlias('assetic.filter.closure', 'assetic.filter.closure.jar'); + } elseif ($container->hasDefinition('assetic.filter.closure.api')) { + $container->removeDefinition('assetic.filter.closure.jar'); + $container->setAlias('assetic.filter.closure', 'assetic.filter.closure.api'); + } + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/CheckCssEmbedFilterPass.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/CheckCssEmbedFilterPass.php new file mode 100644 index 0000000..f1b41fc --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/CheckCssEmbedFilterPass.php @@ -0,0 +1,31 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Checks that the location of the CssEmbed JAR has been configured. + * + * @author Kris Wallsmith + */ +class CheckCssEmbedFilterPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if ($container->hasDefinition('assetic.filter.cssembed') && + !$container->getParameterBag()->resolveValue($container->getParameter('assetic.filter.cssembed.jar'))) { + throw new \RuntimeException('The "assetic.filters.cssembed" configuration requires a "jar" value.'); + } + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/CheckYuiFilterPass.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/CheckYuiFilterPass.php new file mode 100644 index 0000000..7a14ee0 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/CheckYuiFilterPass.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Checks that the location of the YUI JAR has been configured. + * + * @author Kris Wallsmith + */ +class CheckYuiFilterPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if ($container->hasDefinition('assetic.filter.yui_css') && + !$container->getParameterBag()->resolveValue($container->getParameter('assetic.filter.yui_css.jar'))) { + throw new \RuntimeException('The "assetic.filters.yui_css" configuration requires a "jar" value.'); + } + + if ($container->hasDefinition('assetic.filter.yui_js') && + !$container->getParameterBag()->resolveValue($container->getParameter('assetic.filter.yui_js.jar'))) { + throw new \RuntimeException('The "assetic.filters.yui_js" configuration requires a "jar" value.'); + } + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/FilterManagerPass.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/FilterManagerPass.php new file mode 100644 index 0000000..bf62112 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/FilterManagerPass.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds services tagged as filters to the filter manager. + * + * @author Kris Wallsmith + */ +class FilterManagerPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('assetic.filter_manager')) { + return; + } + + $mapping = array(); + foreach ($container->findTaggedServiceIds('assetic.filter') as $id => $attributes) { + foreach ($attributes as $attr) { + if (isset($attr['alias'])) { + $mapping[$attr['alias']] = $id; + } + } + } + + $container + ->getDefinition('assetic.filter_manager') + ->replaceArgument(1, $mapping); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/RouterResourcePass.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/RouterResourcePass.php new file mode 100644 index 0000000..fd33e97 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/RouterResourcePass.php @@ -0,0 +1,47 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Yaml\Yaml; + +/** + * This pass adds Assetic routes when use_controller is true. + * + * @author Kris Wallsmith + */ +class RouterResourcePass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $useController = $container->getParameterBag()->resolveValue($container->getParameter('assetic.use_controller')); + $routerResource = $container->getParameterBag()->resolveValue($container->getParameter('router.resource')); + + if (!$useController || !$routerResource) { + return; + } + + $file = $container->getParameter('kernel.cache_dir').'/assetic/routing.yml'; + + if (!is_dir($dir = dirname($file))) { + mkdir($dir, 0777, true); + } + + file_put_contents($file, Yaml::dump(array( + '_assetic' => array('resource' => '.', 'type' => 'assetic'), + '_app' => array('resource' => $container->getParameter('router.resource')), + ))); + + $container->setParameter('router.resource', $file); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/SprocketsFilterPass.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/SprocketsFilterPass.php new file mode 100644 index 0000000..f2623c5 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/SprocketsFilterPass.php @@ -0,0 +1,35 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Finishes configuration of the Sprockets filter. + * + * @author Kris Wallsmith + */ +class SprocketsFilterPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('assetic.filter.sprockets')) { + return; + } + + $filter = $container->getDefinition('assetic.filter.sprockets'); + foreach ($container->getParameter('assetic.filter.sprockets.include_dirs') as $dir) { + $filter->addMethodCall('addIncludeDir', array($dir)); + } + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/TemplateResourcesPass.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/TemplateResourcesPass.php new file mode 100644 index 0000000..ab7b46d --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/TemplateResourcesPass.php @@ -0,0 +1,67 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler; + +use Symfony\Bundle\AsseticBundle\DependencyInjection\DirectoryResourceDefinition; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This pass adds directory resources to scan for assetic assets. + * + * @author Kris Wallsmith + * @author Lukas Kahwe Smith + */ +class TemplateResourcesPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('assetic.asset_manager')) { + return; + } + + $engines = $container->getParameter('templating.engines'); + + // bundle and kernel resources + $bundles = $container->getParameter('kernel.bundles'); + $asseticBundles = $container->getParameterBag()->resolveValue($container->getParameter('assetic.bundles')); + foreach ($asseticBundles as $bundleName) { + $rc = new \ReflectionClass($bundles[$bundleName]); + foreach ($engines as $engine) { + $this->setBundleDirectoryResources($container, $engine, dirname($rc->getFileName()), $bundleName); + } + } + + foreach ($engines as $engine) { + $this->setAppDirectoryResources($container, $engine); + } + } + + protected function setBundleDirectoryResources(ContainerBuilder $container, $engine, $bundleDirName, $bundleName) + { + $container->setDefinition( + 'assetic.'.$engine.'_directory_resource.'.$bundleName, + new DirectoryResourceDefinition($bundleName, $engine, array( + $container->getParameter('kernel.root_dir').'/Resources/'.$bundleName.'/views', + $bundleDirName.'/Resources/views', + )) + ); + } + + protected function setAppDirectoryResources(ContainerBuilder $container, $engine) + { + $container->setDefinition( + 'assetic.'.$engine.'_directory_resource.kernel', + new DirectoryResourceDefinition('', $engine, array($container->getParameter('kernel.root_dir').'/Resources/views')) + ); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/TemplatingPass.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/TemplatingPass.php new file mode 100644 index 0000000..8ee4bf8 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Compiler/TemplatingPass.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This pass removes services associated with unused templating engines. + * + * @author Kris Wallsmith + */ +class TemplatingPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('assetic.asset_manager')) { + return; + } + + $engines = $container->getParameterBag()->resolveValue($container->getParameter('templating.engines')); + + if (!in_array('twig', $engines)) { + foreach ($container->findTaggedServiceIds('assetic.templating.twig') as $id => $attr) { + $container->removeDefinition($id); + } + } + + if (!in_array('php', $engines)) { + foreach ($container->findTaggedServiceIds('assetic.templating.php') as $id => $attr) { + $container->removeDefinition($id); + } + } + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Configuration.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..5f01b55 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/Configuration.php @@ -0,0 +1,202 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\AsseticBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Process\ExecutableFinder; + +/** + * 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 + * @author Kris Wallsmith + */ +class Configuration implements ConfigurationInterface +{ + private $bundles; + + /** + * Constructor + * + * @param array $bundles An array of bundle names + */ + public function __construct(array $bundles) + { + $this->bundles = $bundles; + } + + /** + * Generates the configuration tree builder. + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $builder = new TreeBuilder(); + $finder = new ExecutableFinder(); + + $builder->root('assetic') + ->children() + ->booleanNode('debug')->defaultValue('%kernel.debug%')->end() + ->arrayNode('use_controller') + ->addDefaultsIfNotSet() + ->treatTrueLike(array('enabled' => true)) + ->treatFalseLike(array('enabled' => false)) + ->children() + ->booleanNode('enabled')->defaultValue('%kernel.debug%')->end() + ->booleanNode('profiler')->defaultFalse()->end() + ->end() + ->end() + ->scalarNode('read_from')->defaultValue('%kernel.root_dir%/../web')->end() + ->scalarNode('write_to')->defaultValue('%assetic.read_from%')->end() + ->scalarNode('java')->defaultValue(function() use($finder) { return $finder->find('java', '/usr/bin/java'); })->end() + ->scalarNode('node')->defaultValue(function() use($finder) { return $finder->find('node', '/usr/bin/node'); })->end() + ->arrayNode('node_paths') + ->prototype('scalar')->end() + ->end() + ->scalarNode('ruby')->defaultValue(function() use($finder) { return $finder->find('ruby', '/usr/bin/ruby'); })->end() + ->scalarNode('sass')->defaultValue(function() use($finder) { return $finder->find('sass', '/usr/bin/sass'); })->end() + ->end() + + // variables + ->fixXmlConfig('variable') + ->children() + ->arrayNode('variables') + ->useAttributeAsKey('name') + ->prototype('array') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + + // bundles + ->fixXmlConfig('bundle') + ->children() + ->arrayNode('bundles') + ->defaultValue($this->bundles) + ->prototype('scalar') + ->validate() + ->ifNotInArray($this->bundles) + ->thenInvalid('%s is not a valid bundle.') + ->end() + ->end() + ->end() + ->end() + + // assets + ->fixXmlConfig('asset') + ->children() + ->arrayNode('assets') + ->requiresAtLeastOneElement() + ->useAttributeAsKey('name') + ->prototype('array') + ->beforeNormalization() + // a scalar is a simple formula of one input file + ->ifTrue(function($v) { return !is_array($v); }) + ->then(function($v) { return array('inputs' => array($v)); }) + ->end() + ->beforeNormalization() + ->always() + ->then(function($v) + { + // cast scalars as array + foreach (array('input', 'inputs', 'filter', 'filters') as $key) { + if (isset($v[$key]) && !is_array($v[$key])) { + $v[$key] = array($v[$key]); + } + } + + // organize arbitrary options + foreach ($v as $key => $value) { + if (!in_array($key, array('input', 'inputs', 'filter', 'filters', 'option', 'options'))) { + $v['options'][$key] = $value; + unset($v[$key]); + } + } + + return $v; + }) + ->end() + + // the formula + ->fixXmlConfig('input') + ->fixXmlConfig('filter') + ->children() + ->arrayNode('inputs') + ->prototype('scalar')->end() + ->end() + ->arrayNode('filters') + ->prototype('scalar')->end() + ->end() + ->arrayNode('options') + ->useAttributeAsKey('name') + ->prototype('variable')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + + // filters + ->fixXmlConfig('filter') + ->children() + ->arrayNode('filters') + ->requiresAtLeastOneElement() + ->useAttributeAsKey('name') + ->prototype('variable') + ->treatNullLike(array()) + ->validate() + ->ifTrue(function($v) { return !is_array($v); }) + ->thenInvalid('The assetic.filters config %s must be either null or an array.') + ->end() + ->end() + ->validate() + ->always(function($v) use ($finder) { + if (isset($v['compass']) && !isset($v['compass']['bin'])) { + $v['compass']['bin'] = $finder->find('compass', '/usr/bin/compass'); + } + + return $v; + }) + ->end() + ->end() + ->end() + + // twig + ->children() + ->arrayNode('twig') + ->addDefaultsIfNotSet() + ->fixXmlConfig('function') + ->children() + ->arrayNode('functions') + ->defaultValue(array()) + ->useAttributeAsKey('name') + ->prototype('variable') + ->treatNullLike(array()) + ->validate() + ->ifTrue(function($v) { return !is_array($v); }) + ->thenInvalid('The assetic.twig.functions config %s must be either null or an array.') + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + + return $builder; + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/DirectoryResourceDefinition.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/DirectoryResourceDefinition.php new file mode 100644 index 0000000..f572a65 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/DependencyInjection/DirectoryResourceDefinition.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\AsseticBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Encapsulates logic for creating a directory resource. + * + * @author Kris Wallsmith + */ +class DirectoryResourceDefinition extends Definition +{ + /** + * Constructor. + * + * @param string $bundle A bundle name or empty string + * @param string $engine The templating engine + * @param array $dirs An array of directories to merge + */ + public function __construct($bundle, $engine, array $dirs) + { + if (!count($dirs)) { + throw new \InvalidArgumentException('You must provide at least one directory.'); + } + + parent::__construct(); + + $this + ->addTag('assetic.templating.'.$engine) + ->addTag('assetic.formula_resource', array('loader' => $engine)); + ; + + if (1 == count($dirs)) { + // no need to coalesce + self::configureDefinition($this, $bundle, $engine, reset($dirs)); + return; + } + + // gather the wrapped resource definitions + $resources = array(); + foreach ($dirs as $dir) { + $resources[] = $resource = new Definition(); + self::configureDefinition($resource, $bundle, $engine, $dir); + } + + $this + ->setClass('%assetic.coalescing_directory_resource.class%') + ->addArgument($resources) + ->setPublic(false) + ; + } + + static private function configureDefinition(Definition $definition, $bundle, $engine, $dir) + { + $definition + ->setClass('%assetic.directory_resource.class%') + ->addArgument(new Reference('templating.loader')) + ->addArgument($bundle) + ->addArgument($dir) + ->addArgument('/\.[^.]+\.'.$engine.'$/') + ->setPublic(false) + ; + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/EventListener/RequestListener.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/EventListener/RequestListener.php new file mode 100644 index 0000000..ae1d874 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/EventListener/RequestListener.php @@ -0,0 +1,31 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\EventListener; + +use Symfony\Component\HttpKernel\Event\GetResponseEvent; + +/** + * Adds a few formats to each request. + * + * @author Kris Wallsmith + */ +class RequestListener +{ + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + + $request->setFormat('png', 'image/png'); + $request->setFormat('jpg', 'image/jpeg'); + $request->setFormat('gif', 'image/gif'); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Exception/InvalidBundleException.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Exception/InvalidBundleException.php new file mode 100644 index 0000000..960473f --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Exception/InvalidBundleException.php @@ -0,0 +1,26 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Exception; + +class InvalidBundleException extends \LogicException +{ + public function __construct($bundle, $usage, $template, array $enabled, $code = 0, \Exception $previous = null) + { + $message = sprintf('You must add %s to the assetic.bundle config to use %s in %s.', $bundle, $usage, $template); + + if ($enabled) { + $message .= sprintf(' (currently enabled: %s)', implode(', ', $enabled)); + } + + parent::__construct($message, $code, $previous); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/AssetFactory.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/AssetFactory.php new file mode 100644 index 0000000..85f7324 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/AssetFactory.php @@ -0,0 +1,97 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Factory; + +use Assetic\Factory\AssetFactory as BaseAssetFactory; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * Loads asset formulae from the filesystem. + * + * @author Kris Wallsmith + */ +class AssetFactory extends BaseAssetFactory +{ + private $kernel; + private $container; + private $parameterBag; + + /** + * Constructor. + * + * @param KernelInterface $kernel The kernel is used to parse bundle notation + * @param ContainerInterface $container The container is used to load the managers lazily, thus avoiding a circular dependency + * @param ParameterBagInterface $parameterBag The container parameter bag + * @param string $baseDir The base directory for relative inputs + * @param Boolean $debug The current debug mode + */ + public function __construct(KernelInterface $kernel, ContainerInterface $container, ParameterBagInterface $parameterBag, $baseDir, $debug = false) + { + $this->kernel = $kernel; + $this->container = $container; + $this->parameterBag = $parameterBag; + + parent::__construct($baseDir, $debug); + } + + /** + * Adds support for bundle notation file and glob assets and parameter placeholders. + * + * FIXME: This is a naive implementation of globs in that it doesn't + * attempt to support bundle inheritance within the glob pattern itself. + */ + protected function parseInput($input, array $options = array()) + { + $input = $this->parameterBag->resolveValue($input); + + // expand bundle notation + if ('@' == $input[0] && false !== strpos($input, '/')) { + // use the bundle path as this asset's root + $bundle = substr($input, 1); + if (false !== $pos = strpos($bundle, '/')) { + $bundle = substr($bundle, 0, $pos); + } + $options['root'] = array($this->kernel->getBundle($bundle)->getPath()); + + // canonicalize the input + if (false !== $pos = strpos($input, '*')) { + // locateResource() does not support globs so we provide a naive implementation here + list($before, $after) = explode('*', $input, 2); + $input = $this->kernel->locateResource($before).'*'.$after; + } else { + $input = $this->kernel->locateResource($input); + } + } + + return parent::parseInput($input, $options); + } + + protected function createAssetReference($name) + { + if (!$this->getAssetManager()) { + $this->setAssetManager($this->container->get('assetic.asset_manager')); + } + + return parent::createAssetReference($name); + } + + protected function getFilter($name) + { + if (!$this->getFilterManager()) { + $this->setFilterManager($this->container->get('assetic.filter_manager')); + } + + return parent::getFilter($name); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Loader/AsseticHelperFormulaLoader.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Loader/AsseticHelperFormulaLoader.php new file mode 100644 index 0000000..3f69e18 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Loader/AsseticHelperFormulaLoader.php @@ -0,0 +1,87 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Factory\Loader; + +use Assetic\Factory\Loader\BasePhpFormulaLoader; + +/** + * Loads formulae from Symfony2 PHP templates. + * + * @author Kris Wallsmith + */ +class AsseticHelperFormulaLoader extends BasePhpFormulaLoader +{ + protected function registerPrototypes() + { + return array( + '$view[\'assetic\']->javascripts(*)' => array('output' => 'js/*.js'), + '$view[\'assetic\']->stylesheets(*)' => array('output' => 'css/*.css'), + '$view[\'assetic\']->image(*)' => array('output' => 'images/*', 'single' => true), + '$view["assetic"]->javascripts(*)' => array('output' => 'js/*.js'), + '$view["assetic"]->stylesheets(*)' => array('output' => 'css/*.css'), + '$view["assetic"]->image(*)' => array('output' => 'images/*', 'single' => true), + '$view->get(\'assetic\')->javascripts(*)' => array('output' => 'js/*.js'), + '$view->get(\'assetic\')->stylesheets(*)' => array('output' => 'css/*.css'), + '$view->get(\'assetic\')->image(*)' => array('output' => 'images/*', 'single' => true), + '$view->get("assetic")->javascripts(*)' => array('output' => 'js/*.js'), + '$view->get("assetic")->stylesheets(*)' => array('output' => 'css/*.css'), + '$view->get("assetic")->image(*)' => array('output' => 'images/*', 'single' => true), + ); + } + + protected function registerSetupCode() + { + return <<<'EOF' +class Helper +{ + public function assets() + { + global $_call; + $_call = func_get_args(); + } + + public function javascripts() + { + global $_call; + $_call = func_get_args(); + } + + public function stylesheets() + { + global $_call; + $_call = func_get_args(); + } + + public function image() + { + global $_call; + $_call = func_get_args(); + } +} + +class View extends ArrayObject +{ + public function __construct(Helper $helper) + { + parent::__construct(array('assetic' => $helper)); + } + + public function get() + { + return $this['assetic']; + } +} + +$view = new View(new Helper()); +EOF; + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Loader/ConfigurationLoader.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Loader/ConfigurationLoader.php new file mode 100644 index 0000000..28385e1 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Loader/ConfigurationLoader.php @@ -0,0 +1,29 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Factory\Loader; + +use Assetic\Factory\Loader\FormulaLoaderInterface; +use Assetic\Factory\Resource\ResourceInterface; +use Symfony\Bundle\AsseticBundle\Factory\Resource\ConfigurationResource; + +/** + * Loads configured formulae. + * + * @author Kris Wallsmith + */ +class ConfigurationLoader implements FormulaLoaderInterface +{ + public function load(ResourceInterface $resource) + { + return $resource instanceof ConfigurationResource ? $resource->getContent() : array(); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/CoalescingDirectoryResource.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/CoalescingDirectoryResource.php new file mode 100644 index 0000000..1ad87e9 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/CoalescingDirectoryResource.php @@ -0,0 +1,30 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Factory\Resource; + +use Assetic\Factory\Resource\CoalescingDirectoryResource as BaseCoalescingDirectoryResource; +use Assetic\Factory\Resource\ResourceInterface; + +/** + * Coalesces multiple directories together into one merged resource. + * + * @author Kris Wallsmith + */ +class CoalescingDirectoryResource extends BaseCoalescingDirectoryResource +{ + protected function getRelativeName(ResourceInterface $file, ResourceInterface $directory) + { + $name = (string) $file; + + return substr($name, strpos($name, ':')); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/ConfigurationResource.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/ConfigurationResource.php new file mode 100644 index 0000000..e1fac48 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/ConfigurationResource.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Factory\Resource; + +use Assetic\Factory\Resource\ResourceInterface; + +/** + * A configured resource. + * + * @author Kris Wallsmith + */ +class ConfigurationResource implements ResourceInterface +{ + private $formulae; + + public function __construct(array $formulae) + { + $this->formulae = $formulae; + } + + public function isFresh($timestamp) + { + return true; + } + + public function getContent() + { + return $this->formulae; + } + + public function __toString() + { + return 'symfony'; + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/DirectoryResource.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/DirectoryResource.php new file mode 100644 index 0000000..54ce81c --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/DirectoryResource.php @@ -0,0 +1,51 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Factory\Resource; + +use Assetic\Factory\Resource\DirectoryResource as BaseDirectoryResource; +use Symfony\Component\Templating\Loader\LoaderInterface; + +/** + * A directory resource that creates Symfony2 templating resources. + * + * @author Kris Wallsmith + */ +class DirectoryResource extends BaseDirectoryResource +{ + protected $loader; + protected $bundle; + protected $path; + + /** + * Constructor. + * + * @param LoaderInterface $loader The templating loader + * @param string $bundle The current bundle name + * @param string $path The directory path + * @param string $pattern A regex pattern for file basenames + */ + public function __construct(LoaderInterface $loader, $bundle, $path, $pattern = null) + { + $this->loader = $loader; + $this->bundle = $bundle; + $this->path = rtrim($path, '/').'/'; + + parent::__construct($path, $pattern); + } + + public function getIterator() + { + return is_dir($this->path) + ? new DirectoryResourceIterator($this->loader, $this->bundle, $this->path, $this->getInnerIterator()) + : new \EmptyIterator(); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/DirectoryResourceIterator.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/DirectoryResourceIterator.php new file mode 100644 index 0000000..e31cc7e --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/DirectoryResourceIterator.php @@ -0,0 +1,45 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Factory\Resource; + +use Symfony\Component\Templating\Loader\LoaderInterface; + +class DirectoryResourceIterator extends \RecursiveIteratorIterator +{ + protected $loader; + protected $bundle; + protected $path; + + /** + * Constructor. + * + * @param LoaderInterface $loader The templating loader + * @param string $bundle The current bundle name + * @param string $path The directory + * @param RecursiveIterator $iterator The inner iterator + */ + public function __construct(LoaderInterface $loader, $bundle, $path, \RecursiveIterator $iterator) + { + $this->loader = $loader; + $this->bundle = $bundle; + $this->path = $path; + + parent::__construct($iterator); + } + + public function current() + { + $file = parent::current(); + + return new FileResource($this->loader, $this->bundle, $this->path, $file->getPathname()); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/FileResource.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/FileResource.php new file mode 100644 index 0000000..72585d5 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Resource/FileResource.php @@ -0,0 +1,88 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Factory\Resource; + +use Assetic\Factory\Resource\ResourceInterface; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Component\Templating\Loader\LoaderInterface; + +/** + * A file resource. + * + * @author Kris Wallsmith + */ +class FileResource implements ResourceInterface +{ + protected $loader; + protected $bundle; + protected $baseDir; + protected $path; + protected $template; + + /** + * Constructor. + * + * @param LoaderInterface $loader The templating loader + * @param string $bundle The current bundle name + * @param string $baseDir The directory + * @param string $path The file path + */ + public function __construct(LoaderInterface $loader, $bundle, $baseDir, $path) + { + $this->loader = $loader; + $this->bundle = $bundle; + $this->baseDir = $baseDir; + $this->path = $path; + } + + public function isFresh($timestamp) + { + return $this->loader->isFresh($this->getTemplate(), $timestamp); + } + + public function getContent() + { + $templateReference = $this->getTemplate(); + $fileResource = $this->loader->load($templateReference); + + if (!$fileResource) { + throw new \InvalidArgumentException(sprintf('Unable to find template "%s".', $templateReference)); + } + + return $fileResource->getContent(); + } + + public function __toString() + { + return (string) $this->getTemplate(); + } + + protected function getTemplate() + { + if (null === $this->template) { + $this->template = self::createTemplateReference($this->bundle, substr($this->path, strlen($this->baseDir))); + } + + return $this->template; + } + + static private function createTemplateReference($bundle, $file) + { + $parts = explode('/', strtr($file, '\\', '/')); + $elements = explode('.', array_pop($parts)); + $engine = array_pop($elements); + $format = array_pop($elements); + $name = implode('.', $elements); + + return new TemplateReference($bundle, implode('/', $parts), $name, $format, $engine); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Worker/UseControllerWorker.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Worker/UseControllerWorker.php new file mode 100644 index 0000000..b894154 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Factory/Worker/UseControllerWorker.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. + */ + +namespace Symfony\Bundle\AsseticBundle\Factory\Worker; + +use Assetic\Asset\AssetInterface; +use Assetic\Factory\Worker\WorkerInterface; + +/** + * Prepends a fake front controller so the asset knows where it is-ish. + * + * @author Kris Wallsmith + */ +class UseControllerWorker implements WorkerInterface +{ + public function process(AssetInterface $asset) + { + $targetUrl = $asset->getTargetPath(); + if ($targetUrl && '/' != $targetUrl[0] && 0 !== strpos($targetUrl, '_controller/')) { + $asset->setTargetPath('_controller/'.$targetUrl); + } + + return $asset; + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/FilterManager.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/FilterManager.php new file mode 100644 index 0000000..c547a86 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/FilterManager.php @@ -0,0 +1,55 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle; + +use Assetic\FilterManager as BaseFilterManager; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazy filter manager. + * + * @author Kris Wallsmith + */ +class FilterManager extends BaseFilterManager +{ + protected $container; + protected $mappings; + + /** + * Constructor. + * + * @param ContainerInterface $container The service container + * @param array $mappings A hash of filter names to service ids + */ + public function __construct(ContainerInterface $container, array $mappings) + { + $this->container = $container; + $this->mappings = $mappings; + } + + public function get($name) + { + return isset($this->mappings[$name]) + ? $this->container->get($this->mappings[$name]) + : parent::get($name); + } + + public function has($name) + { + return isset($this->mappings[$name]) || parent::has($name); + } + + public function getNames() + { + return array_unique(array_merge(array_keys($this->mappings), parent::getNames())); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/LICENSE b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/assetic.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/assetic.xml new file mode 100644 index 0000000..7d05979 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/assetic.xml @@ -0,0 +1,75 @@ + + + + + + Symfony\Bundle\AsseticBundle\Factory\AssetFactory + Assetic\Factory\LazyAssetManager + Symfony\Bundle\AsseticBundle\CacheWarmer\AssetManagerCacheWarmer + Assetic\Factory\Loader\CachedFormulaLoader + Assetic\Cache\ConfigCache + Symfony\Bundle\AsseticBundle\Factory\Loader\ConfigurationLoader + Symfony\Bundle\AsseticBundle\Factory\Resource\ConfigurationResource + Symfony\Bundle\AsseticBundle\Factory\Resource\CoalescingDirectoryResource + Symfony\Bundle\AsseticBundle\Factory\Resource\DirectoryResource + Symfony\Bundle\AsseticBundle\FilterManager + Assetic\Factory\Worker\EnsureFilterWorker + Symfony\Bundle\AsseticBundle\DefaultValueSupplier + + + %kernel.cache_dir%/assetic + + + + + + + + + + + + + + + + + + + %assetic.read_from% + %assetic.debug% + + + + + + + + + + + + %assetic.cache_dir%/config + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/controller.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/controller.xml new file mode 100644 index 0000000..19b348f --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/controller.xml @@ -0,0 +1,41 @@ + + + + + + Symfony\Bundle\AsseticBundle\Controller\AsseticController + Symfony\Bundle\AsseticBundle\Routing\AsseticLoader + Assetic\Cache\FilesystemCache + Symfony\Bundle\AsseticBundle\Factory\Worker\UseControllerWorker + Symfony\Bundle\AsseticBundle\EventListener\RequestListener + + + + + + + + + + + + %assetic.enable_profiler% + + + + + + + + %assetic.cache_dir%/assets + + + + + + + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/closure.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/closure.xml new file mode 100644 index 0000000..7112d0f --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/closure.xml @@ -0,0 +1,30 @@ + + + + + + Assetic\Filter\GoogleClosure\CompilerApiFilter + Assetic\Filter\GoogleClosure\CompilerJarFilter + %assetic.java.bin% + null + null + + + + + + %assetic.filter.closure.jar% + %assetic.filter.closure.java% + %assetic.filter.closure.timeout% + %assetic.filter.closure.compilation_level% + + + + + %assetic.filter.closure.timeout% + %assetic.filter.closure.compilation_level% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/coffee.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/coffee.xml new file mode 100644 index 0000000..cab8901 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/coffee.xml @@ -0,0 +1,26 @@ + + + + + + Assetic\Filter\CoffeeScriptFilter + /usr/bin/coffee + %assetic.node.bin% + null + %assetic.node.paths% + null + + + + + + %assetic.filter.coffee.bin% + %assetic.filter.coffee.node% + %assetic.filter.coffee.timeout% + %assetic.filter.coffee.node_paths% + %assetic.filter.coffee.bare% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/compass.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/compass.xml new file mode 100644 index 0000000..53a97c4 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/compass.xml @@ -0,0 +1,51 @@ + + + + + + Assetic\Filter\CompassFilter + /usr/bin/compass + null + false + false + null + null + null + null + null + null + null + null + null + null + + + true + + + + + + %assetic.filter.compass.bin% + %assetic.ruby.bin% + %assetic.filter.compass.timeout% + %assetic.filter.compass.debug% + %assetic.filter.compass.no_line_comments% + %assetic.filter.compass.style% + %assetic.filter.compass.images_dir% + %assetic.filter.compass.fonts_dir% + %assetic.filter.compass.javascripts_dir% + %assetic.filter.compass.http_path% + %assetic.filter.compass.http_images_path% + %assetic.filter.compass.http_fonts_path% + %assetic.filter.compass.http_generated_images_path% + %assetic.filter.compass.generated_images_path% + %assetic.filter.compass.http_javascripts_path% + %assetic.filter.compass.plugins% + %assetic.filter.compass.load_paths% + %assetic.filter.compass.home_env% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssembed.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssembed.xml new file mode 100644 index 0000000..93e1d6e --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssembed.xml @@ -0,0 +1,36 @@ + + + + + + Assetic\Filter\CssEmbedFilter + %assetic.java.bin% + + null + %kernel.charset% + false + null + null + false + null + null + + + + + + %assetic.filter.cssembed.jar% + %assetic.filter.cssembed.java% + %assetic.filter.cssembed.timeout% + %assetic.filter.cssembed.charset% + %assetic.filter.cssembed.mhtml% + %assetic.filter.cssembed.mhtml_root% + %assetic.filter.cssembed.root% + %assetic.filter.cssembed.skip_missing% + %assetic.filter.cssembed.max_uri_length% + %assetic.filter.cssembed.max_image_size% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssimport.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssimport.xml new file mode 100644 index 0000000..08440cd --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssimport.xml @@ -0,0 +1,16 @@ + + + + + + Assetic\Filter\CssImportFilter + + + + + + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssmin.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssmin.xml new file mode 100644 index 0000000..0fd1057 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssmin.xml @@ -0,0 +1,20 @@ + + + + + + Assetic\Filter\CssMinFilter + + + + + + + + %assetic.filter.cssmin.filters% + %assetic.filter.cssmin.plugins% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssrewrite.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssrewrite.xml new file mode 100644 index 0000000..383d8cd --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/cssrewrite.xml @@ -0,0 +1,16 @@ + + + + + + Assetic\Filter\CssRewriteFilter + + + + + + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/dart.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/dart.xml new file mode 100644 index 0000000..b73d7dd --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/dart.xml @@ -0,0 +1,20 @@ + + + + + + Assetic\Filter\DartFilter + /usr/bin/dart2js + null + + + + + + %assetic.filter.dart.bin% + %assetic.filter.dart.timeout% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/gss.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/gss.xml new file mode 100644 index 0000000..968f932 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/gss.xml @@ -0,0 +1,22 @@ + + + + + + Assetic\Filter\GssFilter + %assetic.java.bin% + + null + + + + + + %assetic.filter.gss.jar% + %assetic.filter.gss.java% + %assetic.filter.gss.timeout% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/handlebars.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/handlebars.xml new file mode 100644 index 0000000..78d6fbc --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/handlebars.xml @@ -0,0 +1,28 @@ + + + + + + Assetic\Filter\HandlebarsFilter + /usr/bin/handlebars + %assetic.node.bin% + null + %assetic.node.paths% + false + false + + + + + + %assetic.filter.handlebars.bin% + %assetic.filter.handlebars.node% + %assetic.filter.handlebars.timeout% + %assetic.filter.handlebars.node_paths% + %assetic.filter.handlebars.minimize% + %assetic.filter.handlebars.simple% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jpegoptim.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jpegoptim.xml new file mode 100644 index 0000000..f578017 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jpegoptim.xml @@ -0,0 +1,24 @@ + + + + + + Assetic\Filter\JpegoptimFilter + /usr/bin/jpegoptim + null + false + null + + + + + + %assetic.filter.jpegoptim.bin% + %assetic.filter.jpegoptim.timeout% + %assetic.filter.jpegoptim.strip_all% + %assetic.filter.jpegoptim.max% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jpegtran.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jpegtran.xml new file mode 100644 index 0000000..37a14c5 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jpegtran.xml @@ -0,0 +1,28 @@ + + + + + + Assetic\Filter\JpegtranFilter + /usr/bin/jpegtran + null + null + false + false + null + + + + + + %assetic.filter.jpegtran.bin% + %assetic.filter.jpegtran.timeout% + %assetic.filter.jpegtran.copy% + %assetic.filter.jpegtran.optimize% + %assetic.filter.jpegtran.progressive% + %assetic.filter.jpegtran.restart% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jsmin.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jsmin.xml new file mode 100644 index 0000000..9fc3242 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jsmin.xml @@ -0,0 +1,16 @@ + + + + + + Assetic\Filter\JSMinFilter + + + + + + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jsminplus.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jsminplus.xml new file mode 100644 index 0000000..70c2b9c --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/jsminplus.xml @@ -0,0 +1,16 @@ + + + + + + Assetic\Filter\JSMinPlusFilter + + + + + + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/less.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/less.xml new file mode 100644 index 0000000..48212b3 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/less.xml @@ -0,0 +1,24 @@ + + + + + + Assetic\Filter\LessFilter + %assetic.node.bin% + %assetic.node.paths% + null + null + + + + + + %assetic.filter.less.node% + %assetic.filter.less.node_paths% + %assetic.filter.less.timeout% + %assetic.filter.less.compress% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/lessphp.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/lessphp.xml new file mode 100644 index 0000000..7b615e2 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/lessphp.xml @@ -0,0 +1,28 @@ + + + + + + Assetic\Filter\LessphpFilter + + + + null + null + + + + + + %assetic.filter.lessphp.presets% + %assetic.filter.lessphp.paths% + %assetic.filter.lessphp.formatter% + %assetic.filter.lessphp.preserve_comments% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/optipng.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/optipng.xml new file mode 100644 index 0000000..e5f9f3b --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/optipng.xml @@ -0,0 +1,22 @@ + + + + + + Assetic\Filter\OptiPngFilter + /usr/bin/optipng + null + null + + + + + + %assetic.filter.optipng.bin% + %assetic.filter.optipng.timeout% + %assetic.filter.optipng.level% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/packager.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/packager.xml new file mode 100644 index 0000000..6446116 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/packager.xml @@ -0,0 +1,18 @@ + + + + + + Assetic\Filter\PackagerFilter + + + + + + + %assetic.filter.packager.packages% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/phpcssembed.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/phpcssembed.xml new file mode 100644 index 0000000..b208c01 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/phpcssembed.xml @@ -0,0 +1,16 @@ + + + + + + Assetic\Filter\PhpCssEmbedFilter + + + + + + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/pngout.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/pngout.xml new file mode 100644 index 0000000..6365315 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/pngout.xml @@ -0,0 +1,28 @@ + + + + + + Assetic\Filter\PngoutFilter + /usr/bin/pngout + null + null + null + null + null + + + + + + %assetic.filter.pngout.bin% + %assetic.filter.pngout.timeout% + %assetic.filter.pngout.color% + %assetic.filter.pngout.filter% + %assetic.filter.pngout.strategy% + %assetic.filter.pngout.block_split_threshold% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/sass.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/sass.xml new file mode 100644 index 0000000..e0f0351 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/sass.xml @@ -0,0 +1,25 @@ + + + + + + Assetic\Filter\Sass\SassFilter + %assetic.sass.bin% + null + null + null + + + + + + %assetic.filter.sass.bin% + %assetic.ruby.bin% + %assetic.filter.sass.timeout% + %assetic.filter.sass.style% + %assetic.filter.sass.compass% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/scss.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/scss.xml new file mode 100644 index 0000000..798b80b --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/scss.xml @@ -0,0 +1,25 @@ + + + + + + Assetic\Filter\Sass\ScssFilter + %assetic.sass.bin% + null + null + null + + + + + + %assetic.filter.scss.sass% + %assetic.ruby.bin% + %assetic.filter.scss.timeout% + %assetic.filter.scss.style% + %assetic.filter.scss.compass% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/scssphp.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/scssphp.xml new file mode 100644 index 0000000..1c74a4a --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/scssphp.xml @@ -0,0 +1,24 @@ + + + + + + Assetic\Filter\ScssphpFilter + + false + + + + + + %assetic.filter.scssphp.compass% + + + %assetic.filter.scssphp.import_paths% + + + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/sprockets.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/sprockets.xml new file mode 100644 index 0000000..6ec295d --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/sprockets.xml @@ -0,0 +1,25 @@ + + + + + + Assetic\Filter\SprocketsFilter + null + %assetic.ruby.bin% + null + %assetic.write_to% + + + + + + + %assetic.filter.sprockets.lib% + %assetic.filter.sprockets.ruby% + %assetic.filter.sprockets.timeout% + %assetic.filter.sprockets.asset_root% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/stylus.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/stylus.xml new file mode 100644 index 0000000..24ac603 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/stylus.xml @@ -0,0 +1,24 @@ + + + + + + Assetic\Filter\StylusFilter + %assetic.node.bin% + %assetic.node.paths% + null + null + + + + + + %assetic.filter.stylus.node% + %assetic.filter.stylus.node_paths% + %assetic.filter.stylus.timeout% + %assetic.filter.stylus.compress% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/typescript.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/typescript.xml new file mode 100644 index 0000000..5b08056 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/typescript.xml @@ -0,0 +1,24 @@ + + + + + + Assetic\Filter\TypeScriptFilter + /usr/bin/tsc + %assetic.node.bin% + %assetic.node.paths% + null + + + + + + %assetic.filter.typescript.bin% + %assetic.filter.typescript.node% + %assetic.filter.typescript.timeout% + %assetic.filter.typescript.node_paths% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/uglifycss.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/uglifycss.xml new file mode 100644 index 0000000..8d97bb4 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/uglifycss.xml @@ -0,0 +1,30 @@ + + + + + + Assetic\Filter\UglifyCssFilter + /usr/bin/uglifycss + %assetic.node.bin% + null + %assetic.node.paths% + false + false + false + + + + + + %assetic.filter.uglifycss.bin% + %assetic.filter.uglifycss.node% + %assetic.filter.uglifycss.timeout% + %assetic.filter.uglifycss.node_paths% + %assetic.filter.uglifycss.expand_vars% + %assetic.filter.uglifycss.ugly_comments% + %assetic.filter.uglifycss.cute_comments% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/uglifyjs.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/uglifyjs.xml new file mode 100644 index 0000000..12dfe29 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/uglifyjs.xml @@ -0,0 +1,32 @@ + + + + + + Assetic\Filter\UglifyJsFilter + /usr/bin/uglifyjs + %assetic.node.bin% + null + %assetic.node.paths% + false + false + false + false + + + + + + %assetic.filter.uglifyjs.bin% + %assetic.filter.uglifyjs.node% + %assetic.filter.uglifyjs.timeout% + %assetic.filter.uglifyjs.node_paths% + %assetic.filter.uglifyjs.beautify% + %assetic.filter.uglifyjs.no_copyright% + %assetic.filter.uglifyjs.unsafe% + %assetic.filter.uglifyjs.mangle% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/uglifyjs2.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/uglifyjs2.xml new file mode 100644 index 0000000..b74796c --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/uglifyjs2.xml @@ -0,0 +1,30 @@ + + + + + + Assetic\Filter\UglifyJs2Filter + /usr/bin/uglifyjs + %assetic.node.bin% + null + %assetic.node.paths% + false + false + false + + + + + + %assetic.filter.uglifyjs2.bin% + %assetic.filter.uglifyjs2.node% + %assetic.filter.uglifyjs2.timeout% + %assetic.filter.uglifyjs2.node_paths% + %assetic.filter.uglifyjs2.compress% + %assetic.filter.uglifyjs2.beautify% + %assetic.filter.uglifyjs2.mangle% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/yui_css.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/yui_css.xml new file mode 100644 index 0000000..5e3f1e6 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/yui_css.xml @@ -0,0 +1,26 @@ + + + + + + Assetic\Filter\Yui\CssCompressorFilter + %assetic.java.bin% + + %kernel.charset% + null + null + + + + + + %assetic.filter.yui_css.jar% + %assetic.filter.yui_css.java% + %assetic.filter.yui_css.charset% + %assetic.filter.yui_css.timeout% + %assetic.filter.yui_css.stacksize% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/yui_js.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/yui_js.xml new file mode 100644 index 0000000..869f10d --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/filters/yui_js.xml @@ -0,0 +1,32 @@ + + + + + + Assetic\Filter\Yui\JsCompressorFilter + %assetic.java.bin% + + %kernel.charset% + null + null + null + null + null + + + + + + %assetic.filter.yui_js.jar% + %assetic.filter.yui_js.java% + %assetic.filter.yui_js.charset% + %assetic.filter.yui_js.timeout% + %assetic.filter.yui_js.stacksize% + %assetic.filter.yui_js.nomunge% + %assetic.filter.yui_js.preserve_semi% + %assetic.filter.yui_js.disable_optimizations% + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/schema/assetic-1.0.xsd b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/schema/assetic-1.0.xsd new file mode 100644 index 0000000..82e35a3 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/schema/assetic-1.0.xsd @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/templating_php.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/templating_php.xml new file mode 100644 index 0000000..b6843d9 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/templating_php.xml @@ -0,0 +1,39 @@ + + + + + + Symfony\Bundle\AsseticBundle\Templating\DynamicAsseticHelper + Symfony\Bundle\AsseticBundle\Templating\StaticAsseticHelper + Symfony\Bundle\AsseticBundle\Factory\Loader\AsseticHelperFormulaLoader + + + + + + + + + + + + + + + + + + + + + %kernel.debug% + + + + + + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/templating_twig.xml b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/templating_twig.xml new file mode 100644 index 0000000..8ab025b --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Resources/config/templating_twig.xml @@ -0,0 +1,35 @@ + + + + + + Symfony\Bundle\AsseticBundle\Twig\AsseticExtension + Assetic\Extension\Twig\TwigFormulaLoader + + + + + + + + + %assetic.use_controller% + %assetic.twig_extension.functions% + %assetic.bundles% + + + + + + + + %kernel.debug% + + + + + + + diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Routing/AsseticLoader.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Routing/AsseticLoader.php new file mode 100644 index 0000000..b2e8385 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Routing/AsseticLoader.php @@ -0,0 +1,122 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Routing; + +use Assetic\Asset\AssetInterface; +use Assetic\Factory\LazyAssetManager; +use Symfony\Bundle\AsseticBundle\Config\AsseticResource; +use Symfony\Component\Config\Loader\Loader; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * Loads routes for all assets. + * + * Assets should only be served through the routing system for ease-of-use + * during development. + * + * For example, add the following to your application's routing_dev.yml: + * + * _assetic: + * resource: . + * type: assetic + * + * In a production environment you should use the `assetic:dump` command to + * create static asset files. + * + * @author Kris Wallsmith + */ +class AsseticLoader extends Loader +{ + protected $am; + + public function __construct(LazyAssetManager $am) + { + $this->am = $am; + } + + public function load($routingResource, $type = null) + { + $routes = new RouteCollection(); + + // resources + foreach ($this->am->getResources() as $resources) { + if (!$resources instanceof \Traversable) { + $resources = array($resources); + } + foreach ($resources as $resource) { + $routes->addResource(new AsseticResource($resource)); + } + } + + // routes + foreach ($this->am->getNames() as $name) { + $asset = $this->am->get($name); + $formula = $this->am->getFormula($name); + + $this->loadRouteForAsset($routes, $asset, $name); + + $debug = isset($formula[2]['debug']) ? $formula[2]['debug'] : $this->am->isDebug(); + $combine = isset($formula[2]['combine']) ? $formula[2]['combine'] : !$debug; + + // add a route for each "leaf" in debug mode + if (!$combine) { + $i = 0; + foreach ($asset as $leaf) { + $this->loadRouteForAsset($routes, $leaf, $name, $i++); + } + } + } + + return $routes; + } + + /** + * Loads a route to serve an supplied asset. + * + * The fake front controller that {@link UseControllerWorker} adds to the + * target URL will be removed before set as a route pattern. + * + * @param RouteCollection $routes The route collection + * @param AssetInterface $asset The asset + * @param string $name The name to use + * @param integer $pos The leaf index + */ + private function loadRouteForAsset(RouteCollection $routes, AssetInterface $asset, $name, $pos = null) + { + $defaults = array( + '_controller' => 'assetic.controller:render', + 'name' => $name, + 'pos' => $pos, + ); + + // remove the fake front controller + $pattern = str_replace('_controller/', '', $asset->getTargetPath()); + + if ($format = pathinfo($pattern, PATHINFO_EXTENSION)) { + $defaults['_format'] = $format; + } + + $route = '_assetic_'.$name; + if (null !== $pos) { + $route .= '_'.$pos; + } + + $routes->add($route, new Route($pattern, $defaults)); + } + + public function supports($resource, $type = null) + { + return 'assetic' == $type; + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Templating/AsseticHelper.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Templating/AsseticHelper.php new file mode 100644 index 0000000..4944209 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Templating/AsseticHelper.php @@ -0,0 +1,157 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Templating; + +use Assetic\Asset\AssetInterface; +use Assetic\Factory\AssetFactory; +use Assetic\Util\TraversableString; +use Symfony\Component\Templating\Helper\Helper; + +/** + * The "assetic" templating helper. + * + * @author Kris Wallsmith + */ +abstract class AsseticHelper extends Helper +{ + protected $factory; + + /** + * Constructor. + * + * @param AssetFactory $factory The asset factory + */ + public function __construct(AssetFactory $factory) + { + $this->factory = $factory; + } + + /** + * Returns an array of javascript urls. + */ + public function javascripts($inputs = array(), $filters = array(), array $options = array()) + { + if (!isset($options['output'])) { + $options['output'] = 'js/*.js'; + } + + return $this->getAssetUrls($inputs, $filters, $options); + } + + /** + * Returns an array of stylesheet urls. + */ + public function stylesheets($inputs = array(), $filters = array(), array $options = array()) + { + if (!isset($options['output'])) { + $options['output'] = 'css/*.css'; + } + + return $this->getAssetUrls($inputs, $filters, $options); + } + + /** + * Returns an array of one image url. + */ + public function image($inputs = array(), $filters = array(), array $options = array()) + { + if (!isset($options['output'])) { + $options['output'] = 'images/*'; + } + + $options['single'] = true; + + return $this->getAssetUrls($inputs, $filters, $options); + } + + /** + * Gets the URLs for the configured asset. + * + * Usage looks something like this: + * + * assets('@jquery, js/src/core/*', '?yui_js') as $url): ?> + * + * + * + * When in debug mode, the helper returns an array of one or more URLs. + * When not in debug mode it returns an array of one URL. + * + * @param array|string $inputs An array or comma-separated list of input strings + * @param array|string $filters An array or comma-separated list of filter names + * @param array $options An array of options + * + * @return array An array of URLs for the asset + */ + private function getAssetUrls($inputs = array(), $filters = array(), array $options = array()) + { + $explode = function($value) + { + return array_map('trim', explode(',', $value)); + }; + + if (!is_array($inputs)) { + $inputs = $explode($inputs); + } + + if (!is_array($filters)) { + $filters = $explode($filters); + } + + if (!isset($options['debug'])) { + $options['debug'] = $this->factory->isDebug(); + } + + if (!isset($options['combine'])) { + $options['combine'] = !$options['debug']; + } + + if (isset($options['single']) && $options['single'] && 1 < count($inputs)) { + $inputs = array_slice($inputs, -1); + } + + if (!isset($options['name'])) { + $options['name'] = $this->factory->generateAssetName($inputs, $filters, $options); + } + + $asset = $this->factory->createAsset($inputs, $filters, $options); + + $one = $this->getAssetUrl($asset, $options); + $many = array(); + if ($options['combine']) { + $many[] = $one; + } else { + $i = 0; + foreach ($asset as $leaf) { + $many[] = $this->getAssetUrl($leaf, array_replace($options, array( + 'name' => $options['name'].'_'.$i++, + ))); + } + } + + return new TraversableString($one, $many); + } + + /** + * Returns an URL for the supplied asset. + * + * @param AssetInterface $asset An asset + * @param array $options An array of options + * + * @return string An echo-ready URL + */ + abstract protected function getAssetUrl(AssetInterface $asset, $options = array()); + + public function getName() + { + return 'assetic'; + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Templating/DynamicAsseticHelper.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Templating/DynamicAsseticHelper.php new file mode 100644 index 0000000..c4bdcd7 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Templating/DynamicAsseticHelper.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Templating; + +use Assetic\Asset\AssetInterface; +use Assetic\Factory\AssetFactory; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\RouterHelper; + +/** + * The dynamic "assetic" templating helper. + * + * @author Kris Wallsmith + */ +class DynamicAsseticHelper extends AsseticHelper +{ + private $routerHelper; + + /** + * Constructor. + * + * @param RouterHelper $routerHelper The router helper + * @param AssetFactory $factory The asset factory + */ + public function __construct(RouterHelper $routerHelper, AssetFactory $factory) + { + $this->routerHelper = $routerHelper; + + parent::__construct($factory); + } + + protected function getAssetUrl(AssetInterface $asset, $options = array()) + { + return $this->routerHelper->generate('_assetic_'.$options['name']); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Templating/StaticAsseticHelper.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Templating/StaticAsseticHelper.php new file mode 100644 index 0000000..39ba793 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Templating/StaticAsseticHelper.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Templating; + +use Assetic\Asset\AssetInterface; +use Assetic\Factory\AssetFactory; +use Symfony\Component\Templating\Helper\CoreAssetsHelper; + +/** + * The static "assetic" templating helper. + * + * @author Kris Wallsmith + */ +class StaticAsseticHelper extends AsseticHelper +{ + private $assetsHelper; + + /** + * Constructor. + * + * @param CoreAssetsHelper $assetsHelper The assets helper + * @param AssetFactory $factory The asset factory + */ + public function __construct(CoreAssetsHelper $assetsHelper, AssetFactory $factory) + { + $this->assetsHelper = $assetsHelper; + + parent::__construct($factory); + } + + protected function getAssetUrl(AssetInterface $asset, $options = array()) + { + return $this->assetsHelper->getUrl($asset->getTargetPath(), isset($options['package']) ? $options['package'] : null); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticExtension.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticExtension.php new file mode 100644 index 0000000..788bbc3 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticExtension.php @@ -0,0 +1,71 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Twig; + +use Assetic\Extension\Twig\AsseticExtension as BaseAsseticExtension; +use Assetic\Factory\AssetFactory; +use Assetic\ValueSupplierInterface; +use Symfony\Component\Templating\TemplateNameParserInterface; + +/** + * Assetic extension. + * + * @author Kris Wallsmith + */ +class AsseticExtension extends BaseAsseticExtension +{ + private $useController; + private $templateNameParser; + private $enabledBundles; + + public function __construct(AssetFactory $factory, TemplateNameParserInterface $templateNameParser, $useController = false, $functions = array(), $enabledBundles = array(), ValueSupplierInterface $valueSupplier = null) + { + parent::__construct($factory, $functions, $valueSupplier); + + $this->useController = $useController; + $this->templateNameParser = $templateNameParser; + $this->enabledBundles = $enabledBundles; + } + + public function getTokenParsers() + { + return array( + $this->createTokenParser('javascripts', 'js/*.js'), + $this->createTokenParser('stylesheets', 'css/*.css'), + $this->createTokenParser('image', 'images/*', true), + ); + } + + public function getNodeVisitors() + { + return array( + new AsseticNodeVisitor($this->templateNameParser, $this->enabledBundles), + ); + } + + public function getGlobals() + { + $globals = parent::getGlobals(); + $globals['assetic']['use_controller'] = $this->useController; + + return $globals; + } + + private function createTokenParser($tag, $output, $single = false) + { + $tokenParser = new AsseticTokenParser($this->factory, $tag, $output, $single, array('package')); + $tokenParser->setTemplateNameParser($this->templateNameParser); + $tokenParser->setEnabledBundles($this->enabledBundles); + + return $tokenParser; + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticNode.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticNode.php new file mode 100644 index 0000000..1a6a0cc --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticNode.php @@ -0,0 +1,83 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Twig; + +use Assetic\Asset\AssetInterface; +use Assetic\Extension\Twig\AsseticNode as BaseAsseticNode; + +/** + * Assetic node. + * + * @author Kris Wallsmith + */ +class AsseticNode extends BaseAsseticNode +{ + protected function compileAssetUrl(\Twig_Compiler $compiler, AssetInterface $asset, $name) + { + $compiler + ->raw('isset($context[\'assetic\'][\'use_controller\']) && $context[\'assetic\'][\'use_controller\'] ? ') + ->subcompile($this->getPathFunction($name)) + ->raw(' : ') + ->subcompile($this->getAssetFunction(new TargetPathNode($this, $asset, $name))) + ; + } + + private function getPathFunction($name) + { + return new \Twig_Node_Expression_Function( + version_compare(\Twig_Environment::VERSION, '1.2.0-DEV', '<') + ? new \Twig_Node_Expression_Name('path', $this->getLine()) : 'path', + new \Twig_Node(array(new \Twig_Node_Expression_Constant('_assetic_'.$name, $this->getLine()))), + $this->getLine() + ); + } + + private function getAssetFunction($path) + { + $arguments = array($path); + + if ($this->hasAttribute('package')) { + $arguments[] = new \Twig_Node_Expression_Constant($this->getAttribute('package'), $this->getLine()); + } + + return new \Twig_Node_Expression_Function( + version_compare(\Twig_Environment::VERSION, '1.2.0-DEV', '<') + ? new \Twig_Node_Expression_Name('asset', $this->getLine()) : 'asset', + new \Twig_Node($arguments), + $this->getLine() + ); + } +} + +class TargetPathNode extends AsseticNode +{ + private $node; + private $asset; + private $name; + + public function __construct(AsseticNode $node, AssetInterface $asset, $name) + { + $this->node = $node; + $this->asset = $asset; + $this->name = $name; + } + + public function compile(\Twig_Compiler $compiler) + { + BaseAsseticNode::compileAssetUrl($compiler, $this->asset, $this->name); + } + + public function getLine() + { + return $this->node->getLine(); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticNodeVisitor.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticNodeVisitor.php new file mode 100644 index 0000000..96126ee --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticNodeVisitor.php @@ -0,0 +1,120 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Twig; + +use Assetic\Extension\Twig\AsseticFilterFunction; +use Symfony\Bundle\AsseticBundle\Exception\InvalidBundleException; +use Symfony\Component\Templating\TemplateNameParserInterface; + +/** + * Assetic node visitor. + * + * @author Kris Wallsmith + */ +class AsseticNodeVisitor implements \Twig_NodeVisitorInterface +{ + private $templateNameParser; + private $enabledBundles; + private $useNodeName; + + public function __construct(TemplateNameParserInterface $templateNameParser, array $enabledBundles) + { + $this->templateNameParser = $templateNameParser; + $this->enabledBundles = $enabledBundles; + + $this->useNodeName = version_compare(\Twig_Environment::VERSION, '1.2.0-DEV', '<'); + } + + public function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env) + { + return $node; + } + + public function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) + { + if (!$formula = $this->checkNode($node, $env, $name)) { + return $node; + } + + // check the bundle + $templateRef = $this->templateNameParser->parse($env->getParser()->getStream()->getFilename()); + $bundle = $templateRef->get('bundle'); + if ($bundle && !in_array($bundle, $this->enabledBundles)) { + throw new InvalidBundleException($bundle, "the $name() function", $templateRef->getLogicalName(), $this->enabledBundles); + } + + list($input, $filters, $options) = $formula; + $line = $node->getLine(); + + // check context and call either asset() or path() + return new \Twig_Node_Expression_Conditional( + new \Twig_Node_Expression_GetAttr( + new \Twig_Node_Expression_Name('assetic', $line), + new \Twig_Node_Expression_Constant('use_controller', $line), + new \Twig_Node_Expression_Array(array(), 0), + \Twig_TemplateInterface::ARRAY_CALL, + $line + ), + new \Twig_Node_Expression_Function( + $this->useNodeName ? new \Twig_Node_Expression_Name('path', $line) : 'path', + new \Twig_Node(array( + new \Twig_Node_Expression_Constant('_assetic_'.$options['name'], $line), + )), + $line + ), + new \Twig_Node_Expression_Function( + $this->useNodeName ? new \Twig_Node_Expression_Name('asset', $line) : 'asset', + new \Twig_Node(array($node, new \Twig_Node_Expression_Constant(isset($options['package']) ? $options['package'] : null, $line))), + $line + ), + $line + ); + } + + /** + * Extracts formulae from filter function nodes. + * + * @return array|null The formula + */ + private function checkNode(\Twig_NodeInterface $node, \Twig_Environment $env, & $name = null) + { + if ($node instanceof \Twig_Node_Expression_Function) { + $name = $this->useNodeName + ? $node->getNode('name')->getAttribute('name') + : $node->getAttribute('name'); + if ($env->getFunction($name) instanceof AsseticFilterFunction) { + $arguments = array(); + foreach ($node->getNode('arguments') as $argument) { + $arguments[] = eval('return '.$env->compile($argument).';'); + } + + $invoker = $env->getExtension('assetic')->getFilterInvoker($name); + $factory = $invoker->getFactory(); + + $inputs = isset($arguments[0]) ? (array) $arguments[0] : array(); + $filters = $invoker->getFilters(); + $options = array_replace($invoker->getOptions(), isset($arguments[1]) ? $arguments[1] : array()); + + if (!isset($options['name'])) { + $options['name'] = $factory->generateAssetName($inputs, $filters); + } + + return array($inputs, $filters, $options); + } + } + } + + public function getPriority() + { + return 0; + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticTokenParser.php b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticTokenParser.php new file mode 100644 index 0000000..2a10fc9 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/Twig/AsseticTokenParser.php @@ -0,0 +1,66 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Twig; + +use Assetic\Asset\AssetInterface; +use Assetic\Extension\Twig\AsseticTokenParser as BaseAsseticTokenParser; +use Symfony\Bundle\AsseticBundle\Exception\InvalidBundleException; +use Symfony\Component\Templating\TemplateNameParserInterface; + +/** + * Assetic token parser. + * + * @author Kris Wallsmith + */ +class AsseticTokenParser extends BaseAsseticTokenParser +{ + private $templateNameParser; + private $enabledBundles; + + public function setTemplateNameParser(TemplateNameParserInterface $templateNameParser) + { + $this->templateNameParser = $templateNameParser; + } + + public function setEnabledBundles(array $enabledBundles = null) + { + $this->enabledBundles = $enabledBundles; + } + + public function parse(\Twig_Token $token) + { + if ($this->templateNameParser && is_array($this->enabledBundles)) { + // check the bundle + $templateRef = null; + try { + $templateRef = $this->templateNameParser->parse($this->parser->getStream()->getFilename()); + } catch (\RuntimeException $e) { + // this happens when the filename isn't a Bundle:* url + // and it contains ".." + } catch (\InvalidArgumentException $e) { + // this happens when the filename isn't a Bundle:* url + // but an absolute path instead + } + $bundle = $templateRef ? $templateRef->get('bundle') : null; + if ($bundle && !in_array($bundle, $this->enabledBundles)) { + throw new InvalidBundleException($bundle, "the {% {$this->getTag()} %} tag", $templateRef->getLogicalName(), $this->enabledBundles); + } + } + + return parent::parse($token); + } + + protected function createNode(AssetInterface $asset, \Twig_NodeInterface $body, array $inputs, array $filters, $name, array $attributes = array(), $lineno = 0, $tag = null) + { + return new AsseticNode($asset, $body, $inputs, $filters, $name, $attributes, $lineno, $tag); + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/UPGRADE.md b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/UPGRADE.md new file mode 100644 index 0000000..a61e131 --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/UPGRADE.md @@ -0,0 +1,10 @@ +Changes in version 2.1 +---------------------- + + * Friendlier exception message when using Assetic in a bundle that Assetic + has not been configured to scan + * Assets are no longer dumped to the filesystem when `cache:warmup` is run. + This can only be done using the `assetic:dump` command + * Added support for GSS filter + * Assetic's routes are now automatically added when `use_controller` is + `true` diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/composer.json b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/composer.json new file mode 100644 index 0000000..50944bf --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/composer.json @@ -0,0 +1,42 @@ +{ + "name": "symfony/assetic-bundle", + "description": "Integrates Assetic into Symfony2", + "keywords": ["assets", "compression", "minification"], + "homepage": "https://github.com/symfony/AsseticBundle", + "type": "symfony-bundle", + "license": "MIT", + "minimum-stability": "dev", + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "require": { + "php": ">=5.3.0", + "symfony/framework-bundle": "~2.1", + "kriswallsmith/assetic": "~1.1.0-beta1" + }, + "require-dev": { + "symfony/twig-bundle": "~2.1", + "symfony/console": "~2.1", + "symfony/class-loader": "~2.1", + "symfony/yaml": "~2.1", + "symfony/form": "~2.1", + "symfony/dom-crawler": "~2.1", + "symfony/css-selector": "~2.1" + }, + "suggest": { + "symfony/twig-bundle": "~2.1" + }, + "autoload": { + "psr-0": { "Symfony\\Bundle\\AsseticBundle": "" } + }, + "target-dir": "Symfony/Bundle/AsseticBundle", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + } +} diff --git a/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/phpunit.xml.dist b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/phpunit.xml.dist new file mode 100644 index 0000000..652036c --- /dev/null +++ b/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/icu/Symfony/Component/Icu/IcuCurrencyBundle.php b/vendor/symfony/icu/Symfony/Component/Icu/IcuCurrencyBundle.php new file mode 100644 index 0000000..ef34c60 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/IcuCurrencyBundle.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\Icu; + +use Symfony\Component\Intl\ResourceBundle\CurrencyBundle; +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface; + +/** + * An ICU-specific implementation of {@link \Symfony\Component\Intl\ResourceBundle\CurrencyBundleInterface}. + * + * This class normalizes the data of the ICU .res files to satisfy the contract + * defined in {@link \Symfony\Component\Intl\ResourceBundle\CurrencyBundleInterface}. + * + * @author Bernhard Schussek + */ +class IcuCurrencyBundle extends CurrencyBundle +{ + const INDEX_SYMBOL = 0; + + const INDEX_NAME = 1; + + const INDEX_FRACTION_DIGITS = 0; + + const INDEX_ROUNDING_INCREMENT = 1; + + public function __construct(StructuredBundleReaderInterface $reader) + { + parent::__construct(realpath(IcuData::getResourceDirectory() . '/curr'), $reader); + } + + /** + * {@inheritdoc} + */ + public function getCurrencyNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + $names = parent::getCurrencyNames($locale); + + $collator = new \Collator($locale); + $collator->asort($names); + + return $names; + } + + /** + * {@inheritdoc} + */ + public function getFractionDigits($currency) + { + $entry = $this->readEntry('supplementaldata', array('CurrencyMeta')); + + if (!isset($entry[$currency][self::INDEX_FRACTION_DIGITS])) { + // The 'DEFAULT' key contains the fraction digits and the rounding + // increment that are common for a lot of currencies. + // Only currencies with different values are added to the icu-data + // (e.g: CHF and JPY) + return $entry['DEFAULT'][self::INDEX_FRACTION_DIGITS]; + } + + return $entry[$currency][self::INDEX_FRACTION_DIGITS]; + } + + /** + * {@inheritdoc} + */ + public function getRoundingIncrement($currency) + { + $entry = $this->readEntry('supplementaldata', array('CurrencyMeta')); + + if (!isset($entry[$currency][self::INDEX_ROUNDING_INCREMENT])) { + // The 'DEFAULT' key contains the fraction digits and the rounding + // increment that are common for a lot of currencies. + // Only currencies with different values are added to the icu-data + // (e.g: CHF and JPY) + return $entry['DEFAULT'][self::INDEX_ROUNDING_INCREMENT]; + } + + return $entry[$currency][self::INDEX_ROUNDING_INCREMENT]; + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/IcuData.php b/vendor/symfony/icu/Symfony/Component/Icu/IcuData.php new file mode 100644 index 0000000..b3725b9 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/IcuData.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\Icu; + +use Symfony\Component\Intl\ResourceBundle\Reader\BinaryBundleReader; + +/** + * @author Bernhard Schussek + */ +class IcuData +{ + /** + * Returns the version of the bundled ICU data. + * + * @return string The version string. + */ + public static function getVersion() + { + return trim(file_get_contents(__DIR__ . '/Resources/data/version.txt')); + } + + /** + * Returns whether the ICU data is stubbed. + * + * @return Boolean Returns true if the ICU data is stubbed, false if it is + * loaded from ICU .res files. + */ + public static function isStubbed() + { + return false; + } + + /** + * Returns the path to the directory where the resource bundles are stored. + * + * @return string The absolute path to the resource directory. + */ + public static function getResourceDirectory() + { + return realpath(__DIR__ . '/Resources/data'); + } + + /** + * Returns a reader for reading resource bundles in this component. + * + * @return \Symfony\Component\Intl\ResourceBundle\Reader\BundleReaderInterface + */ + public static function getBundleReader() + { + return new BinaryBundleReader(); + } + + /** + * This class must not be instantiated. + */ + private function __construct() {} +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/IcuLanguageBundle.php b/vendor/symfony/icu/Symfony/Component/Icu/IcuLanguageBundle.php new file mode 100644 index 0000000..b3afa77 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/IcuLanguageBundle.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\Icu; + +use Symfony\Component\Intl\ResourceBundle\LanguageBundle; +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface; + +/** + * An ICU-specific implementation of {@link \Symfony\Component\Intl\ResourceBundle\LanguageBundleInterface}. + * + * This class normalizes the data of the ICU .res files to satisfy the contract + * defined in {@link \Symfony\Component\Intl\ResourceBundle\LanguageBundleInterface}. + * + * @author Bernhard Schussek + */ +class IcuLanguageBundle extends LanguageBundle +{ + public function __construct(StructuredBundleReaderInterface $reader) + { + parent::__construct(realpath(IcuData::getResourceDirectory() . '/lang'), $reader); + } + + /** + * {@inheritdoc} + */ + public function getLanguageName($lang, $region = null, $locale = null) + { + if ('mul' === $lang) { + return null; + } + + return parent::getLanguageName($lang, $region, $locale); + } + + /** + * {@inheritdoc} + */ + public function getLanguageNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + $languages = parent::getLanguageNames($locale); + + $collator = new \Collator($locale); + $collator->asort($languages); + + // "mul" is the code for multiple languages + unset($languages['mul']); + + return $languages; + } + + /** + * {@inheritdoc} + */ + public function getScriptNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + $scripts = parent::getScriptNames($locale); + + $collator = new \Collator($locale); + $collator->asort($scripts); + + return $scripts; + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/IcuLocaleBundle.php b/vendor/symfony/icu/Symfony/Component/Icu/IcuLocaleBundle.php new file mode 100644 index 0000000..215f215 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/IcuLocaleBundle.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\Icu; + +use Symfony\Component\Intl\ResourceBundle\LocaleBundle; +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface; + +/** + * An ICU-specific implementation of {@link \Symfony\Component\Intl\ResourceBundle\LocaleBundleInterface}. + * + * This class normalizes the data of the ICU .res files to satisfy the contract + * defined in {@link \Symfony\Component\Intl\ResourceBundle\LocaleBundleInterface}. + * + * @author Bernhard Schussek + */ +class IcuLocaleBundle extends LocaleBundle +{ + public function __construct(StructuredBundleReaderInterface $reader) + { + parent::__construct(realpath(IcuData::getResourceDirectory() . '/locales'), $reader); + } + + /** + * {@inheritdoc} + */ + public function getLocaleNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + $locales = parent::getLocaleNames($locale); + + $collator = new \Collator($locale); + $collator->asort($locales); + + return $locales; + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/IcuRegionBundle.php b/vendor/symfony/icu/Symfony/Component/Icu/IcuRegionBundle.php new file mode 100644 index 0000000..5617bb7 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/IcuRegionBundle.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\Icu; + +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface; +use Symfony\Component\Intl\ResourceBundle\RegionBundle; + +/** + * An ICU-specific implementation of {@link \Symfony\Component\Intl\ResourceBundle\RegionBundleInterface}. + * + * This class normalizes the data of the ICU .res files to satisfy the contract + * defined in {@link \Symfony\Component\Intl\ResourceBundle\RegionBundleInterface}. + * + * @author Bernhard Schussek + */ +class IcuRegionBundle extends RegionBundle +{ + public function __construct(StructuredBundleReaderInterface $reader) + { + parent::__construct(realpath(IcuData::getResourceDirectory() . '/region'), $reader); + } + + /** + * {@inheritdoc} + */ + public function getCountryName($country, $locale = null) + { + if ('ZZ' === $country || ctype_digit((string) $country)) { + return null; + } + + return parent::getCountryName($country, $locale); + } + + /** + * {@inheritdoc} + */ + public function getCountryNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + $countries = parent::getCountryNames($locale); + + // "ZZ" is the code for unknown country + unset($countries['ZZ']); + + // Global countries (f.i. "America") have numeric codes + // Countries have alphabetic codes + foreach ($countries as $code => $name) { + // is_int() does not work, since some numbers start with '0' and + // thus are stored as strings. + // The (string) cast is necessary since ctype_digit() returns false + // for integers. + if (ctype_digit((string) $code)) { + unset($countries[$code]); + } + } + + $collator = new \Collator($locale); + $collator->asort($countries); + + return $countries; + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/LICENSE b/vendor/symfony/icu/Symfony/Component/Icu/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/icu/Symfony/Component/Icu/README.md b/vendor/symfony/icu/Symfony/Component/Icu/README.md new file mode 100644 index 0000000..895617e --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/README.md @@ -0,0 +1,25 @@ +Icu Component +============= + +Contains data of the ICU library. + +The bundled resource files have the [resource bundle format version 2.*] [1], +which can be read using ICU 4.4 and later. Compatibility can be tested with the +test-compat.php script bundled in the Intl component: + + php path/to/Symfony/Component/Intl/Resources/bin/test-compat.php + +You should not directly use this component. Use it through the API of the +[Intl component] [2] instead. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Icu/ + $ composer.phar install --dev + $ phpunit + +[1]: http://site.icu-project.org/design/data/res2 +[2]: https://github.com/symfony/Intl diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/af.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/af.res new file mode 100644 index 0000000..4294dde Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/af.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/af_NA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/af_NA.res new file mode 100644 index 0000000..c0bca2d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/af_NA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/agq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/agq.res new file mode 100644 index 0000000..800c739 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/agq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ak.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ak.res new file mode 100644 index 0000000..405f0c8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ak.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/am.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/am.res new file mode 100644 index 0000000..e6354c3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/am.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ar.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ar.res new file mode 100644 index 0000000..0d787bd Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ar.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/as.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/as.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/as.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/asa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/asa.res new file mode 100644 index 0000000..7e1542d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/asa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az.res new file mode 100644 index 0000000..12816b5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_AZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_AZ.res new file mode 100644 index 0000000..ff23f22 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_AZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_Cyrl.res new file mode 100644 index 0000000..2030632 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_Latn.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_Latn_AZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_Latn_AZ.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/az_Latn_AZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bas.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bas.res new file mode 100644 index 0000000..9933441 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bas.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/be.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/be.res new file mode 100644 index 0000000..ff1a49b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/be.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bem.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bem.res new file mode 100644 index 0000000..1ceeab5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bem.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bez.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bez.res new file mode 100644 index 0000000..5e93ff1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bez.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bg.res new file mode 100644 index 0000000..c31c9ad Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bm.res new file mode 100644 index 0000000..796358c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bn.res new file mode 100644 index 0000000..51b8447 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bn_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bn_IN.res new file mode 100644 index 0000000..1971914 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bn_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bo.res new file mode 100644 index 0000000..5975a5d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/br.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/br.res new file mode 100644 index 0000000..abca79b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/br.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/brx.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/brx.res new file mode 100644 index 0000000..e52d0ac Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/brx.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs.res new file mode 100644 index 0000000..142bc6d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_BA.res new file mode 100644 index 0000000..a916f07 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_Cyrl.res new file mode 100644 index 0000000..e083d73 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_Latn.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_Latn_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_Latn_BA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/bs_Latn_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ca.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ca.res new file mode 100644 index 0000000..c709d6e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ca.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/cgg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/cgg.res new file mode 100644 index 0000000..6a6e548 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/cgg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/chr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/chr.res new file mode 100644 index 0000000..64c85bb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/chr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/cs.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/cs.res new file mode 100644 index 0000000..8624975 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/cs.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/cy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/cy.res new file mode 100644 index 0000000..b52d0e1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/cy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/da.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/da.res new file mode 100644 index 0000000..21d780b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/da.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dav.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dav.res new file mode 100644 index 0000000..22850e1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dav.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/de.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/de.res new file mode 100644 index 0000000..60858eb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/de.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/de_LU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/de_LU.res new file mode 100644 index 0000000..63e2f55 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/de_LU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dje.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dje.res new file mode 100644 index 0000000..91d7060 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dje.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dua.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dua.res new file mode 100644 index 0000000..f359575 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dua.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dyo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dyo.res new file mode 100644 index 0000000..bc30825 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dyo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dz.res new file mode 100644 index 0000000..da154ab Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/dz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ebu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ebu.res new file mode 100644 index 0000000..61d8233 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ebu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ee.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ee.res new file mode 100644 index 0000000..f3910a6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ee.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/el.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/el.res new file mode 100644 index 0000000..ba17c31 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/el.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en.res new file mode 100644 index 0000000..65f3e1a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_AU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_AU.res new file mode 100644 index 0000000..d295818 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_AU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BB.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BB.res new file mode 100644 index 0000000..d67d1a2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BB.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BM.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BM.res new file mode 100644 index 0000000..e8da74d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BM.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BW.res new file mode 100644 index 0000000..afacb55 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BZ.res new file mode 100644 index 0000000..7241e96 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_BZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_CA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_CA.res new file mode 100644 index 0000000..98ce5fb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_CA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_GB.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_GB.res new file mode 100644 index 0000000..ada036f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_GB.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_HK.res new file mode 100644 index 0000000..f2d2a61 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_JM.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_JM.res new file mode 100644 index 0000000..852458a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_JM.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_MT.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_MT.res new file mode 100644 index 0000000..01c3632 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_MT.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_NA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_NA.res new file mode 100644 index 0000000..e30874b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_NA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_NH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_NH.res new file mode 100644 index 0000000..1088544 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_NH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_NZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_NZ.res new file mode 100644 index 0000000..d0cc6ec Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_NZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_PH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_PH.res new file mode 100644 index 0000000..b81b9a2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_PH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_PK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_PK.res new file mode 100644 index 0000000..352a312 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_PK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_RH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_RH.res new file mode 100644 index 0000000..df1b860 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_RH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_SG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_SG.res new file mode 100644 index 0000000..9f4611a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_SG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_TT.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_TT.res new file mode 100644 index 0000000..fd3385f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_TT.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_VU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_VU.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_VU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_ZA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_ZA.res new file mode 100644 index 0000000..56728c0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_ZA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_ZW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_ZW.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en_ZW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/eo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/eo.res new file mode 100644 index 0000000..a231307 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/eo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es.res new file mode 100644 index 0000000..9bbd9a9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_AR.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_AR.res new file mode 100644 index 0000000..a19ee89 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_AR.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_BO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_BO.res new file mode 100644 index 0000000..a87f48e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_BO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_CL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_CL.res new file mode 100644 index 0000000..16878d4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_CL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_CO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_CO.res new file mode 100644 index 0000000..0f42ae8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_CO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_CR.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_CR.res new file mode 100644 index 0000000..38f87e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_CR.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_DO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_DO.res new file mode 100644 index 0000000..6b4d741 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_DO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_EC.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_EC.res new file mode 100644 index 0000000..ad70fa5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_EC.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_GT.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_GT.res new file mode 100644 index 0000000..7eccc60 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_GT.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_HN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_HN.res new file mode 100644 index 0000000..43478ae Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_HN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_MX.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_MX.res new file mode 100644 index 0000000..f8363a7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_MX.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_NI.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_NI.res new file mode 100644 index 0000000..86a4192 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_NI.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PA.res new file mode 100644 index 0000000..f7edecb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PE.res new file mode 100644 index 0000000..4a2d9ce Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PR.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PR.res new file mode 100644 index 0000000..517ffdc Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PR.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PY.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PY.res new file mode 100644 index 0000000..e12bda8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_PY.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_US.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_US.res new file mode 100644 index 0000000..1d8cae1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_US.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_UY.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_UY.res new file mode 100644 index 0000000..0e0a113 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_UY.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_VE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_VE.res new file mode 100644 index 0000000..c8e9139 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/es_VE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/et.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/et.res new file mode 100644 index 0000000..3a59a57 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/et.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/eu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/eu.res new file mode 100644 index 0000000..446a639 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/eu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ewo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ewo.res new file mode 100644 index 0000000..d28fff2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ewo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fa.res new file mode 100644 index 0000000..2b76bd4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fa_AF.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fa_AF.res new file mode 100644 index 0000000..3fa3540 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fa_AF.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ff.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ff.res new file mode 100644 index 0000000..b25e039 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ff.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fi.res new file mode 100644 index 0000000..29b16b4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fil.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fil.res new file mode 100644 index 0000000..f9fdeab Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fil.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fil_PH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fil_PH.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fil_PH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fo.res new file mode 100644 index 0000000..4813060 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr.res new file mode 100644 index 0000000..2aae3f1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_BI.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_BI.res new file mode 100644 index 0000000..9c61374 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_BI.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_CA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_CA.res new file mode 100644 index 0000000..1b3b1d0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_CA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_DJ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_DJ.res new file mode 100644 index 0000000..8083936 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_DJ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_GN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_GN.res new file mode 100644 index 0000000..cac6d3c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_GN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_KM.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_KM.res new file mode 100644 index 0000000..a8e67e0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_KM.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_LU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_LU.res new file mode 100644 index 0000000..5d09178 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/fr_LU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ga.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ga.res new file mode 100644 index 0000000..32053a2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ga.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gl.res new file mode 100644 index 0000000..65d2a8e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gsw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gsw.res new file mode 100644 index 0000000..3e29103 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gsw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gu.res new file mode 100644 index 0000000..3a4a60c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/guz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/guz.res new file mode 100644 index 0000000..de22883 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/guz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gv.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/gv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha.res new file mode 100644 index 0000000..62448ac Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_GH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_GH.res new file mode 100644 index 0000000..1edc596 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_GH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn_GH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn_GH.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn_GH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn_NE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn_NE.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn_NE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn_NG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn_NG.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_Latn_NG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_NE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_NE.res new file mode 100644 index 0000000..46a9888 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_NE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_NG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_NG.res new file mode 100644 index 0000000..954922a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ha_NG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/haw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/haw.res new file mode 100644 index 0000000..da8a7c3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/haw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/he.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/he.res new file mode 100644 index 0000000..dc8df8a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/he.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/he_IL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/he_IL.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/he_IL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hi.res new file mode 100644 index 0000000..c30b5e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hr.res new file mode 100644 index 0000000..fdf7ce8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hu.res new file mode 100644 index 0000000..5489364 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hy.res new file mode 100644 index 0000000..67fc7ff Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/hy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/id.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/id.res new file mode 100644 index 0000000..290b65d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/id.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/id_ID.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/id_ID.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/id_ID.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ig.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ig.res new file mode 100644 index 0000000..5a58600 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ig.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ii.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ii.res new file mode 100644 index 0000000..bbf28fa Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ii.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/in.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/in.res new file mode 100644 index 0000000..6a5ac33 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/in.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/in_ID.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/in_ID.res new file mode 100644 index 0000000..dcdcac2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/in_ID.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/is.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/is.res new file mode 100644 index 0000000..b95c429 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/is.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/it.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/it.res new file mode 100644 index 0000000..c4b341f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/it.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/iw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/iw.res new file mode 100644 index 0000000..e64e1b3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/iw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/iw_IL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/iw_IL.res new file mode 100644 index 0000000..e586d07 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/iw_IL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ja.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ja.res new file mode 100644 index 0000000..d2a561c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ja.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ja_JP.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ja_JP.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ja_JP.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ja_JP_TRADITIONAL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ja_JP_TRADITIONAL.res new file mode 100644 index 0000000..4b6ec41 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ja_JP_TRADITIONAL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/jgo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/jgo.res new file mode 100644 index 0000000..d101af9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/jgo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/jmc.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/jmc.res new file mode 100644 index 0000000..d3b69a0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/jmc.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ka.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ka.res new file mode 100644 index 0000000..dc62e9f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ka.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kab.res new file mode 100644 index 0000000..d778110 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kam.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kam.res new file mode 100644 index 0000000..e3c2cb9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kam.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kde.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kde.res new file mode 100644 index 0000000..1f5ca9a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kde.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kea.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kea.res new file mode 100644 index 0000000..de5be58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kea.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/khq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/khq.res new file mode 100644 index 0000000..274baa7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/khq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ki.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ki.res new file mode 100644 index 0000000..0681a12 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ki.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk.res new file mode 100644 index 0000000..ae378c2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk_Cyrl.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk_Cyrl_KZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk_Cyrl_KZ.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk_Cyrl_KZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk_KZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk_KZ.res new file mode 100644 index 0000000..6cfb7f0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kk_KZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kl.res new file mode 100644 index 0000000..3caab70 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kln.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kln.res new file mode 100644 index 0000000..31fa862 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kln.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/km.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/km.res new file mode 100644 index 0000000..0386621 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/km.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kn.res new file mode 100644 index 0000000..026435a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ko.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ko.res new file mode 100644 index 0000000..fd39779 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ko.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kok.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kok.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kok.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks.res new file mode 100644 index 0000000..de6fc96 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks_Arab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks_Arab.res new file mode 100644 index 0000000..dd4ea19 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks_Arab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks_Arab_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks_Arab_IN.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks_Arab_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks_IN.res new file mode 100644 index 0000000..bd13a11 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ks_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ksb.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ksb.res new file mode 100644 index 0000000..5353a48 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ksb.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ksf.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ksf.res new file mode 100644 index 0000000..57bd71f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ksf.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kw.res new file mode 100644 index 0000000..a231307 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/kw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lag.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lag.res new file mode 100644 index 0000000..60807f3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lag.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lg.res new file mode 100644 index 0000000..e492870 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ln.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ln.res new file mode 100644 index 0000000..f872ee2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ln.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lo.res new file mode 100644 index 0000000..603e435 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lt.res new file mode 100644 index 0000000..47e89d3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lu.res new file mode 100644 index 0000000..e524a07 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/luo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/luo.res new file mode 100644 index 0000000..23f726b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/luo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/luy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/luy.res new file mode 100644 index 0000000..4ac106b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/luy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lv.res new file mode 100644 index 0000000..972518e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/lv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mas.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mas.res new file mode 100644 index 0000000..acf2f72 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mas.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mas_TZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mas_TZ.res new file mode 100644 index 0000000..d88b1c0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mas_TZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mer.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mer.res new file mode 100644 index 0000000..6c1e7d3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mer.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mfe.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mfe.res new file mode 100644 index 0000000..b9929db Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mfe.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mg.res new file mode 100644 index 0000000..0894140 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mgh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mgh.res new file mode 100644 index 0000000..555a4e1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mgh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mgo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mgo.res new file mode 100644 index 0000000..31971e1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mgo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mk.res new file mode 100644 index 0000000..a6e2dd5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ml.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ml.res new file mode 100644 index 0000000..711a25e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ml.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mo.res new file mode 100644 index 0000000..3f8911a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mr.res new file mode 100644 index 0000000..7ab7a8f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ms.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ms.res new file mode 100644 index 0000000..2b53266 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ms.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ms_BN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ms_BN.res new file mode 100644 index 0000000..68840a3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ms_BN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mt.res new file mode 100644 index 0000000..f3f60b8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mua.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mua.res new file mode 100644 index 0000000..be481fe Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/mua.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/my.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/my.res new file mode 100644 index 0000000..fbae03d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/my.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/naq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/naq.res new file mode 100644 index 0000000..3dbab06 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/naq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nb.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nb.res new file mode 100644 index 0000000..8fef453 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nb.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nb_NO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nb_NO.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nb_NO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nd.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nd.res new file mode 100644 index 0000000..e816055 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nd.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ne.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ne.res new file mode 100644 index 0000000..445dafc Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ne.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ne_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ne_IN.res new file mode 100644 index 0000000..da90bcc Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ne_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl.res new file mode 100644 index 0000000..d1dafa1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl_AW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl_AW.res new file mode 100644 index 0000000..40d2bc3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl_AW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl_CW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl_CW.res new file mode 100644 index 0000000..244ddca Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl_CW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl_SX.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl_SX.res new file mode 100644 index 0000000..244ddca Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nl_SX.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nmg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nmg.res new file mode 100644 index 0000000..66db3fc Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nmg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nn.res new file mode 100644 index 0000000..12a0c01 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nn_NO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nn_NO.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nn_NO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/no.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/no.res new file mode 100644 index 0000000..f11a728 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/no.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/no_NO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/no_NO.res new file mode 100644 index 0000000..4c61d61 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/no_NO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/no_NO_NY.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/no_NO_NY.res new file mode 100644 index 0000000..4599f32 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/no_NO_NY.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nus.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nus.res new file mode 100644 index 0000000..f359575 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nus.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nyn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nyn.res new file mode 100644 index 0000000..c4a5384 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/nyn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/om.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/om.res new file mode 100644 index 0000000..7c95d93 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/om.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/om_KE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/om_KE.res new file mode 100644 index 0000000..84650b6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/om_KE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/or.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/or.res new file mode 100644 index 0000000..7ac41ca Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/or.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa.res new file mode 100644 index 0000000..d09eaea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Arab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Arab.res new file mode 100644 index 0000000..e897cfb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Arab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Arab_PK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Arab_PK.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Arab_PK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Guru.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Guru.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Guru.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Guru_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Guru_IN.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_Guru_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_IN.res new file mode 100644 index 0000000..5a7533d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_PK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_PK.res new file mode 100644 index 0000000..a713857 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pa_PK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pl.res new file mode 100644 index 0000000..b7ec655 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ps.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ps.res new file mode 100644 index 0000000..745aab6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ps.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt.res new file mode 100644 index 0000000..71e5be6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_AO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_AO.res new file mode 100644 index 0000000..4158462 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_AO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_MZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_MZ.res new file mode 100644 index 0000000..5359281 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_MZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_PT.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_PT.res new file mode 100644 index 0000000..79210d5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_PT.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_ST.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_ST.res new file mode 100644 index 0000000..eb1c3fb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/pt_ST.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rm.res new file mode 100644 index 0000000..037c2bb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rn.res new file mode 100644 index 0000000..a451c16 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ro.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ro.res new file mode 100644 index 0000000..fa5e5db Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ro.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ro_MD.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ro_MD.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ro_MD.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rof.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rof.res new file mode 100644 index 0000000..93c2360 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rof.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/root.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/root.res new file mode 100644 index 0000000..0f6cf24 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/root.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ru.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ru.res new file mode 100644 index 0000000..2019e7a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ru.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rw.res new file mode 100644 index 0000000..8d1fa7b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rwk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rwk.res new file mode 100644 index 0000000..d3b69a0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/rwk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/saq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/saq.res new file mode 100644 index 0000000..e65dda5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/saq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sbp.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sbp.res new file mode 100644 index 0000000..7f338f9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sbp.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/seh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/seh.res new file mode 100644 index 0000000..dc1bef2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/seh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ses.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ses.res new file mode 100644 index 0000000..274baa7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ses.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sg.res new file mode 100644 index 0000000..87fa01c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh.res new file mode 100644 index 0000000..0cdf3e0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh_BA.res new file mode 100644 index 0000000..ba85740 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh_CS.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh_YU.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sh_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi.res new file mode 100644 index 0000000..df177b3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_Latn.res new file mode 100644 index 0000000..70522c3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_Latn_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_Latn_MA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_Latn_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_MA.res new file mode 100644 index 0000000..36bc365 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_Tfng.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_Tfng.res new file mode 100644 index 0000000..81f334e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/shi_Tfng.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/si.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/si.res new file mode 100644 index 0000000..261710a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/si.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sk.res new file mode 100644 index 0000000..3e3eb93 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sl.res new file mode 100644 index 0000000..0216918 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sn.res new file mode 100644 index 0000000..3f3af44 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so.res new file mode 100644 index 0000000..629e189 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so_DJ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so_DJ.res new file mode 100644 index 0000000..558a049 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so_DJ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so_ET.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so_ET.res new file mode 100644 index 0000000..e77ad53 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so_ET.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so_KE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so_KE.res new file mode 100644 index 0000000..84650b6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/so_KE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sq.res new file mode 100644 index 0000000..c3a21bb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr.res new file mode 100644 index 0000000..d695939 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_BA.res new file mode 100644 index 0000000..dfc6b9c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_CS.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_BA.res new file mode 100644 index 0000000..e625d85 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_CS.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_RS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_RS.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_RS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_YU.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Cyrl_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn.res new file mode 100644 index 0000000..7538d8e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_BA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_CS.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_ME.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_ME.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_ME.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_RS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_RS.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_RS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_YU.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_Latn_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_ME.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_ME.res new file mode 100644 index 0000000..aac52f2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_ME.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_RS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_RS.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_RS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_YU.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sr_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/supplementaldata.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/supplementaldata.res new file mode 100644 index 0000000..63d1782 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/supplementaldata.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sv.res new file mode 100644 index 0000000..d2be5a5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sw.res new file mode 100644 index 0000000..0ccbcd6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/sw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/swc.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/swc.res new file mode 100644 index 0000000..0841a0d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/swc.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta.res new file mode 100644 index 0000000..1f5404d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta_LK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta_LK.res new file mode 100644 index 0000000..e202753 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta_LK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta_MY.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta_MY.res new file mode 100644 index 0000000..588a1a4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta_MY.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta_SG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta_SG.res new file mode 100644 index 0000000..588a1a4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ta_SG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/te.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/te.res new file mode 100644 index 0000000..ac07787 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/te.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/teo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/teo.res new file mode 100644 index 0000000..67655c3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/teo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/teo_KE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/teo_KE.res new file mode 100644 index 0000000..c38c094 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/teo_KE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/th.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/th.res new file mode 100644 index 0000000..fdc8411 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/th.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/th_TH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/th_TH.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/th_TH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/th_TH_TRADITIONAL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/th_TH_TRADITIONAL.res new file mode 100644 index 0000000..4341e54 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/th_TH_TRADITIONAL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ti.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ti.res new file mode 100644 index 0000000..561a1de Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ti.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ti_ER.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ti_ER.res new file mode 100644 index 0000000..f97351f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ti_ER.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tl.res new file mode 100644 index 0000000..a705804 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tl_PH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tl_PH.res new file mode 100644 index 0000000..6c39389 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tl_PH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/to.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/to.res new file mode 100644 index 0000000..6383b5c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/to.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tr.res new file mode 100644 index 0000000..244ae3b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/twq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/twq.res new file mode 100644 index 0000000..91d7060 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/twq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm.res new file mode 100644 index 0000000..2d0e1fb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm_Latn.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm_Latn_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm_Latn_MA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm_Latn_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm_MA.res new file mode 100644 index 0000000..fa1287b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/tzm_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uk.res new file mode 100644 index 0000000..64f70d6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ur.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ur.res new file mode 100644 index 0000000..357eebd Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/ur.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_AF.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_AF.res new file mode 100644 index 0000000..4243259 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_AF.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Arab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Arab.res new file mode 100644 index 0000000..eeeb042 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Arab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Arab_AF.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Arab_AF.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Arab_AF.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Cyrl.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Cyrl_UZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Cyrl_UZ.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Cyrl_UZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Latn.res new file mode 100644 index 0000000..eae4c4e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_UZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_UZ.res new file mode 100644 index 0000000..ca4f1c3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/uz_UZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai.res new file mode 100644 index 0000000..b90e1a6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_LR.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_LR.res new file mode 100644 index 0000000..8d03de2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_LR.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_Latn.res new file mode 100644 index 0000000..2ec68b8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_Vaii.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_Vaii.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_Vaii.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_Vaii_LR.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_Vaii_LR.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vai_Vaii_LR.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vi.res new file mode 100644 index 0000000..8a0f4a1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vun.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vun.res new file mode 100644 index 0000000..765be7a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/vun.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/xog.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/xog.res new file mode 100644 index 0000000..f89c077 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/xog.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/yav.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/yav.res new file mode 100644 index 0000000..f6d2d9f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/yav.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/yo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/yo.res new file mode 100644 index 0000000..9f6408a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/yo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh.res new file mode 100644 index 0000000..db8756d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_CN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_CN.res new file mode 100644 index 0000000..b92fec8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_CN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_HK.res new file mode 100644 index 0000000..740476e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_CN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_CN.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_CN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_HK.res new file mode 100644 index 0000000..f8c04eb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_MO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_MO.res new file mode 100644 index 0000000..0c4d9e6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_MO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_SG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_SG.res new file mode 100644 index 0000000..df902b7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hans_SG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant.res new file mode 100644 index 0000000..d782115 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant_HK.res new file mode 100644 index 0000000..d871936 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant_MO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant_MO.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant_MO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant_TW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant_TW.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_Hant_TW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_MO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_MO.res new file mode 100644 index 0000000..9970807 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_MO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_SG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_SG.res new file mode 100644 index 0000000..6dc927e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_SG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_TW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_TW.res new file mode 100644 index 0000000..27020e6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zh_TW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zu.res new file mode 100644 index 0000000..141f84f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/zu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/af.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/af.res new file mode 100644 index 0000000..41a2f4b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/af.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/agq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/agq.res new file mode 100644 index 0000000..769a55f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/agq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ak.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ak.res new file mode 100644 index 0000000..de9601e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ak.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/am.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/am.res new file mode 100644 index 0000000..c488ea0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/am.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ar.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ar.res new file mode 100644 index 0000000..4f28835 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ar.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/as.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/as.res new file mode 100644 index 0000000..abdf70a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/as.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/asa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/asa.res new file mode 100644 index 0000000..a1f42f8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/asa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az.res new file mode 100644 index 0000000..b366b17 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_AZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_AZ.res new file mode 100644 index 0000000..ff23f22 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_AZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_Cyrl.res new file mode 100644 index 0000000..d6502ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_Latn.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_Latn_AZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_Latn_AZ.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/az_Latn_AZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bas.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bas.res new file mode 100644 index 0000000..c4b9428 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bas.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/be.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/be.res new file mode 100644 index 0000000..74a937c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/be.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bem.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bem.res new file mode 100644 index 0000000..e37ee57 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bem.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bez.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bez.res new file mode 100644 index 0000000..99be726 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bez.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bg.res new file mode 100644 index 0000000..5c3693d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bm.res new file mode 100644 index 0000000..a9c7a81 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bn.res new file mode 100644 index 0000000..6f627b3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bn_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bn_IN.res new file mode 100644 index 0000000..a16f439 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bn_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bo.res new file mode 100644 index 0000000..ab39665 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/br.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/br.res new file mode 100644 index 0000000..99431b8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/br.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/brx.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/brx.res new file mode 100644 index 0000000..c71cb19 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/brx.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs.res new file mode 100644 index 0000000..6352349 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_BA.res new file mode 100644 index 0000000..a916f07 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_Cyrl.res new file mode 100644 index 0000000..e1ccd20 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_Latn.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_Latn_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_Latn_BA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/bs_Latn_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ca.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ca.res new file mode 100644 index 0000000..ecde474 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ca.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/cgg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/cgg.res new file mode 100644 index 0000000..be7f0d5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/cgg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/chr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/chr.res new file mode 100644 index 0000000..3852a0d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/chr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/cs.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/cs.res new file mode 100644 index 0000000..6abfdbb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/cs.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/cy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/cy.res new file mode 100644 index 0000000..03d85be Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/cy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/da.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/da.res new file mode 100644 index 0000000..04b22a0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/da.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dav.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dav.res new file mode 100644 index 0000000..0c16489 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dav.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/de.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/de.res new file mode 100644 index 0000000..0749e56 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/de.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/de_CH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/de_CH.res new file mode 100644 index 0000000..7595884 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/de_CH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dje.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dje.res new file mode 100644 index 0000000..669dd18 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dje.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dua.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dua.res new file mode 100644 index 0000000..21d1284 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dua.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dyo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dyo.res new file mode 100644 index 0000000..6df3d00 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dyo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dz.res new file mode 100644 index 0000000..30dabcf Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/dz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ebu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ebu.res new file mode 100644 index 0000000..1027479 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ebu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ee.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ee.res new file mode 100644 index 0000000..80722e7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ee.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/el.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/el.res new file mode 100644 index 0000000..7c1500b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/el.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en.res new file mode 100644 index 0000000..643e73d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_GB.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_GB.res new file mode 100644 index 0000000..c8e0ef2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_GB.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_NH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_NH.res new file mode 100644 index 0000000..1088544 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_NH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_RH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_RH.res new file mode 100644 index 0000000..df1b860 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_RH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_VU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_VU.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_VU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_ZW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_ZW.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en_ZW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/eo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/eo.res new file mode 100644 index 0000000..b97241d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/eo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/es.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/es.res new file mode 100644 index 0000000..0082a06 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/es.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/es_419.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/es_419.res new file mode 100644 index 0000000..6b550c6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/es_419.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/es_CL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/es_CL.res new file mode 100644 index 0000000..b74f613 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/es_CL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/et.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/et.res new file mode 100644 index 0000000..ef208fa Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/et.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/eu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/eu.res new file mode 100644 index 0000000..9adef50 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/eu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ewo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ewo.res new file mode 100644 index 0000000..4c62a48 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ewo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fa.res new file mode 100644 index 0000000..5ac7252 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fa_AF.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fa_AF.res new file mode 100644 index 0000000..e6b416d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fa_AF.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ff.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ff.res new file mode 100644 index 0000000..d1e0b94 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ff.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fi.res new file mode 100644 index 0000000..05a21fe Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fil.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fil.res new file mode 100644 index 0000000..fae708c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fil.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fil_PH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fil_PH.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fil_PH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fo.res new file mode 100644 index 0000000..7bc967d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fr.res new file mode 100644 index 0000000..d5c3bfa Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fr_CA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fr_CA.res new file mode 100644 index 0000000..7f92a2f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/fr_CA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ga.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ga.res new file mode 100644 index 0000000..7f7f958 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ga.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gl.res new file mode 100644 index 0000000..f84459e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gsw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gsw.res new file mode 100644 index 0000000..d8a57b6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gsw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gu.res new file mode 100644 index 0000000..975db18 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/guz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/guz.res new file mode 100644 index 0000000..a4eccdd Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/guz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gv.res new file mode 100644 index 0000000..56930a4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/gv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha.res new file mode 100644 index 0000000..dda52be Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_GH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_GH.res new file mode 100644 index 0000000..1edc596 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_GH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn_GH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn_GH.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn_GH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn_NE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn_NE.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn_NE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn_NG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn_NG.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_Latn_NG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_NE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_NE.res new file mode 100644 index 0000000..46a9888 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_NE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_NG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_NG.res new file mode 100644 index 0000000..954922a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ha_NG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/haw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/haw.res new file mode 100644 index 0000000..367ab2e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/haw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/he.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/he.res new file mode 100644 index 0000000..696bf3a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/he.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/he_IL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/he_IL.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/he_IL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hi.res new file mode 100644 index 0000000..e3b41c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hr.res new file mode 100644 index 0000000..4938324 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hu.res new file mode 100644 index 0000000..cdaedad Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hy.res new file mode 100644 index 0000000..dc94d3e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/hy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/id.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/id.res new file mode 100644 index 0000000..820db69 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/id.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/id_ID.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/id_ID.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/id_ID.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ig.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ig.res new file mode 100644 index 0000000..d7d9634 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ig.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ii.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ii.res new file mode 100644 index 0000000..ee28272 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ii.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/in.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/in.res new file mode 100644 index 0000000..6a5ac33 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/in.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/in_ID.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/in_ID.res new file mode 100644 index 0000000..dcdcac2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/in_ID.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/is.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/is.res new file mode 100644 index 0000000..9f64b86 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/is.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/it.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/it.res new file mode 100644 index 0000000..4492472 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/it.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/iw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/iw.res new file mode 100644 index 0000000..e64e1b3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/iw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/iw_IL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/iw_IL.res new file mode 100644 index 0000000..e586d07 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/iw_IL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ja.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ja.res new file mode 100644 index 0000000..da12980 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ja.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ja_JP.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ja_JP.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ja_JP.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ja_JP_TRADITIONAL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ja_JP_TRADITIONAL.res new file mode 100644 index 0000000..4b6ec41 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ja_JP_TRADITIONAL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/jgo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/jgo.res new file mode 100644 index 0000000..b855ea6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/jgo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/jmc.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/jmc.res new file mode 100644 index 0000000..37aa929 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/jmc.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ka.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ka.res new file mode 100644 index 0000000..6371616 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ka.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kab.res new file mode 100644 index 0000000..8221307 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kam.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kam.res new file mode 100644 index 0000000..f4c1d3d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kam.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kde.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kde.res new file mode 100644 index 0000000..44c1df0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kde.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kea.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kea.res new file mode 100644 index 0000000..2479ffe Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kea.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/khq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/khq.res new file mode 100644 index 0000000..0366fc1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/khq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ki.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ki.res new file mode 100644 index 0000000..de26230 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ki.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk.res new file mode 100644 index 0000000..4f793ed Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk_Cyrl.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk_Cyrl_KZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk_Cyrl_KZ.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk_Cyrl_KZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk_KZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk_KZ.res new file mode 100644 index 0000000..6cfb7f0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kk_KZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kl.res new file mode 100644 index 0000000..6fd8a2b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kln.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kln.res new file mode 100644 index 0000000..7315cfc Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kln.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/km.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/km.res new file mode 100644 index 0000000..d57a925 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/km.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kn.res new file mode 100644 index 0000000..ec3e2e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ko.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ko.res new file mode 100644 index 0000000..06cceb8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ko.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kok.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kok.res new file mode 100644 index 0000000..42a2ff4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kok.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks.res new file mode 100644 index 0000000..28f1beb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks_Arab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks_Arab.res new file mode 100644 index 0000000..dd4ea19 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks_Arab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks_Arab_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks_Arab_IN.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks_Arab_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks_IN.res new file mode 100644 index 0000000..bd13a11 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ks_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ksb.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ksb.res new file mode 100644 index 0000000..17e9424 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ksb.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ksf.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ksf.res new file mode 100644 index 0000000..5f2d53e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ksf.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kw.res new file mode 100644 index 0000000..7309db4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/kw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lag.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lag.res new file mode 100644 index 0000000..4f632f2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lag.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lg.res new file mode 100644 index 0000000..466a218 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ln.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ln.res new file mode 100644 index 0000000..dbba8dc Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ln.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lo.res new file mode 100644 index 0000000..db154cd Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lt.res new file mode 100644 index 0000000..55fc82d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lu.res new file mode 100644 index 0000000..afa7edf Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/luo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/luo.res new file mode 100644 index 0000000..cc036bc Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/luo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/luy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/luy.res new file mode 100644 index 0000000..887319b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/luy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lv.res new file mode 100644 index 0000000..b38282a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/lv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mas.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mas.res new file mode 100644 index 0000000..fcae8d8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mas.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mer.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mer.res new file mode 100644 index 0000000..f644974 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mer.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mfe.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mfe.res new file mode 100644 index 0000000..cecb637 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mfe.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mg.res new file mode 100644 index 0000000..d55a6dd Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mgh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mgh.res new file mode 100644 index 0000000..872112d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mgh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mgo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mgo.res new file mode 100644 index 0000000..b3b2964 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mgo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mk.res new file mode 100644 index 0000000..ea3975e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ml.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ml.res new file mode 100644 index 0000000..b4afb76 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ml.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mo.res new file mode 100644 index 0000000..3f8911a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mr.res new file mode 100644 index 0000000..9a97914 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ms.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ms.res new file mode 100644 index 0000000..4c4a42c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ms.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mt.res new file mode 100644 index 0000000..e4b708c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mua.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mua.res new file mode 100644 index 0000000..99bd29c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/mua.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/my.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/my.res new file mode 100644 index 0000000..e2cfe70 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/my.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/naq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/naq.res new file mode 100644 index 0000000..37013b1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/naq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nb.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nb.res new file mode 100644 index 0000000..10d907e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nb.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nb_NO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nb_NO.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nb_NO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nd.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nd.res new file mode 100644 index 0000000..0567236 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nd.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ne.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ne.res new file mode 100644 index 0000000..a36e364 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ne.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nl.res new file mode 100644 index 0000000..8a2b58b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nl_BE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nl_BE.res new file mode 100644 index 0000000..17a3109 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nl_BE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nmg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nmg.res new file mode 100644 index 0000000..8af2ff1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nmg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nn.res new file mode 100644 index 0000000..7687939 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nn_NO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nn_NO.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nn_NO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/no.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/no.res new file mode 100644 index 0000000..f11a728 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/no.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/no_NO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/no_NO.res new file mode 100644 index 0000000..4c61d61 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/no_NO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/no_NO_NY.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/no_NO_NY.res new file mode 100644 index 0000000..4599f32 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/no_NO_NY.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nus.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nus.res new file mode 100644 index 0000000..a4ca08e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nus.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nyn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nyn.res new file mode 100644 index 0000000..40dd36c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/nyn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/om.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/om.res new file mode 100644 index 0000000..6bfed18 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/om.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/or.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/or.res new file mode 100644 index 0000000..c690389 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/or.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa.res new file mode 100644 index 0000000..81ac32c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Arab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Arab.res new file mode 100644 index 0000000..df819e7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Arab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Arab_PK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Arab_PK.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Arab_PK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Guru.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Guru.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Guru.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Guru_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Guru_IN.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_Guru_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_IN.res new file mode 100644 index 0000000..5a7533d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_PK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_PK.res new file mode 100644 index 0000000..a713857 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pa_PK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pl.res new file mode 100644 index 0000000..39041a0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ps.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ps.res new file mode 100644 index 0000000..22c15df Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ps.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pt.res new file mode 100644 index 0000000..7add4d8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pt_PT.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pt_PT.res new file mode 100644 index 0000000..3dbd7a2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/pt_PT.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rm.res new file mode 100644 index 0000000..c8a94b6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rn.res new file mode 100644 index 0000000..71b2166 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ro.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ro.res new file mode 100644 index 0000000..ec396f1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ro.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ro_MD.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ro_MD.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ro_MD.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rof.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rof.res new file mode 100644 index 0000000..3493c4a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rof.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/root.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/root.res new file mode 100644 index 0000000..35928c2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/root.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ru.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ru.res new file mode 100644 index 0000000..1d91b37 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ru.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ru_UA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ru_UA.res new file mode 100644 index 0000000..8ce9286 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ru_UA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rw.res new file mode 100644 index 0000000..2203f48 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rwk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rwk.res new file mode 100644 index 0000000..5ad2c1e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/rwk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/saq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/saq.res new file mode 100644 index 0000000..78c2a5d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/saq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sbp.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sbp.res new file mode 100644 index 0000000..bc4ca5a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sbp.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/seh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/seh.res new file mode 100644 index 0000000..5320dfc Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/seh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ses.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ses.res new file mode 100644 index 0000000..9168fda Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ses.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sg.res new file mode 100644 index 0000000..ccb5e64 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh.res new file mode 100644 index 0000000..0cdf3e0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh_BA.res new file mode 100644 index 0000000..ba85740 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh_CS.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh_YU.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sh_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi.res new file mode 100644 index 0000000..f95f892 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_Latn.res new file mode 100644 index 0000000..5635438 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_Latn_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_Latn_MA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_Latn_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_MA.res new file mode 100644 index 0000000..36bc365 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_Tfng.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_Tfng.res new file mode 100644 index 0000000..81f334e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/shi_Tfng.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/si.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/si.res new file mode 100644 index 0000000..3d9d5a4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/si.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sk.res new file mode 100644 index 0000000..4056620 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sl.res new file mode 100644 index 0000000..a79d68a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sn.res new file mode 100644 index 0000000..fcb6f6c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/so.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/so.res new file mode 100644 index 0000000..8784fc0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/so.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sq.res new file mode 100644 index 0000000..15f368b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr.res new file mode 100644 index 0000000..bf36080 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_BA.res new file mode 100644 index 0000000..dfc6b9c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_CS.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_BA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_CS.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_RS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_RS.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_RS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_YU.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Cyrl_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn.res new file mode 100644 index 0000000..f446b6b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_BA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_CS.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_ME.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_ME.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_ME.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_RS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_RS.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_RS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_YU.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_Latn_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_ME.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_ME.res new file mode 100644 index 0000000..aac52f2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_ME.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_RS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_RS.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_RS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_YU.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sr_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sv.res new file mode 100644 index 0000000..7b7f248 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sv_FI.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sv_FI.res new file mode 100644 index 0000000..5bebc3e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sv_FI.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sw.res new file mode 100644 index 0000000..c60c4e2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/sw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/swc.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/swc.res new file mode 100644 index 0000000..cc77572 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/swc.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ta.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ta.res new file mode 100644 index 0000000..4d7d478 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ta.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/te.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/te.res new file mode 100644 index 0000000..a1fb6e2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/te.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/teo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/teo.res new file mode 100644 index 0000000..64f28af Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/teo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/th.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/th.res new file mode 100644 index 0000000..48fc618 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/th.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/th_TH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/th_TH.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/th_TH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/th_TH_TRADITIONAL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/th_TH_TRADITIONAL.res new file mode 100644 index 0000000..4341e54 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/th_TH_TRADITIONAL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ti.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ti.res new file mode 100644 index 0000000..c581071 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ti.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tl.res new file mode 100644 index 0000000..a705804 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tl_PH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tl_PH.res new file mode 100644 index 0000000..6c39389 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tl_PH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/to.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/to.res new file mode 100644 index 0000000..d12b10b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/to.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tr.res new file mode 100644 index 0000000..3c38d5c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/twq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/twq.res new file mode 100644 index 0000000..2eaf2b1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/twq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm.res new file mode 100644 index 0000000..021387a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm_Latn.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm_Latn_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm_Latn_MA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm_Latn_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm_MA.res new file mode 100644 index 0000000..fa1287b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/tzm_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uk.res new file mode 100644 index 0000000..313a490 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ur.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ur.res new file mode 100644 index 0000000..8bdde54 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/ur.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz.res new file mode 100644 index 0000000..17e4c37 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_AF.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_AF.res new file mode 100644 index 0000000..4243259 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_AF.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Arab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Arab.res new file mode 100644 index 0000000..781305a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Arab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Arab_AF.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Arab_AF.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Arab_AF.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Cyrl.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Cyrl_UZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Cyrl_UZ.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Cyrl_UZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Latn.res new file mode 100644 index 0000000..758704a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_UZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_UZ.res new file mode 100644 index 0000000..ca4f1c3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/uz_UZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai.res new file mode 100644 index 0000000..9874607 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_LR.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_LR.res new file mode 100644 index 0000000..8d03de2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_LR.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_Latn.res new file mode 100644 index 0000000..2bd00c2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_Vaii.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_Vaii.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_Vaii.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_Vaii_LR.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_Vaii_LR.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vai_Vaii_LR.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vi.res new file mode 100644 index 0000000..3e8358c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vun.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vun.res new file mode 100644 index 0000000..3aa4953 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/vun.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/xog.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/xog.res new file mode 100644 index 0000000..9a0ab62 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/xog.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/yav.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/yav.res new file mode 100644 index 0000000..d194f95 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/yav.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/yo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/yo.res new file mode 100644 index 0000000..fd680ec Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/yo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh.res new file mode 100644 index 0000000..e47fa8d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_CN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_CN.res new file mode 100644 index 0000000..b92fec8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_CN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_HK.res new file mode 100644 index 0000000..740476e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_CN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_CN.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_CN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_HK.res new file mode 100644 index 0000000..963ffe9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_MO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_MO.res new file mode 100644 index 0000000..70fbd5f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_MO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_SG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_SG.res new file mode 100644 index 0000000..6bff462 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hans_SG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant.res new file mode 100644 index 0000000..b36baf6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant_HK.res new file mode 100644 index 0000000..e7e6f5d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant_MO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant_MO.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant_MO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant_TW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant_TW.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_Hant_TW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_MO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_MO.res new file mode 100644 index 0000000..9970807 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_MO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_SG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_SG.res new file mode 100644 index 0000000..6dc927e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_SG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_TW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_TW.res new file mode 100644 index 0000000..27020e6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zh_TW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zu.res new file mode 100644 index 0000000..3f776c5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/zu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/af.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/af.res new file mode 100644 index 0000000..f9704e2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/af.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/agq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/agq.res new file mode 100644 index 0000000..6490d4b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/agq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ak.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ak.res new file mode 100644 index 0000000..ff2e4ac Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ak.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/am.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/am.res new file mode 100644 index 0000000..1f3c436 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/am.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ar.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ar.res new file mode 100644 index 0000000..bc8f362 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ar.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/as.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/as.res new file mode 100644 index 0000000..8f00a0d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/as.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/asa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/asa.res new file mode 100644 index 0000000..fa50cb2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/asa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/az.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/az.res new file mode 100644 index 0000000..bb7ef39 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/az.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/az_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/az_Cyrl.res new file mode 100644 index 0000000..eda99c3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/az_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bas.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bas.res new file mode 100644 index 0000000..44e8b42 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bas.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/be.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/be.res new file mode 100644 index 0000000..77209a9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/be.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bem.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bem.res new file mode 100644 index 0000000..f040169 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bem.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bez.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bez.res new file mode 100644 index 0000000..9a7373e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bez.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bg.res new file mode 100644 index 0000000..7527526 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bm.res new file mode 100644 index 0000000..a7b6b8a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bn.res new file mode 100644 index 0000000..bb43f42 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bn_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bn_IN.res new file mode 100644 index 0000000..ab7a3df Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bn_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bo.res new file mode 100644 index 0000000..f55c645 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bo_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bo_IN.res new file mode 100644 index 0000000..de5460b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bo_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/br.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/br.res new file mode 100644 index 0000000..158794a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/br.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/brx.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/brx.res new file mode 100644 index 0000000..32aa98d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/brx.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bs.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bs.res new file mode 100644 index 0000000..572fee1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bs.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bs_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bs_Cyrl.res new file mode 100644 index 0000000..1104f81 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/bs_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ca.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ca.res new file mode 100644 index 0000000..b2c0d69 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ca.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/cgg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/cgg.res new file mode 100644 index 0000000..a2b84f3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/cgg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/chr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/chr.res new file mode 100644 index 0000000..16ecf3b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/chr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/cs.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/cs.res new file mode 100644 index 0000000..890dcc5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/cs.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/cy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/cy.res new file mode 100644 index 0000000..97ac09d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/cy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/da.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/da.res new file mode 100644 index 0000000..3dff8f0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/da.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dav.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dav.res new file mode 100644 index 0000000..b2c5e81 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dav.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/de.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/de.res new file mode 100644 index 0000000..8a04766 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/de.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/de_CH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/de_CH.res new file mode 100644 index 0000000..d865870 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/de_CH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dje.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dje.res new file mode 100644 index 0000000..9c21b7a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dje.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dua.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dua.res new file mode 100644 index 0000000..11cdc04 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dua.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dyo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dyo.res new file mode 100644 index 0000000..4820276 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dyo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dz.res new file mode 100644 index 0000000..d4eda3c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/dz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ebu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ebu.res new file mode 100644 index 0000000..5c57bd0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ebu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ee.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ee.res new file mode 100644 index 0000000..7b114a1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ee.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/el.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/el.res new file mode 100644 index 0000000..1d5dd4a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/el.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en.res new file mode 100644 index 0000000..c9ec298 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en_GB.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en_GB.res new file mode 100644 index 0000000..c68952a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en_GB.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/eo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/eo.res new file mode 100644 index 0000000..a3dff50 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/eo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/es.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/es.res new file mode 100644 index 0000000..30dea61 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/es.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/et.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/et.res new file mode 100644 index 0000000..262d5e7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/et.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/eu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/eu.res new file mode 100644 index 0000000..355f9ef Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/eu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ewo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ewo.res new file mode 100644 index 0000000..e2d6a1b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ewo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fa.res new file mode 100644 index 0000000..5042f79 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fa_AF.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fa_AF.res new file mode 100644 index 0000000..58c4b83 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fa_AF.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ff.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ff.res new file mode 100644 index 0000000..d236637 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ff.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fi.res new file mode 100644 index 0000000..a9b96c0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fil.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fil.res new file mode 100644 index 0000000..f7e4c53 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fil.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fo.res new file mode 100644 index 0000000..b265f78 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fr.res new file mode 100644 index 0000000..5c3c6fb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fr_CA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fr_CA.res new file mode 100644 index 0000000..0347d8d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/fr_CA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ga.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ga.res new file mode 100644 index 0000000..756654f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ga.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gl.res new file mode 100644 index 0000000..2ca40a7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gsw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gsw.res new file mode 100644 index 0000000..e0ad5f3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gsw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gu.res new file mode 100644 index 0000000..3d0b0a3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/guz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/guz.res new file mode 100644 index 0000000..4c89bc2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/guz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gv.res new file mode 100644 index 0000000..54ee28f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/gv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ha.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ha.res new file mode 100644 index 0000000..a55f626 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ha.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/haw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/haw.res new file mode 100644 index 0000000..07de4ed Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/haw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/he.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/he.res new file mode 100644 index 0000000..8094005 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/he.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hi.res new file mode 100644 index 0000000..bb8fcce Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hr.res new file mode 100644 index 0000000..b119b23 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hu.res new file mode 100644 index 0000000..d6a4ce5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hy.res new file mode 100644 index 0000000..50984e2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/hy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/id.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/id.res new file mode 100644 index 0000000..497e17a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/id.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ig.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ig.res new file mode 100644 index 0000000..8dfdb05 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ig.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ii.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ii.res new file mode 100644 index 0000000..bcd8e8f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ii.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/in.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/in.res new file mode 100644 index 0000000..497e17a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/in.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/is.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/is.res new file mode 100644 index 0000000..73dda0a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/is.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/it.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/it.res new file mode 100644 index 0000000..65ed11d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/it.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/iw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/iw.res new file mode 100644 index 0000000..8094005 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/iw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ja.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ja.res new file mode 100644 index 0000000..c05cdce Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ja.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/jgo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/jgo.res new file mode 100644 index 0000000..9265a06 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/jgo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/jmc.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/jmc.res new file mode 100644 index 0000000..3ff7ad7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/jmc.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ka.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ka.res new file mode 100644 index 0000000..e1872ba Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ka.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kab.res new file mode 100644 index 0000000..044f1fe Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kam.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kam.res new file mode 100644 index 0000000..6ddb74e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kam.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kde.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kde.res new file mode 100644 index 0000000..0a9cd06 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kde.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kea.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kea.res new file mode 100644 index 0000000..185d21c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kea.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/khq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/khq.res new file mode 100644 index 0000000..002f743 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/khq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ki.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ki.res new file mode 100644 index 0000000..f778432 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ki.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kk.res new file mode 100644 index 0000000..ad8a0d2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kl.res new file mode 100644 index 0000000..28ccc40 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kln.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kln.res new file mode 100644 index 0000000..d8296cf Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kln.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/km.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/km.res new file mode 100644 index 0000000..e6f5715 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/km.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kn.res new file mode 100644 index 0000000..320cdfb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ko.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ko.res new file mode 100644 index 0000000..8467464 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ko.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kok.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kok.res new file mode 100644 index 0000000..000760c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kok.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ks.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ks.res new file mode 100644 index 0000000..8d87d5b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ks.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ksb.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ksb.res new file mode 100644 index 0000000..613a05f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ksb.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ksf.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ksf.res new file mode 100644 index 0000000..b9b9d38 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ksf.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kw.res new file mode 100644 index 0000000..bb48c81 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/kw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lag.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lag.res new file mode 100644 index 0000000..a340aba Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lag.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lg.res new file mode 100644 index 0000000..58a8840 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ln.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ln.res new file mode 100644 index 0000000..b425a90 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ln.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lo.res new file mode 100644 index 0000000..a8c2358 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lt.res new file mode 100644 index 0000000..872b4d6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lu.res new file mode 100644 index 0000000..04694cb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/luo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/luo.res new file mode 100644 index 0000000..5fbc999 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/luo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/luy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/luy.res new file mode 100644 index 0000000..6362c11 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/luy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lv.res new file mode 100644 index 0000000..4e798d2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/lv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mas.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mas.res new file mode 100644 index 0000000..f6e5bc6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mas.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mer.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mer.res new file mode 100644 index 0000000..d9889cd Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mer.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mfe.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mfe.res new file mode 100644 index 0000000..aa69b39 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mfe.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mg.res new file mode 100644 index 0000000..9c807f1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mgh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mgh.res new file mode 100644 index 0000000..8b1fb83 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mgh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mgo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mgo.res new file mode 100644 index 0000000..d878f07 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mgo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mk.res new file mode 100644 index 0000000..4228c3a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ml.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ml.res new file mode 100644 index 0000000..9eb1a84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ml.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mr.res new file mode 100644 index 0000000..342a8fa Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ms.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ms.res new file mode 100644 index 0000000..78cc8af Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ms.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mt.res new file mode 100644 index 0000000..1954572 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mua.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mua.res new file mode 100644 index 0000000..9790215 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/mua.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/my.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/my.res new file mode 100644 index 0000000..fe9c796 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/my.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/naq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/naq.res new file mode 100644 index 0000000..dfc8298 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/naq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nb.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nb.res new file mode 100644 index 0000000..e8d6cdf Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nb.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nd.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nd.res new file mode 100644 index 0000000..0879a02 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nd.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ne.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ne.res new file mode 100644 index 0000000..b39b20d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ne.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nl.res new file mode 100644 index 0000000..9d640c0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nl_BE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nl_BE.res new file mode 100644 index 0000000..995aa58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nl_BE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nmg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nmg.res new file mode 100644 index 0000000..a3ad71f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nmg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nn.res new file mode 100644 index 0000000..5996e50 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/no.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/no.res new file mode 100644 index 0000000..e8d6cdf Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/no.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nus.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nus.res new file mode 100644 index 0000000..1a9a7a9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nus.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nyn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nyn.res new file mode 100644 index 0000000..e0cac07 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/nyn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/om.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/om.res new file mode 100644 index 0000000..d0c7f08 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/om.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/or.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/or.res new file mode 100644 index 0000000..c87c904 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/or.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pa.res new file mode 100644 index 0000000..ac2133a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pa_Arab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pa_Arab.res new file mode 100644 index 0000000..5bb69da Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pa_Arab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pl.res new file mode 100644 index 0000000..ee12ea7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ps.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ps.res new file mode 100644 index 0000000..8442b8b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ps.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pt.res new file mode 100644 index 0000000..d89bb84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pt_PT.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pt_PT.res new file mode 100644 index 0000000..4df61eb Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/pt_PT.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rm.res new file mode 100644 index 0000000..0fe2b57 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rn.res new file mode 100644 index 0000000..b78cd4c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ro.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ro.res new file mode 100644 index 0000000..1045f35 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ro.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rof.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rof.res new file mode 100644 index 0000000..e3a3a5f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rof.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ru.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ru.res new file mode 100644 index 0000000..c3d5321 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ru.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rw.res new file mode 100644 index 0000000..c22fc2b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rwk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rwk.res new file mode 100644 index 0000000..bf6c478 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/rwk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/saq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/saq.res new file mode 100644 index 0000000..fded5ce Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/saq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sbp.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sbp.res new file mode 100644 index 0000000..451955c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sbp.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/seh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/seh.res new file mode 100644 index 0000000..6ae4ddf Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/seh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ses.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ses.res new file mode 100644 index 0000000..418a99b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ses.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sg.res new file mode 100644 index 0000000..916b113 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sh.res new file mode 100644 index 0000000..44b5496 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/shi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/shi.res new file mode 100644 index 0000000..c741197 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/shi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/shi_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/shi_Latn.res new file mode 100644 index 0000000..fc850c3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/shi_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/si.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/si.res new file mode 100644 index 0000000..52679cc Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/si.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sk.res new file mode 100644 index 0000000..a2eff24 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sl.res new file mode 100644 index 0000000..3b69352 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sn.res new file mode 100644 index 0000000..f6029c2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/so.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/so.res new file mode 100644 index 0000000..b6c96f1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/so.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sq.res new file mode 100644 index 0000000..8f2367f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sr.res new file mode 100644 index 0000000..94795c0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sr_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sr_Latn.res new file mode 100644 index 0000000..44b5496 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sr_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sv.res new file mode 100644 index 0000000..a58cd5c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sw.res new file mode 100644 index 0000000..98a3b2e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/sw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/swc.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/swc.res new file mode 100644 index 0000000..f7a0d5f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/swc.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ta.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ta.res new file mode 100644 index 0000000..0f0a2d2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ta.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/te.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/te.res new file mode 100644 index 0000000..3b8ce18 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/te.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/teo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/teo.res new file mode 100644 index 0000000..42e967c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/teo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/th.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/th.res new file mode 100644 index 0000000..19042d2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/th.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ti.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ti.res new file mode 100644 index 0000000..f5543a4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ti.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/tl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/tl.res new file mode 100644 index 0000000..f7e4c53 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/tl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/to.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/to.res new file mode 100644 index 0000000..e44720e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/to.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/tr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/tr.res new file mode 100644 index 0000000..8cd954c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/tr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/twq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/twq.res new file mode 100644 index 0000000..56f1bea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/twq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/tzm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/tzm.res new file mode 100644 index 0000000..97533a2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/tzm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uk.res new file mode 100644 index 0000000..2570440 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ur.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ur.res new file mode 100644 index 0000000..8f78467 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/ur.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uz.res new file mode 100644 index 0000000..f6f19a3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uz_Arab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uz_Arab.res new file mode 100644 index 0000000..67773a6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uz_Arab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uz_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uz_Latn.res new file mode 100644 index 0000000..1ffede3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/uz_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vai.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vai.res new file mode 100644 index 0000000..7470e3f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vai.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vai_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vai_Latn.res new file mode 100644 index 0000000..360eb7f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vai_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vi.res new file mode 100644 index 0000000..6de6538 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vun.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vun.res new file mode 100644 index 0000000..189e957 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/vun.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/xog.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/xog.res new file mode 100644 index 0000000..466582b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/xog.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/yav.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/yav.res new file mode 100644 index 0000000..06aa20c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/yav.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/yo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/yo.res new file mode 100644 index 0000000..709074f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/yo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh.res new file mode 100644 index 0000000..444e185 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh_HK.res new file mode 100644 index 0000000..949ba43 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh_Hant.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh_Hant.res new file mode 100644 index 0000000..996a51e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh_Hant.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh_Hant_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh_Hant_HK.res new file mode 100644 index 0000000..949ba43 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zh_Hant_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zu.res new file mode 100644 index 0000000..89e3173 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/zu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/af.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/af.res new file mode 100644 index 0000000..a1bd7a8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/af.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/agq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/agq.res new file mode 100644 index 0000000..a10a3bc Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/agq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ak.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ak.res new file mode 100644 index 0000000..72890b8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ak.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/am.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/am.res new file mode 100644 index 0000000..e014c41 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/am.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ar.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ar.res new file mode 100644 index 0000000..29c5dc0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ar.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/as.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/as.res new file mode 100644 index 0000000..05e85a8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/as.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/asa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/asa.res new file mode 100644 index 0000000..019d78e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/asa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az.res new file mode 100644 index 0000000..336139c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_AZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_AZ.res new file mode 100644 index 0000000..ff23f22 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_AZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_Cyrl.res new file mode 100644 index 0000000..d9330b2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_Latn.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_Latn_AZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_Latn_AZ.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/az_Latn_AZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bas.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bas.res new file mode 100644 index 0000000..69785b7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bas.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/be.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/be.res new file mode 100644 index 0000000..5d2f9ad Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/be.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bem.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bem.res new file mode 100644 index 0000000..27f5ce9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bem.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bez.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bez.res new file mode 100644 index 0000000..3e9aab1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bez.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bg.res new file mode 100644 index 0000000..049de0c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bm.res new file mode 100644 index 0000000..c9dea90 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bn.res new file mode 100644 index 0000000..2614e9d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bn_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bn_IN.res new file mode 100644 index 0000000..7b2481e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bn_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bo.res new file mode 100644 index 0000000..80e27e3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bo_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bo_IN.res new file mode 100644 index 0000000..537a360 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bo_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/br.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/br.res new file mode 100644 index 0000000..967a6ab Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/br.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/brx.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/brx.res new file mode 100644 index 0000000..67f3e93 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/brx.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs.res new file mode 100644 index 0000000..90c6419 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_BA.res new file mode 100644 index 0000000..a916f07 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_Cyrl.res new file mode 100644 index 0000000..d855a81 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_Latn.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_Latn_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_Latn_BA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/bs_Latn_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ca.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ca.res new file mode 100644 index 0000000..dc9390c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ca.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/cgg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/cgg.res new file mode 100644 index 0000000..50d0fea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/cgg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/chr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/chr.res new file mode 100644 index 0000000..16323b0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/chr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/cs.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/cs.res new file mode 100644 index 0000000..f37865d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/cs.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/cy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/cy.res new file mode 100644 index 0000000..16faa81 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/cy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/da.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/da.res new file mode 100644 index 0000000..878d503 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/da.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dav.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dav.res new file mode 100644 index 0000000..7bcb198 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dav.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/de.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/de.res new file mode 100644 index 0000000..aa103b1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/de.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/de_CH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/de_CH.res new file mode 100644 index 0000000..abd9d58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/de_CH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dje.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dje.res new file mode 100644 index 0000000..6f75ce7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dje.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dua.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dua.res new file mode 100644 index 0000000..9eb2d7e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dua.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dyo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dyo.res new file mode 100644 index 0000000..49e55c2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dyo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dz.res new file mode 100644 index 0000000..0a0111e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/dz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ebu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ebu.res new file mode 100644 index 0000000..a3a7013 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ebu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ee.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ee.res new file mode 100644 index 0000000..6252227 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ee.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/el.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/el.res new file mode 100644 index 0000000..c5c0ae1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/el.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en.res new file mode 100644 index 0000000..41d426e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_GB.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_GB.res new file mode 100644 index 0000000..4195444 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_GB.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_NH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_NH.res new file mode 100644 index 0000000..1088544 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_NH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_RH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_RH.res new file mode 100644 index 0000000..df1b860 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_RH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_VU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_VU.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_VU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_ZW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_ZW.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en_ZW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/eo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/eo.res new file mode 100644 index 0000000..b7049ba Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/eo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/es.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/es.res new file mode 100644 index 0000000..11bd594 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/es.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/es_CL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/es_CL.res new file mode 100644 index 0000000..6e450ac Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/es_CL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/et.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/et.res new file mode 100644 index 0000000..5568a20 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/et.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/eu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/eu.res new file mode 100644 index 0000000..f815a03 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/eu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ewo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ewo.res new file mode 100644 index 0000000..8d56bdf Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ewo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fa.res new file mode 100644 index 0000000..f37f4c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fa_AF.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fa_AF.res new file mode 100644 index 0000000..49d6d84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fa_AF.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ff.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ff.res new file mode 100644 index 0000000..cf59172 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ff.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fi.res new file mode 100644 index 0000000..fd670e3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fil.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fil.res new file mode 100644 index 0000000..fbe37ab Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fil.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fil_PH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fil_PH.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fil_PH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fo.res new file mode 100644 index 0000000..fa5074f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fr.res new file mode 100644 index 0000000..a4bd3a1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fr_CA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fr_CA.res new file mode 100644 index 0000000..814eb58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/fr_CA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ga.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ga.res new file mode 100644 index 0000000..72d8aab Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ga.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gl.res new file mode 100644 index 0000000..f8d896c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gsw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gsw.res new file mode 100644 index 0000000..c85c585 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gsw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gu.res new file mode 100644 index 0000000..aacbec5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/guz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/guz.res new file mode 100644 index 0000000..a3a7013 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/guz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gv.res new file mode 100644 index 0000000..1862d51 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/gv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha.res new file mode 100644 index 0000000..261aa85 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_GH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_GH.res new file mode 100644 index 0000000..1edc596 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_GH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn_GH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn_GH.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn_GH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn_NE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn_NE.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn_NE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn_NG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn_NG.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_Latn_NG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_NE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_NE.res new file mode 100644 index 0000000..46a9888 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_NE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_NG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_NG.res new file mode 100644 index 0000000..954922a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ha_NG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/haw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/haw.res new file mode 100644 index 0000000..ec6f762 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/haw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/he.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/he.res new file mode 100644 index 0000000..77ce11d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/he.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/he_IL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/he_IL.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/he_IL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hi.res new file mode 100644 index 0000000..047a02a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hr.res new file mode 100644 index 0000000..5189462 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hu.res new file mode 100644 index 0000000..67a970a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hy.res new file mode 100644 index 0000000..573f341 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/hy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/id.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/id.res new file mode 100644 index 0000000..07fe433 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/id.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/id_ID.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/id_ID.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/id_ID.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ig.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ig.res new file mode 100644 index 0000000..63f742e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ig.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ii.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ii.res new file mode 100644 index 0000000..e3f0e7e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ii.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/in.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/in.res new file mode 100644 index 0000000..6a5ac33 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/in.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/in_ID.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/in_ID.res new file mode 100644 index 0000000..dcdcac2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/in_ID.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/is.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/is.res new file mode 100644 index 0000000..c98535e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/is.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/it.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/it.res new file mode 100644 index 0000000..7faa600 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/it.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/iw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/iw.res new file mode 100644 index 0000000..e64e1b3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/iw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/iw_IL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/iw_IL.res new file mode 100644 index 0000000..e586d07 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/iw_IL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ja.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ja.res new file mode 100644 index 0000000..97c3ed8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ja.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ja_JP.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ja_JP.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ja_JP.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ja_JP_TRADITIONAL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ja_JP_TRADITIONAL.res new file mode 100644 index 0000000..4b6ec41 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ja_JP_TRADITIONAL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/jgo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/jgo.res new file mode 100644 index 0000000..26ffb6b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/jgo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/jmc.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/jmc.res new file mode 100644 index 0000000..a3a7013 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/jmc.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ka.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ka.res new file mode 100644 index 0000000..07a807d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ka.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kab.res new file mode 100644 index 0000000..4a632f8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kam.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kam.res new file mode 100644 index 0000000..668ced5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kam.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kde.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kde.res new file mode 100644 index 0000000..387c176 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kde.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kea.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kea.res new file mode 100644 index 0000000..d63b0c3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kea.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/khq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/khq.res new file mode 100644 index 0000000..313b02f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/khq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ki.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ki.res new file mode 100644 index 0000000..98d2b42 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ki.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk.res new file mode 100644 index 0000000..a45a66e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk_Cyrl.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk_Cyrl_KZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk_Cyrl_KZ.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk_Cyrl_KZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk_KZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk_KZ.res new file mode 100644 index 0000000..6cfb7f0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kk_KZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kl.res new file mode 100644 index 0000000..6c637ed Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kln.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kln.res new file mode 100644 index 0000000..8821cf6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kln.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/km.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/km.res new file mode 100644 index 0000000..98bc3b0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/km.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kn.res new file mode 100644 index 0000000..6e30607 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ko.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ko.res new file mode 100644 index 0000000..97c3eab Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ko.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kok.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kok.res new file mode 100644 index 0000000..004468e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kok.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks.res new file mode 100644 index 0000000..b56462d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks_Arab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks_Arab.res new file mode 100644 index 0000000..dd4ea19 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks_Arab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks_Arab_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks_Arab_IN.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks_Arab_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks_IN.res new file mode 100644 index 0000000..bd13a11 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ks_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ksb.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ksb.res new file mode 100644 index 0000000..ac16733 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ksb.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ksf.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ksf.res new file mode 100644 index 0000000..56db0a1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ksf.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kw.res new file mode 100644 index 0000000..be7b5ae Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/kw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lag.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lag.res new file mode 100644 index 0000000..343677e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lag.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lg.res new file mode 100644 index 0000000..5c75764 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ln.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ln.res new file mode 100644 index 0000000..52920a6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ln.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lo.res new file mode 100644 index 0000000..31f6fce Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lt.res new file mode 100644 index 0000000..bccc928 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lu.res new file mode 100644 index 0000000..1aec5f3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/luo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/luo.res new file mode 100644 index 0000000..3df96f9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/luo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/luy.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/luy.res new file mode 100644 index 0000000..4dd4342 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/luy.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lv.res new file mode 100644 index 0000000..b852baf Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/lv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mas.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mas.res new file mode 100644 index 0000000..ad96e56 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mas.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mer.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mer.res new file mode 100644 index 0000000..d79eb29 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mer.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mfe.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mfe.res new file mode 100644 index 0000000..c734bca Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mfe.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mg.res new file mode 100644 index 0000000..1bff846 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mgh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mgh.res new file mode 100644 index 0000000..592d2b7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mgh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mgo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mgo.res new file mode 100644 index 0000000..fa572df Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mgo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mk.res new file mode 100644 index 0000000..8a994d4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ml.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ml.res new file mode 100644 index 0000000..2c2abb7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ml.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mo.res new file mode 100644 index 0000000..3f8911a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mr.res new file mode 100644 index 0000000..8d07ec3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ms.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ms.res new file mode 100644 index 0000000..596ef12 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ms.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mt.res new file mode 100644 index 0000000..f57c128 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mua.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mua.res new file mode 100644 index 0000000..e72a05b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/mua.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/my.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/my.res new file mode 100644 index 0000000..f5a0c23 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/my.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/naq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/naq.res new file mode 100644 index 0000000..383dcc1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/naq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nb.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nb.res new file mode 100644 index 0000000..4dff7d1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nb.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nb_NO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nb_NO.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nb_NO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nd.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nd.res new file mode 100644 index 0000000..696d7e3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nd.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ne.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ne.res new file mode 100644 index 0000000..29cfd04 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ne.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nl.res new file mode 100644 index 0000000..995d5a5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nl_BE.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nl_BE.res new file mode 100644 index 0000000..5af4992 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nl_BE.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nmg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nmg.res new file mode 100644 index 0000000..ece20a9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nmg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nn.res new file mode 100644 index 0000000..16446e0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nn_NO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nn_NO.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nn_NO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/no.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/no.res new file mode 100644 index 0000000..f11a728 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/no.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/no_NO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/no_NO.res new file mode 100644 index 0000000..4c61d61 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/no_NO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/no_NO_NY.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/no_NO_NY.res new file mode 100644 index 0000000..4599f32 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/no_NO_NY.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nus.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nus.res new file mode 100644 index 0000000..d1faa5e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nus.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nyn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nyn.res new file mode 100644 index 0000000..b0aee4f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/nyn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/om.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/om.res new file mode 100644 index 0000000..92f2853 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/om.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/or.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/or.res new file mode 100644 index 0000000..3aac078 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/or.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa.res new file mode 100644 index 0000000..0cacd5e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Arab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Arab.res new file mode 100644 index 0000000..154912d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Arab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Arab_PK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Arab_PK.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Arab_PK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Guru.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Guru.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Guru.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Guru_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Guru_IN.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_Guru_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_IN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_IN.res new file mode 100644 index 0000000..5a7533d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_IN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_PK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_PK.res new file mode 100644 index 0000000..a713857 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pa_PK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pl.res new file mode 100644 index 0000000..3f1cfe1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ps.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ps.res new file mode 100644 index 0000000..fcd8b21 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ps.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pt.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pt.res new file mode 100644 index 0000000..5be0eb4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pt.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pt_PT.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pt_PT.res new file mode 100644 index 0000000..b101ee7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/pt_PT.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rm.res new file mode 100644 index 0000000..27db8a1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rn.res new file mode 100644 index 0000000..071ab75 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ro.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ro.res new file mode 100644 index 0000000..a42364c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ro.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ro_MD.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ro_MD.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ro_MD.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rof.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rof.res new file mode 100644 index 0000000..4d0c2be Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rof.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/root.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/root.res new file mode 100644 index 0000000..62fe0cf Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/root.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ru.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ru.res new file mode 100644 index 0000000..7f03c8f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ru.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rw.res new file mode 100644 index 0000000..3f88aa8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rwk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rwk.res new file mode 100644 index 0000000..a3a7013 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/rwk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/saq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/saq.res new file mode 100644 index 0000000..a3a7013 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/saq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sbp.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sbp.res new file mode 100644 index 0000000..fcea764 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sbp.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/seh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/seh.res new file mode 100644 index 0000000..1c47ae1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/seh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ses.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ses.res new file mode 100644 index 0000000..313b02f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ses.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sg.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sg.res new file mode 100644 index 0000000..e43b106 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sg.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh.res new file mode 100644 index 0000000..0cdf3e0 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh_BA.res new file mode 100644 index 0000000..ba85740 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh_CS.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh_YU.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sh_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi.res new file mode 100644 index 0000000..f35428b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_Latn.res new file mode 100644 index 0000000..284cc65 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_Latn_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_Latn_MA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_Latn_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_MA.res new file mode 100644 index 0000000..36bc365 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_Tfng.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_Tfng.res new file mode 100644 index 0000000..81f334e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/shi_Tfng.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/si.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/si.res new file mode 100644 index 0000000..a1ee3d8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/si.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sk.res new file mode 100644 index 0000000..08d156a Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sl.res new file mode 100644 index 0000000..5f859bf Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sn.res new file mode 100644 index 0000000..dcf710e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/so.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/so.res new file mode 100644 index 0000000..eafd99f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/so.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sq.res new file mode 100644 index 0000000..4d58a23 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr.res new file mode 100644 index 0000000..b195cb6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_BA.res new file mode 100644 index 0000000..dfc6b9c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_CS.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_BA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_CS.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_RS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_RS.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_RS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_YU.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Cyrl_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn.res new file mode 100644 index 0000000..4b56c18 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_BA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_BA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_BA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_CS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_CS.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_CS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_ME.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_ME.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_ME.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_RS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_RS.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_RS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_YU.res new file mode 100644 index 0000000..0b452c9 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_Latn_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_ME.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_ME.res new file mode 100644 index 0000000..aac52f2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_ME.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_RS.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_RS.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_RS.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_YU.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_YU.res new file mode 100644 index 0000000..b926e84 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sr_YU.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sv.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sv.res new file mode 100644 index 0000000..ed9e54e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sv.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sv_FI.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sv_FI.res new file mode 100644 index 0000000..ee7f85d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sv_FI.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sw.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sw.res new file mode 100644 index 0000000..73d4e98 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/sw.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/swc.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/swc.res new file mode 100644 index 0000000..a3a7013 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/swc.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ta.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ta.res new file mode 100644 index 0000000..2561e82 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ta.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/te.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/te.res new file mode 100644 index 0000000..503b705 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/te.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/teo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/teo.res new file mode 100644 index 0000000..ab87b66 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/teo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/th.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/th.res new file mode 100644 index 0000000..9f322a5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/th.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/th_TH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/th_TH.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/th_TH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/th_TH_TRADITIONAL.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/th_TH_TRADITIONAL.res new file mode 100644 index 0000000..4341e54 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/th_TH_TRADITIONAL.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ti.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ti.res new file mode 100644 index 0000000..f359575 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ti.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tl.res new file mode 100644 index 0000000..a705804 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tl_PH.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tl_PH.res new file mode 100644 index 0000000..6c39389 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tl_PH.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/to.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/to.res new file mode 100644 index 0000000..087c55e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/to.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tr.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tr.res new file mode 100644 index 0000000..5e39742 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tr.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/twq.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/twq.res new file mode 100644 index 0000000..dd789b4 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/twq.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm.res new file mode 100644 index 0000000..a312150 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm_Latn.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm_Latn_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm_Latn_MA.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm_Latn_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm_MA.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm_MA.res new file mode 100644 index 0000000..fa1287b Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/tzm_MA.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uk.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uk.res new file mode 100644 index 0000000..5408c5d Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uk.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ur.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ur.res new file mode 100644 index 0000000..aab576c Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/ur.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz.res new file mode 100644 index 0000000..e2b0a42 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_AF.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_AF.res new file mode 100644 index 0000000..4243259 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_AF.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Arab.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Arab.res new file mode 100644 index 0000000..b9c0d9f Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Arab.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Arab_AF.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Arab_AF.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Arab_AF.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Cyrl.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Cyrl.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Cyrl.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Cyrl_UZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Cyrl_UZ.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Cyrl_UZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Latn.res new file mode 100644 index 0000000..1dfe487 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_UZ.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_UZ.res new file mode 100644 index 0000000..ca4f1c3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/uz_UZ.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai.res new file mode 100644 index 0000000..0f042d3 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_LR.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_LR.res new file mode 100644 index 0000000..8d03de2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_LR.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_Latn.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_Latn.res new file mode 100644 index 0000000..cfcbc76 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_Latn.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_Vaii.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_Vaii.res new file mode 100644 index 0000000..dae80e5 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_Vaii.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_Vaii_LR.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_Vaii_LR.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vai_Vaii_LR.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vi.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vi.res new file mode 100644 index 0000000..12f8c68 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vi.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vun.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vun.res new file mode 100644 index 0000000..7bcb198 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/vun.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/xog.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/xog.res new file mode 100644 index 0000000..3a9c8b2 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/xog.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/yav.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/yav.res new file mode 100644 index 0000000..8e4a828 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/yav.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/yo.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/yo.res new file mode 100644 index 0000000..98097f1 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/yo.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh.res new file mode 100644 index 0000000..5984925 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_CN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_CN.res new file mode 100644 index 0000000..b92fec8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_CN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_HK.res new file mode 100644 index 0000000..740476e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans.res new file mode 100644 index 0000000..5ea9f58 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_CN.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_CN.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_CN.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_HK.res new file mode 100644 index 0000000..7f25a25 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_MO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_MO.res new file mode 100644 index 0000000..54be3b8 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_MO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_SG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_SG.res new file mode 100644 index 0000000..28de3b7 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hans_SG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant.res new file mode 100644 index 0000000..04f6abe Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant_HK.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant_HK.res new file mode 100644 index 0000000..a861a43 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant_HK.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant_MO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant_MO.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant_MO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant_TW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant_TW.res new file mode 100644 index 0000000..81ba7ea Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_Hant_TW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_MO.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_MO.res new file mode 100644 index 0000000..9970807 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_MO.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_SG.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_SG.res new file mode 100644 index 0000000..6dc927e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_SG.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_TW.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_TW.res new file mode 100644 index 0000000..27020e6 Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zh_TW.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zu.res b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zu.res new file mode 100644 index 0000000..8b7ba0e Binary files /dev/null and b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/zu.res differ diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/svn-info.txt b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/svn-info.txt new file mode 100644 index 0000000..c37f332 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/svn-info.txt @@ -0,0 +1,7 @@ +SVN information +=============== + +URL: http://source.icu-project.org/repos/icu/icu/tags/release-50-1-2/source +Revision: 33035 +Author: srl +Date: 2013-01-11T00:13:29.678212Z diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/version.txt b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/version.txt new file mode 100644 index 0000000..da31678 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Resources/data/version.txt @@ -0,0 +1 @@ +50.1.2 diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuCurrencyBundleTest.php b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuCurrencyBundleTest.php new file mode 100644 index 0000000..7965c1f --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuCurrencyBundleTest.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\Icu\Tests; + +use Symfony\Component\Icu\IcuCurrencyBundle; +use Symfony\Component\Icu\IcuData; + +/** + * @author Bernhard Schussek + */ +class IcuCurrencyBundleTest extends IcuTestCase +{ + /** + * @var string + */ + private $resDir; + + /** + * @var IcuCurrencyBundle + */ + private $bundle; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $reader; + + protected function setUp() + { + parent::setUp(); + + $this->resDir = IcuData::getResourceDirectory() . '/curr'; + $this->reader = $this->getMock('Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface'); + $this->bundle = new IcuCurrencyBundle($this->reader); + } + + public function testGetCurrencySymbol() + { + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'en', array('Currencies', 'EUR', 0)) + ->will($this->returnValue('€')); + + $this->assertSame('€', $this->bundle->getCurrencySymbol('EUR', 'en')); + } + + public function testGetCurrencyName() + { + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'en', array('Currencies', 'EUR', 1)) + ->will($this->returnValue('Euro')); + + $this->assertSame('Euro', $this->bundle->getCurrencyName('EUR', 'en')); + } + + public function testGetCurrencyNames() + { + $currencies = array( + 'EUR' => array(1 => 'Euro'), + 'USD' => array(1 => 'Dollar'), + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'en', array('Currencies')) + ->will($this->returnValue($currencies)); + + $sortedCurrencies = array( + 'USD' => 'Dollar', + 'EUR' => 'Euro', + ); + + $this->assertSame($sortedCurrencies, $this->bundle->getCurrencyNames('en')); + } + + public function testGetFractionDigits() + { + $currencyData = array( + 'EUR' => array(0 => 123), + 'USD' => array(0 => 456), + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'supplementaldata', array('CurrencyMeta')) + ->will($this->returnValue($currencyData)); + + $this->assertSame(123, $this->bundle->getFractionDigits('EUR')); + } + + public function testGetFractionDigitsFromDefaultBlock() + { + $currencyData = array( + 'USD' => array(0 => 456), + 'DEFAULT' => array(0 => 123), + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'supplementaldata', array('CurrencyMeta')) + ->will($this->returnValue($currencyData)); + + $this->assertSame(123, $this->bundle->getFractionDigits('EUR')); + } + + public function testGetRoundingIncrement() + { + $currencyData = array( + 'EUR' => array(1 => 123), + 'USD' => array(1 => 456), + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'supplementaldata', array('CurrencyMeta')) + ->will($this->returnValue($currencyData)); + + $this->assertSame(123, $this->bundle->getRoundingIncrement('EUR')); + } + + public function testGetRoundingIncrementFromDefaultBlock() + { + $currencyData = array( + 'USD' => array(1 => 456), + 'DEFAULT' => array(1 => 123), + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'supplementaldata', array('CurrencyMeta')) + ->will($this->returnValue($currencyData)); + + $this->assertSame(123, $this->bundle->getRoundingIncrement('EUR')); + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuLanguageBundleTest.php b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuLanguageBundleTest.php new file mode 100644 index 0000000..a1459d8 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuLanguageBundleTest.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\Icu\Tests; + +use Symfony\Component\Icu\IcuData; +use Symfony\Component\Icu\IcuLanguageBundle; + +/** + * @author Bernhard Schussek + */ +class IcuLanguageBundleTest extends IcuTestCase +{ + /** + * @var string + */ + private $resDir; + + /** + * @var IcuLanguageBundle + */ + private $bundle; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $reader; + + protected function setUp() + { + parent::setUp(); + + $this->resDir = IcuData::getResourceDirectory() . '/lang'; + $this->reader = $this->getMock('Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface'); + $this->bundle = new IcuLanguageBundle($this->reader); + } + + public function testGetLanguageName() + { + $languages = array( + 'de' => 'German', + 'en' => 'English', + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'en', array('Languages')) + ->will($this->returnValue($languages)); + + $this->assertSame('German', $this->bundle->getLanguageName('de', 'en')); + } + + public function testGetLanguageNameWithRegion() + { + $languages = array( + 'de' => 'German', + 'en' => 'English', + 'en_GB' => 'British English', + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'en', array('Languages')) + ->will($this->returnValue($languages)); + + $this->assertSame('British English', $this->bundle->getLanguageName('en', 'GB', 'en')); + } + + public function testGetLanguageNameWithUntranslatedRegion() + { + $languages = array( + 'de' => 'German', + 'en' => 'English', + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'en', array('Languages')) + ->will($this->returnValue($languages)); + + $this->assertSame('English', $this->bundle->getLanguageName('en', 'US', 'en')); + } + + public function testGetLanguageNameForMultipleLanguages() + { + $this->reader->expects($this->never()) + ->method('readEntry'); + + $this->assertNull($this->bundle->getLanguageName('mul', 'en')); + } + + public function testGetLanguageNames() + { + $languages = array( + 'mul' => 'Multiple Languages', + 'de' => 'German', + 'en' => 'English', + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'en', array('Languages')) + ->will($this->returnValue($languages)); + + $sortedLanguages = array( + 'en' => 'English', + 'de' => 'German', + ); + + $this->assertSame($sortedLanguages, $this->bundle->getLanguageNames('en')); + } + + public function testGetScriptNames() + { + $scripts = array( + 'Latn' => 'latin', + 'Cyrl' => 'cyrillique', + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'en', array('Scripts')) + ->will($this->returnValue($scripts)); + + $sortedScripts = array( + 'Cyrl' => 'cyrillique', + 'Latn' => 'latin', + ); + + $this->assertSame($sortedScripts, $this->bundle->getScriptNames('en')); + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuLocaleBundleTest.php b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuLocaleBundleTest.php new file mode 100644 index 0000000..0dfbf0d --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuLocaleBundleTest.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\Icu\Tests; + +use Symfony\Component\Icu\IcuData; +use Symfony\Component\Icu\IcuLocaleBundle; + +/** + * @author Bernhard Schussek + */ +class IcuLocaleBundleTest extends IcuTestCase +{ + /** + * @var string + */ + private $resDir; + + /** + * @var IcuLocaleBundle + */ + private $bundle; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $reader; + + protected function setUp() + { + parent::setUp(); + + $this->resDir = IcuData::getResourceDirectory() . '/locales'; + $this->reader = $this->getMock('Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface'); + $this->bundle = new IcuLocaleBundle($this->reader); + } + + public function testGetLocaleNames() + { + $locales = array( + 'en_GB' => 'English (United Kingdom)', + 'en_IE' => 'English (Ireland)', + 'en_US' => 'English (United States)', + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'en', array('Locales')) + ->will($this->returnValue($locales)); + + $sortedLocales = array( + 'en_IE' => 'English (Ireland)', + 'en_GB' => 'English (United Kingdom)', + 'en_US' => 'English (United States)', + ); + + $this->assertSame($sortedLocales, $this->bundle->getLocaleNames('en')); + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuRegionBundleTest.php b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuRegionBundleTest.php new file mode 100644 index 0000000..446d02e --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuRegionBundleTest.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\Icu\Tests; + +use Symfony\Component\Icu\IcuData; +use Symfony\Component\Icu\IcuRegionBundle; + +/** + * @author Bernhard Schussek + */ +class IcuRegionBundleTest extends IcuTestCase +{ + /** + * @var string + */ + private $resDir; + + /** + * @var IcuRegionBundle + */ + private $bundle; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $reader; + + protected function setUp() + { + parent::setUp(); + + $this->resDir = IcuData::getResourceDirectory() . '/region'; + $this->reader = $this->getMock('Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface'); + $this->bundle = new IcuRegionBundle($this->reader); + } + + public function testGetCountryNameOfUnknownCountry() + { + $this->reader->expects($this->never()) + ->method('readEntry'); + + $this->assertNull($this->bundle->getCountryName('ZZ', 'en')); + } + + public function testGetCountryNameOfNumericalRegion() + { + $this->reader->expects($this->never()) + ->method('readEntry'); + + $this->assertNull($this->bundle->getCountryName(123, 'en')); + } + + public function testGetCountryNameOfNumericalRegionWithLeadingZero() + { + $this->reader->expects($this->never()) + ->method('readEntry'); + + $this->assertNull($this->bundle->getCountryName('010', 'en')); + } + + public function testGetCountryNames() + { + $countries = array( + 'DE' => 'Germany', + 'AT' => 'Austria', + 'ZZ' => 'Unknown Country', + '010' => 'Europe', + 110 => 'America', + ); + + $this->reader->expects($this->once()) + ->method('readEntry') + ->with($this->resDir, 'en', array('Countries')) + ->will($this->returnValue($countries)); + + $sortedCountries = array( + 'AT' => 'Austria', + 'DE' => 'Germany', + ); + + $this->assertSame($sortedCountries, $this->bundle->getCountryNames('en')); + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuTestCase.php b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuTestCase.php new file mode 100644 index 0000000..ab8f6b2 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuTestCase.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\Icu\Tests; + +use Symfony\Component\Icu\IcuData; +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\Util\IcuVersion; +use Symfony\Component\Intl\Util\Version; + +/** + * Base test case for the Icu component. + * + * @author Bernhard Schussek + */ +abstract class IcuTestCase extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!Intl::isExtensionLoaded()) { + $this->markTestSkipped('The intl extension is not available.'); + } + } +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/composer.json b/vendor/symfony/icu/Symfony/Component/Icu/composer.json new file mode 100644 index 0000000..8d24191 --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/composer.json @@ -0,0 +1,28 @@ +{ + "name": "symfony/icu", + "type": "library", + "description": "Contains an excerpt of the ICU data and classes to load it.", + "keywords": ["icu", "intl"], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "lib-ICU": ">=4.4", + "symfony/intl": ">=2.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Icu\\": "" } + }, + "target-dir": "Symfony/Component/Icu", + "minimum-stability": "dev" +} diff --git a/vendor/symfony/icu/Symfony/Component/Icu/phpunit.xml.dist b/vendor/symfony/icu/Symfony/Component/Icu/phpunit.xml.dist new file mode 100644 index 0000000..acfe4bd --- /dev/null +++ b/vendor/symfony/icu/Symfony/Component/Icu/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/DependencyInjection/Compiler/AddProcessorsPass.php b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/DependencyInjection/Compiler/AddProcessorsPass.php new file mode 100644 index 0000000..21b51a9 --- /dev/null +++ b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/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/Symfony/Bundle/MonologBundle/DependencyInjection/Compiler/DebugHandlerPass.php b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/DependencyInjection/Compiler/DebugHandlerPass.php new file mode 100644 index 0000000..8ada6eb --- /dev/null +++ b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/DependencyInjection/Compiler/DebugHandlerPass.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\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. + * + * @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; + } + + // detect if the profiler is present but will be disabled + $enabled = true; + foreach ($container->getDefinition('profiler')->getMethodCalls() as $call) { + if ($call[0] === 'disable') { + $enabled = false; + } elseif ($call[0] === 'enable') { + $enabled = true; + } + } + if (!$enabled) { + 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/Symfony/Bundle/MonologBundle/DependencyInjection/Compiler/LoggerChannelPass.php b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/DependencyInjection/Compiler/LoggerChannelPass.php new file mode 100644 index 0000000..030a87f --- /dev/null +++ b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/DependencyInjection/Compiler/LoggerChannelPass.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\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; + } + + foreach ($container->findTaggedServiceIds('monolog.logger') as $id => $tags) { + foreach ($tags as $tag) { + if (empty($tag['channel']) || 'app' === $tag['channel']) { + continue; + } + + $definition = $container->getDefinition($id); + $loggerId = sprintf('monolog.logger.%s', $tag['channel']); + $this->createLogger($tag['channel'], $loggerId, $container); + + foreach ($definition->getArguments() as $index => $argument) { + if ($argument instanceof Reference && 'logger' === (string) $argument) { + $definition->replaceArgument($index, new Reference($loggerId, $argument->getInvalidBehavior(), $argument->isStrict())); + } + } + + $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] = new Reference($loggerId, $argument->getInvalidBehavior(), $argument->isStrict()); + } + } + } + $definition->setMethodCalls($calls); + } + } + + $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']; + } + + 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; + } + } +} diff --git a/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/DependencyInjection/Configuration.php b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..d1cda06 --- /dev/null +++ b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/DependencyInjection/Configuration.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\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. + * + * @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('handler') + ->children() + ->arrayNode('handlers') + ->canBeUnset() + ->useAttributeAsKey('name') + ->prototype('array') + ->fixXmlConfig('member') + ->canBeUnset() + ->children() + ->scalarNode('type') + ->isRequired() + ->treatNullLike('null') + ->beforeNormalization() + ->always() + ->then(function($v) { return strtolower($v); }) + ->end() + ->end() + ->scalarNode('id')->end() + ->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('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('buffer_size')->defaultValue(0)->end() // fingers_crossed and buffer + ->scalarNode('handler')->end() // fingers_crossed and buffer + ->scalarNode('token')->end() // pushover + ->scalarNode('user')->end() // pushover + ->scalarNode('title')->defaultNull()->end() // pushover + ->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('members') // group + ->canBeUnset() + ->performNoDeepMerging() + ->prototype('scalar')->end() + ->end() + ->scalarNode('from_email')->end() // swift_mailer and native_mailer + ->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() + ->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 + ->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; + } + } + + 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 ('fingers_crossed' === $v['type'] || 'buffer' === $v['type']) && 1 !== count($v['handler']); }) + ->thenInvalid('The handler has to be specified to use a FingersCrossedHandler or BufferHandler') + ->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 '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'] && !isset($v['dsn']); }) + ->thenInvalid('The DSN has to be specified to use a RavenHandler') + ->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', + 'processors' => array('some_callable') + ), + '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/Symfony/Bundle/MonologBundle/DependencyInjection/MonologExtension.php b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/DependencyInjection/MonologExtension.php new file mode 100644 index 0000000..5c1a50f --- /dev/null +++ b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/DependencyInjection/MonologExtension.php @@ -0,0 +1,340 @@ + + * + * 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\Config\Definition\Exception\InvalidConfigurationException; + +/** + * MonologExtension is an extension for the Monolog library. + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +class MonologExtension extends Extension +{ + private $nestedHandlers = array(); + + /** + * 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' => isset($handler['channels']) ? $handler['channels'] : null + ); + } + + 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\\TestHandler', + 'Monolog\\Logger', + 'Symfony\\Bridge\\Monolog\\Logger', + 'Symfony\\Bridge\\Monolog\\Handler\\DebugHandler', + 'Monolog\\Handler\\FingersCrossed\\ActivationStrategyInterface', + 'Monolog\\Handler\\FingersCrossed\\ErrorLevelActivationStrategy', + )); + } + } + + /** + * 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'] = is_int($handler['level']) ? $handler['level'] : constant('Monolog\Logger::'.strtoupper($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'], + )); + 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']; + } else { + $publisher = new Definition("%monolog.gelf.publisher.class%", array( + $handler['publisher']['hostname'], + $handler['publisher']['port'], + $handler['publisher']['chunk_size'], + )); + + $publisherId = 'monolog.gelf.publisher'; + $publisher->setPublic(false); + $container->setDefinition($publisherId, $publisher); + } + + $definition->setArguments(array( + new Reference($publisherId), + $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'], + )); + break; + + case 'fingers_crossed': + $handler['action_level'] = is_int($handler['action_level']) ? $handler['action_level'] : constant('Monolog\Logger::'.strtoupper($handler['action_level'])); + $nestedHandlerId = $this->getHandlerId($handler['handler']); + $this->nestedHandlers[] = $nestedHandlerId; + + if (isset($handler['activation_strategy'])) { + $activation = new Reference($handler['activation_strategy']); + } else { + $activation = $handler['action_level']; + } + + $definition->setArguments(array( + new Reference($nestedHandlerId), + $activation, + $handler['buffer_size'], + $handler['bubble'], + $handler['stop_buffering'], + )); + break; + + case 'buffer': + $nestedHandlerId = $this->getHandlerId($handler['handler']); + $this->nestedHandlers[] = $nestedHandlerId; + + $definition->setArguments(array( + new Reference($nestedHandlerId), + $handler['buffer_size'], + $handler['level'], + $handler['bubble'], + )); + break; + + case 'group': + $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 'swift_mailer': + 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 { + $message = new Definition('Swift_Message'); + $message->setFactoryService('mailer'); + $message->setFactoryMethod('createMessage'); + $message->setPublic(false); + $message->addMethodCall('setFrom', array($handler['from_email'])); + $message->addMethodCall('setTo', array($handler['to_email'])); + $message->addMethodCall('setSubject', array($handler['subject'])); + + if (isset($handler['content_type'])) { + $message->addMethodCall('setContentType', array($handler['content_type'])); + } + + $messageId = sprintf('%s.mail_prototype', $handlerId); + $container->setDefinition($messageId, $message); + $prototype = new Reference($messageId); + } + $definition->setArguments(array( + new Reference($handler['mailer']), + $prototype, + $handler['level'], + $handler['bubble'], + )); + 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 'raven': + $clientId = 'monolog.raven.client.' . sha1($handler['dsn']); + if (!$container->hasDefinition($clientId)) { + $client = new Definition("Raven_Client", array( + $handler['dsn'] + )); + $client->setPublic(false); + $container->setDefinition($clientId, $client); + } + $definition->setArguments(array( + new Reference($clientId), + $handler['level'], + $handler['bubble'], + )); + break; + + // Handlers using the constructor of AbstractHandler without adding their own arguments + 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/Symfony/Bundle/MonologBundle/LICENSE b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/LICENSE new file mode 100644 index 0000000..cdffe7a --- /dev/null +++ b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/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/Symfony/Bundle/MonologBundle/MonologBundle.php b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/MonologBundle.php new file mode 100644 index 0000000..482f135 --- /dev/null +++ b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/MonologBundle.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\MonologBundle; + +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()); + } +} diff --git a/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/Resources/config/monolog.xml b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/Resources/config/monolog.xml new file mode 100644 index 0000000..ff81955 --- /dev/null +++ b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/Resources/config/monolog.xml @@ -0,0 +1,42 @@ + + + + + + Symfony\Bridge\Monolog\Logger + Gelf\MessagePublisher + Monolog\Handler\StreamHandler + Monolog\Handler\GroupHandler + Monolog\Handler\BufferHandler + Monolog\Handler\RotatingFileHandler + Monolog\Handler\SyslogHandler + Monolog\Handler\NullHandler + Monolog\Handler\TestHandler + Monolog\Handler\GelfHandler + Symfony\Bridge\Monolog\Handler\FirePHPHandler + Symfony\Bridge\Monolog\Handler\ChromePhpHandler + Symfony\Bridge\Monolog\Handler\DebugHandler + Monolog\Handler\SwiftMailerHandler + Monolog\Handler\NativeMailerHandler + Monolog\Handler\SocketHandler + Monolog\Handler\PushoverHandler + Monolog\Handler\RavenHandler + + Monolog\Handler\FingersCrossedHandler + Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy + + + + + app + + + + + + + + + diff --git a/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/Resources/config/schema/monolog-1.0.xsd b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/Resources/config/schema/monolog-1.0.xsd new file mode 100644 index 0000000..ca8ebb3 --- /dev/null +++ b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/Resources/config/schema/monolog-1.0.xsd @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/composer.json b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/composer.json new file mode 100644 index 0000000..c8fd4b0 --- /dev/null +++ b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/composer.json @@ -0,0 +1,37 @@ +{ + "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.2-beta2", + "symfony/dependency-injection": "~2.2-beta2", + "symfony/config": "~2.2-beta2", + "monolog/monolog": "~1.3" + }, + "require-dev": { + "symfony/yaml": "~2.2-beta2" + }, + "autoload": { + "psr-0": { "Symfony\\Bundle\\MonologBundle": "" } + }, + "target-dir": "Symfony/Bundle/MonologBundle", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + } +} diff --git a/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/phpunit.xml.dist b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/phpunit.xml.dist new file mode 100644 index 0000000..90560f7 --- /dev/null +++ b/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + ./Tests + + + + + + . + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Command/SendEmailCommand.php b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Command/SendEmailCommand.php new file mode 100644 index 0000000..f680414 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Command/SendEmailCommand.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\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).') + ->setHelp(<<swiftmailer:spool:send command sends all emails from the spool. + +php app/console swiftmailer:spool:send --message-limit=10 --time-limit=10 --recover-timeout=900 + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $mailer = $this->getContainer()->get('mailer'); + $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('swiftmailer.transport.real')); + + $output->writeln(sprintf('sent %s emails', $sent)); + } + } +} diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DependencyInjection/Compiler/RegisterPluginsPass.php b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DependencyInjection/Compiler/RegisterPluginsPass.php new file mode 100644 index 0000000..63a12dc --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DependencyInjection/Compiler/RegisterPluginsPass.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\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->hasDefinition('swiftmailer.mailer')) { + return; + } + + $definition = $container->findDefinition('swiftmailer.transport'); + foreach ($container->findTaggedServiceIds('swiftmailer.plugin') as $id => $args) { + $definition->addMethodCall('registerPlugin', array(new Reference($id))); + } + } +} diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DependencyInjection/Configuration.php b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..d45112c --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DependencyInjection/Configuration.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\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 + ->children() + ->scalarNode('transport')->defaultValue('smtp')->end() + ->scalarNode('username')->defaultNull()->end() + ->scalarNode('password')->defaultNull()->end() + ->scalarNode('host')->defaultValue('localhost')->end() + ->scalarNode('port')->defaultFalse()->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() + ->arrayNode('spool') + ->children() + ->scalarNode('type')->defaultValue('file')->end() + ->scalarNode('path')->defaultValue('%kernel.cache_dir%/swiftmailer/spool')->end() + ->end() + ->end() + ->scalarNode('sender_address')->end() + ->arrayNode('antiflood') + ->children() + ->scalarNode('threshold')->defaultValue(99)->end() + ->scalarNode('sleep')->defaultValue(0)->end() + ->end() + ->end() + ->scalarNode('delivery_address')->end() + ->end() + ->fixXmlConfig('delivery_whitelist_pattern', 'delivery_whitelist') + ->children() + ->arrayNode('delivery_whitelist') + ->prototype('scalar') + ->end() + ->end() + ->booleanNode('disable_delivery')->end() + ->booleanNode('logging')->defaultValue($this->debug)->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DependencyInjection/SwiftmailerExtension.php b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DependencyInjection/SwiftmailerExtension.php new file mode 100644 index 0000000..bd477fc --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DependencyInjection/SwiftmailerExtension.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\SwiftmailerBundle\DependencyInjection; + +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +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'); + $container->setAlias('mailer', 'swiftmailer.mailer'); + + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + if (null === $config['transport']) { + $transport = 'null'; + } elseif ('gmail' === $config['transport']) { + $config['encryption'] = 'ssl'; + $config['auth_mode'] = 'login'; + $config['host'] = 'smtp.gmail.com'; + $transport = 'smtp'; + } else { + $transport = $config['transport']; + } + + if (isset($config['disable_delivery']) && $config['disable_delivery']) { + $transport = 'null'; + } + + if ('smtp' === $transport) { + $loader->load('smtp.xml'); + } + + if (in_array($transport, array('smtp', 'mail', 'sendmail', 'null'))) { + // built-in transport + $transport = 'swiftmailer.transport.'.$transport; + } + + $container->setAlias('swiftmailer.transport', $transport); + + if (false === $config['port']) { + $config['port'] = 'ssl' === $config['encryption'] ? 465 : 25; + } + + foreach (array('encryption', 'port', 'host', 'username', 'password', 'auth_mode', 'timeout', 'source_ip') as $key) { + $container->setParameter('swiftmailer.transport.smtp.'.$key, $config[$key]); + } + + // spool? + if (isset($config['spool'])) { + $type = $config['spool']['type']; + + $loader->load('spool.xml'); + if ($type === 'file') { + $loader->load('spool_file.xml'); + } elseif ($type === 'memory') { + $loader->load('spool_memory.xml'); + } + $container->setAlias('swiftmailer.transport.real', $transport); + $container->setAlias('swiftmailer.transport', 'swiftmailer.transport.spool'); + $container->setAlias('swiftmailer.spool', 'swiftmailer.spool.'.$type); + + foreach (array('path') as $key) { + $container->setParameter('swiftmailer.spool.'.$type.'.'.$key, $config['spool'][$key]); + } + } + $container->setParameter('swiftmailer.spool.enabled', isset($config['spool'])); + + // antiflood? + if (isset($config['antiflood'])) { + $container->setParameter('swiftmailer.plugin.antiflood.threshold', $config['antiflood']['threshold']); + $container->setParameter('swiftmailer.plugin.antiflood.sleep', $config['antiflood']['sleep']); + + $container->getDefinition('swiftmailer.plugin.antiflood')->addTag('swiftmailer.plugin'); + } + + if ($config['logging']) { + $container->getDefinition('swiftmailer.plugin.messagelogger')->addTag('swiftmailer.plugin'); + $container->findDefinition('swiftmailer.data_collector')->addTag('data_collector', array('template' => 'SwiftmailerBundle:Collector:swiftmailer', 'id' => 'swiftmailer')); + } + + if (isset($config['sender_address']) && $config['sender_address']) { + $container->setParameter('swiftmailer.sender_address', $config['sender_address']); + $container->getDefinition('swiftmailer.plugin.impersonate')->addTag('swiftmailer.plugin'); + } else { + $container->setParameter('swiftmailer.sender_address', null); + } + + if (isset($config['delivery_address']) && $config['delivery_address']) { + $container->setParameter('swiftmailer.single_address', $config['delivery_address']); + $container->getDefinition('swiftmailer.plugin.redirecting')->addTag('swiftmailer.plugin'); + } else { + $container->setParameter('swiftmailer.single_address', null); + } + $container->setParameter('swiftmailer.delivery_whitelist', $config['delivery_whitelist']); + } + + /** + * 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/Symfony/Bundle/SwiftmailerBundle/EventListener/EmailSenderListener.php b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/EventListener/EmailSenderListener.php new file mode 100644 index 0000000..303abc6 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/EventListener/EmailSenderListener.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\SwiftmailerBundle\EventListener; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\IntrospectableContainerInterface; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +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; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function onKernelTerminate(PostResponseEvent $event) + { + if ($this->container instanceof IntrospectableContainerInterface && !$this->container->initialized('mailer')) { + return; + } + + $transport = $this->container->get('mailer')->getTransport(); + if (!$transport instanceof \Swift_Transport_SpoolTransport) { + return; + } + + $spool = $transport->getSpool(); + if (!$spool instanceof \Swift_MemorySpool) { + return; + } + + $spool->flushQueue($this->container->get('swiftmailer.transport.real')); + } + + static public function getSubscribedEvents() + { + return array(KernelEvents::TERMINATE => 'onKernelTerminate'); + } +} diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/LICENSE b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/schema/swiftmailer-1.0.xsd b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/schema/swiftmailer-1.0.xsd new file mode 100644 index 0000000..5f73a12 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/schema/swiftmailer-1.0.xsd @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/smtp.xml b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/smtp.xml new file mode 100644 index 0000000..fafa8ae --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/smtp.xml @@ -0,0 +1,29 @@ + + + + + + Swift_Transport_EsmtpTransport + + + + + + + + + + + %swiftmailer.transport.smtp.host% + %swiftmailer.transport.smtp.port% + %swiftmailer.transport.smtp.encryption% + %swiftmailer.transport.smtp.username% + %swiftmailer.transport.smtp.password% + %swiftmailer.transport.smtp.auth_mode% + %swiftmailer.transport.smtp.timeout% + %swiftmailer.transport.smtp.source_ip% + + + diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/spool.xml b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/spool.xml new file mode 100644 index 0000000..d9009d7 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/spool.xml @@ -0,0 +1,18 @@ + + + + + + Swift_Plugins_RedirectingPlugin + Swift_Plugins_BlackholePlugin + + + + + + + + + diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/spool_file.xml b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/spool_file.xml new file mode 100644 index 0000000..b452553 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/spool_file.xml @@ -0,0 +1,16 @@ + + + + + + Swift_FileSpool + + + + + %swiftmailer.spool.file.path% + + + diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/spool_memory.xml b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/spool_memory.xml new file mode 100644 index 0000000..eefe660 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/spool_memory.xml @@ -0,0 +1,20 @@ + + + + + + Swift_MemorySpool + Symfony\Bundle\SwiftmailerBundle\EventListener\EmailSenderListener + + + + + + + + + + + diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/swiftmailer.xml b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/swiftmailer.xml new file mode 100644 index 0000000..09c705c --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/config/swiftmailer.xml @@ -0,0 +1,87 @@ + + + + + + Swift_Mailer + + Swift_Transport_SendmailTransport + Swift_Transport_MailTransport + + Swift_Transport_FailoverTransport + + Swift_Plugins_RedirectingPlugin + Swift_Plugins_ImpersonatePlugin + Swift_Plugins_MessageLogger + Swift_Plugins_AntiFloodPlugin + 99 + 0 + + Symfony\Bridge\Swiftmailer\DataCollector\MessageDataCollector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %swiftmailer.single_address% + %swiftmailer.delivery_whitelist% + + + + %swiftmailer.plugin.antiflood.threshold% + %swiftmailer.plugin.antiflood.sleep% + + + + %swiftmailer.sender_address% + + + + + + + %swiftmailer.spool.enabled% + + + + + diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/views/Collector/swiftmailer.html.twig b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/views/Collector/swiftmailer.html.twig new file mode 100644 index 0000000..98fb6ea --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/Resources/views/Collector/swiftmailer.html.twig @@ -0,0 +1,71 @@ +{% extends 'WebProfilerBundle:Profiler:layout.html.twig' %} + +{% block toolbar %} + {% if collector.messagecount %} + {% set icon %} + Swiftmailer + {{ collector.messageCount }} + {% endset %} + {% set text %} +
    + Messages + {{ collector.messageCount }} +
    +
    + Is spooled ? + {{ collector.isSpool ? 'yes' : 'no' }} +
    + {% endset %} + {% include 'WebProfilerBundle:Profiler:toolbar_item.html.twig' with { 'link': profiler_url } %} + {% endif %} +{% endblock %} + +{% block menu %} + + Configuration + E-Mails + + {{ collector.messagecount }} + + +{% endblock %} + +{% block panel %} +

    Messages {{ collector.isSpool ? 'spooled' : 'sent' }}

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

    + No message sent. +

    + {% else %} + {% for i, message in collector.messages %} +

    Message {{ i + 1 }} / {{ collector.messagecount }}

    + + {% for header in message.headers.all %} +
    {{ header }}
    + {% endfor %} + +

    +

    +                    {%- if messagePart.charset is defined and message.charset %}
    +                        {{- message.body|e('html', message.charset)|convert_encoding('UTF-8', message.charset) }}
    +                    {%- else %}
    +                        {{- message.body|e('html') }}
    +                    {%- endif -%}
    +                
    +

    + {% for messagePart in message.children if messagePart.contentType == 'text/plain' or messagePart.contentType == 'text/html' %} +

    Alternative part

    +

    +

    +                        {%- if messagePart.charset %}
    +                            {{- messagePart.body|e('html', messagePart.charset)|convert_encoding('UTF-8', messagePart.charset) }}
    +                        {%- else %}
    +                            {{- messagePart.body|e('html') }}
    +                        {%- endif -%}
    +                    
    +

    + {% endfor %} + {% endfor %} + {% endif %} +{% endblock %} diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/SwiftmailerBundle.php b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/SwiftmailerBundle.php new file mode 100644 index 0000000..8456bbf --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/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/Symfony/Bundle/SwiftmailerBundle/composer.json b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/composer.json new file mode 100644 index 0000000..5f35d4d --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/composer.json @@ -0,0 +1,39 @@ +{ + "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.1-dev", + "symfony/swiftmailer-bridge": "~2.1" + }, + "require-dev": { + "symfony/dependency-injection": "~2.1", + "symfony/http-kernel": "~2.1", + "symfony/config": "~2.1", + "symfony/yaml": "~2.1" + }, + "autoload": { + "psr-0": { "Symfony\\Bundle\\SwiftmailerBundle": "" } + }, + "target-dir": "Symfony/Bundle/SwiftmailerBundle", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "minimum-stability": "dev" +} diff --git a/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/phpunit.xml.dist b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/phpunit.xml.dist new file mode 100644 index 0000000..67d0dc9 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + ./Tests + + + + + + . + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/CHANGELOG-2.2.md b/vendor/symfony/symfony/CHANGELOG-2.2.md new file mode 100644 index 0000000..a0ec21c --- /dev/null +++ b/vendor/symfony/symfony/CHANGELOG-2.2.md @@ -0,0 +1,164 @@ +CHANGELOG for 2.2.x +=================== + +This changelog references the relevant changes (bug and security fixes) done +in 2.2 minor versions. + +To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash +To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.2.0...v2.2.1 + +* 2.2.2 (2013-06-02) + + * 2038329: [Form] [Validator] Fixed post_max_size = 0 bug (Issue #8065) + * 169c0b9: [Finder] Fix iteration fails with non-rewindable streams + * 45b68e0: [Finder] Fix unexpected duplicate sub path related AppendIterator issue + * 5321600: Fixed two bugs in HttpCache + * 5c317b7: [Console] fix and refactor exit code handling + * 1469953: [CssSelector] Fix :nth-last-child() translation + * 91b8490: Fix Crawler::children() to not trigger a notice for childless node + * 0a4837d: Fixed XML syntax. + * a5441b2: Fixed parsing of leading blank lines in folded scalars. Closes #7989. + * ef87ba7: [Form] Fixed a method name. + * e8d5d16: Fixed Loader import + * 60edc58: Fixed fatal error in normalize/denormalizeObject. + * 05b987f: [Process] Cleanup tests & prevent assertion that kills randomly Travis-CI + * e4913f8: [Filesystem] Fix regression introduced in 10dea948 + * 5b7e1e6: added a missing check for the provider key + * b0e3ea5: [Validator] fixed wrong URL for XSD + * 59b78c7: [Validator] Fixed: $traverse and $deep is passed to the visitor from Validator::validate() + * bcb5400: [Form] Fixed transform()/reverseTransform() to always throw TransformationFailedExceptions + * 7b2ebbf: [Form] Fixed: String validation groups are never interpreted as callbacks + * 0610750: if the repository method returns an array ensure that it's internal poin... + * dcced01: [Form] Improved multi-byte handling of NumberToLocalizedStringTransformer + * 2b554d7: remove validation related headers when needed + * 2a531d7: Fix getPort() returning 80 instead of 443 when X-FORWARDED-PROTO is set to https + * 10dea94: [Filesystem] copy() is not working when open_basedir is set + * 8757ad4: [Process] Fix #5594 : `termsig` must be used instead of `stopsig` in exceptions when a process is signaled + * be34917: [Console] find command even if its name is a namespace too (closes #7860) + * 3c97004: Reset all catalogues when adding resource to fallback locale (#7715, #7819) + * 0fb35a4: Added reloading of fallback catalogues when calling addResource() (#7715) + * 9e49bc8: Re-added context information to log list + * 06e21ff: Filesystem::touch() not working with different owners (utime/atime issue) + * d98118a: [Config] #7644 add tests for passing number looking attributes as strings + * 36d057b: [HttpFoundation][BrowserKit] fixed path when converting a cookie to a string + * 495d0e3: [HttpFoundation] fixed empty domain= in Cookie::__toString() + * c2bc707: fixed detection of secure cookies received over https + * af819a7: [2.2] Pass ESI header to subrequests + * 54bcf5c: [Translator] added additional conversion for encodings other than utf-8 + * 67b5797: fixed source messages to accept pluralized messages [Validator][translation][japanese] add messages for new validator + * 8a434ed: fix a DI circular reference recognition bug + * 22bf965: [DependencyInjection] fixed wrong exception class + * 5abf887: Fix default value handling for multi-value options + * da156d3: fix overwriting of request's locale if attribute _locale is missing + * 1adbe3c: [HttpKernel] truncate profiler token to 6 chars (see #7665) + * d552e4c: [HttpFoundation] do not use server variable PATH_INFO because it is already decoded and thus symfony is fragile to double encoding of the path + * 4c51ec7: Fix download over SSL using IE < 8 and binary file response + * 46909fa: [Console] Fix merging of application definition, fixes #7068, replaces #7158 + * 972bde7: [HttpKernel] fixed the Kernel when the ClassLoader component is not available (closes #7406) + * f163226: fixed output of bag values + * 047212a: [Yaml] fixed handling an empty value + * 94a9cdc: [Routing][XML Loader] Add a possibility to set a default value to null + * 302d44f: [Console] fixed handling of "0" input on ask + * 383a84b: fixed handling of "0" input on ask + * 0f0c29c: [HttpFoundation] Fixed bug in key searching for NamespacedAttributeBag + * 7fc429f: [Form] DateTimeToRfc3339Transformer use proper transformation exteption in reverse transformation + * 9fcd2f6: [HttpFoundation] fixed the creation of sub-requests under some circumstances for IIS + * 8a9e898: Fix finding ACLs from ObjectIdentity's with different types + * a3826ab: #7531: [HttpKernel][Config] FileLocator adds NULL as global resource path + * 9d71ebe: Fix autocompletion of command names when namespaces conflict + * bec8ff1: Fix timeout in Process::stop method + * 3780fdb: Fix Process timeout + * 99256e4: [HttpKernel] Remove args from 5.3 stack traces to avoid filling log files, fixes #7259 + * e8cae94: fix overwriting of request's locale if attribute _locale is missing + * c4da2d9: [HttpFoundation] getClientIp is fixed. + +* 2.2.1 (2013-04-06) + + * 751abe1: Doctrine cannot handle bare random non-utf8 strings + * 673fd9b: idAsIndex should be true with a smallint or bigint id field. + * 64a1d39: Fixed long multibyte parameter logging in DbalLogger:startQuery + * 4cf06c1: Keep the file extension in the temporary copy and test that it exists (closes #7482) + * 64ac34d: [Security] fixed wrong interface + * 9875c4b: Added '@@' escaping strategy for YamlFileLoader and YamlDumper + * bbcdfe2: [Yaml] fixed bugs with folded scalar parsing + * 5afea04: [Form] made DefaultCsrfProvider using session_status() when available + * c928ddc: [HttpFoudantion] fixed Request::getPreferredLanguage() + * e6b7515: [DomCrawler] added support for query string with slash + * 633c051: Fixed invalid file path for hiddeninput.exe on Windows. + * 7ef90d2: fix xsd definition for strict-requirements + * 39445c5: [WebProfilerBundle] Fixed the toolbar styles to apply them in IE8 + * 601da45: [ClassLoader] fixed heredocs handling + * 17dc2ff: [HttpRequest] fixes Request::getLanguages() bug + * 67fbbac: [DoctrineBridge] Fixed non-utf-8 recognition + * e51432a: sub-requests are now created with the same class as their parent + * cc3a40e: [FrameworkBundle] changed temp kernel name in cache:clear + * d7a7434: [Routing] fix url generation for optional parameter having a null value + * ef53456: [DoctrineBridge] Avoids blob values to be logged by doctrine + * 6575df6: [Security] use current request attributes to generate redirect url? + * 7216cb0: [Validator] fix showing wrong max file size for upload errors + * c423f16: [2.1][TwigBridge] Fixes Issue #7342 in TwigBridge + * 7d87ecd: [FrameworkBundle] fixed cache:clear command's warmup + * 5ad4bd1: [TwigBridge] now enter/leave scope on Twig_Node_Module + * fe4cc24: [TwigBridge] fixed fixed scope & trans_default_domain node visitor + * fc47589: [BrowserKit] added ability to ignored malformed set-cookie header + * 602cdee: replace INF to PHP_INT_MAX inside Finder component. + * 5bc30bb: [Translation] added xliff loader/dumper with resname support + * 663c796: Property accessor custom array object fix + * 4f3771d: [2.2][HttpKernel] fixed wrong option name in FragmentHandler::fixOptions + * a735cbd: fix xargs pipe to work with spaces in dir names + * 15bf033: [FrameworkBundle] fix router debug command + * d16d193: [FramworkBundle] removed unused property of trans update command + * 523ef29: Fix warning for buildXml method + * 7241be9: [Finder] fixed a potential issue on Solaris where INF value is wrong (refs #7269) + * 1d3da29: [FrameworkBundle] avoids cache:clear to break if new/old folders already exist + * b9cdb9a: [HttpKernel] Fixed possible profiler token collision (closes #7272, closes #7171) + * d1f5d25: [FrameworkBundle] Fixes invalid serialized objects in cache + * c82c754: RedisProfilerStorage wrong db-number/index-number selected + * e86fefa: Unset loading[$id] in ContainerBuilder on exception + * 709518b: Default validation message translation fix. + * c0687cd: remove() should not use deprecated getParent() so it does not trigger deprecation internally + * 708c0d3: adjust routing tests to not use prefix in addCollection + * acff735: [Routing] trigger deprecation warning for deprecated features that will be removed in 2.3 + * 41ad9d8: [Routing] make xml loader more tolerant + * 73bead7: [ClassLoader] made DebugClassLoader idempotent + * a4ec677: [DomCrawler] Fix relative path handling in links + * 6681df0: [Console] fixed StringInput binding + * 5bf2f71: [Console] added deprecation annotation + * 8d9cd42: Routing issue with installation in a sub-directory ref: https://github.com/symfony/symfony/issues/7129 + * c97ee8d: [Translator] mention that the message id may also be an object that can be cast to string in TranslatorInterface and fix the IdentityTranslator that did not respect this + * 5a36b2d: [Translator] fix MessageCatalogueInterface::getFallbackCatalogue that can return null + +* 2.2.0 (2013-03-01) + + * 5b19c89: [Console] fixed unparsed StringInput tokens + * e92b76c: Mask PHP_AUTH_PW header in profiler + * bae83c7: [TwigBridge] fixed trans twig extractor + * f40adbc: [Finder] adds adapter selection/unselection capabilities + * 8f8ba38: [DomCrawler] fix handling of schemes by Link::getUri() + * 83382bc: [TwigBridge] fixed the translator extractor that were not trimming the text in trans tags (closes #7056) + * b1ea8e5: Fixed handling absent href attribute in base tag + * 83a61cf: fixed paths/notPaths regex for shell adapters + * 32c5bf7: fix issue 4911 + * 13b8ce0: Adds expandable globs support to shell adapters + * 850bd5a: [HttpFoundation] Fixed messed up headers + * 4ecc246: Fixes AppCache + ESI + Stopwatch problem + * 0690709: added a DebuClassLoader::findFile() method to make the wrapping less invasive + * da22926: [Validator] gracefully handle transChoice errors + * 635b1fc: StringInput resets the given options + +* 2.2.0-RC3 (2013-02-24) + + * b2080c4: [HttpFoundation] Remove Cache-Control when using https download via IE<9 (fixes #6750) + * b7bd630: [Form] Fixed TimeType not to render a "size" attribute in select tags + * 368f62f: Expanded fault-tolerance for unusual cookie dates + * 171cff0: [FrameworkBundle] Fix a BC for Hinclude global template + * 3e40c17: [HttpKernel] fixed locale management when exiting sub-requests + * 3933912: fixed HInclude renderer (closes #7113) + * 189fba6: Removed some leaking deprecation warning in the Form component + * d0e4b76: [HttpFoundation] fixed, overwritten CONTENT_TYPE + * 609636e: [Config] tweaked dumper to indent multi-line info + * 0eff68f: Fix REMOTE_ADDR for cached subrequests + * 54d7d25: [HttpKernel] hinclude fragment renderer must escape URIs properly to return valid html + * f842ae6: [FrameworkBundle] CSRF should be on by default + * cb319ac: [HttpKernel] added error display suppression when using the ErrorHandler (if not, errors are displayed twice, refs #6254) + * de0f7b7: [HttpFoundation] Added getter for httpMethodParameterOverride state diff --git a/vendor/symfony/symfony/CHANGELOG-2.3.md b/vendor/symfony/symfony/CHANGELOG-2.3.md new file mode 100644 index 0000000..1392c6d --- /dev/null +++ b/vendor/symfony/symfony/CHANGELOG-2.3.md @@ -0,0 +1,77 @@ +CHANGELOG for 2.3.x +=================== + +This changelog references the relevant changes (bug and security fixes) done +in 2.3 minor versions. + +To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash +To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.3.0...v2.3.1 + +* 2.3.1 (2013-06-11) + + * 25e3abd: fix many-to-many Propel1 ModelChoiceList + * bce6bd2: [DomCrawler] Fixed a fatal error when setting a value in a malformed field name. + * e3561ce: [FrameworkBundle] Fixed OutOfBoundException when session handler_id is null + * 81b122d: [DependencyInjection] Add support for aliases of aliases + regression test + * 445b2e3: [Console] fix status code when Exception::getCode returns something like 0.1 + * bbfde62: Fixed exit code for exceptions with error code 0 + * d8c0ef7: [DependencyInjection] Rename ContainerBuilder::$aliases to avoid conflicting with the parent class + * bb797ee: [DependencyInjection] Remove get*Alias*Service methods from compiled containers + * 379f5e0: [DependencyInjection] Fix aliased access of shared services, fixes #8096 + * afad9c7: instantiate valid commands only + +* 2.3.0 (2013-06-03) + + * e93fc7a: [FrameworkBundle] set the dispatcher in the console application + * 2038329: [Form] [Validator] Fixed post_max_size = 0 bug (Issue #8065) + * 554ab9f: [Console] renamed ConsoleForExceptionEvent into ConsoleExceptionEvent + * fd151fd: [Security] Fixed the check if an interface exists. + * c8e5503: [FrameworkBundle] removed HttpFoundation classes from HttpKernel cache + * 169c0b9: [Finder] Fix iteration fails with non-rewindable streams + * 45b68e0: [Finder] Fix unexpected duplicate sub path related AppendIterator issue + * 13ba4ea: fix logger in regards to DebugLoggerInterface + * 97b38ed: Added type of return value in VoterInterface. + * 79a842a: [Console] Add namespace support back in to list command + * 5321600: Fixed two bugs in HttpCache + * 435012f: [Config] Adding the previous exception message into the FileLoaderLoadException so it's more easily seen + * 5c317b7: [Console] fix and refactor exit code handling + * 1469953: [CssSelector] Fix :nth-last-child() translation + * 2d9027d: [CssSelector] Fix :nth-last-child() translation + * 91b8490: Fix Crawler::children() to not trigger a notice for childless node + +* 2.3.0-RC1 (2013-05-16) + + * 95f356b: remove check for PHP bug #50731 + * 8f54da7: [BrowserKit] should not follow redirects if status code is not 30x + * f41ac06: changed all version deps to accepts all upcoming Symfony versions + * a4e3ebf: [DomCrawler] Fixed the Crawler::html() method for PHP versions earlier than 5.3.6. + * 3beaf52: [Security] Disabled the BCryptPasswordEncoder tests for PHP versions lower than 5.3.7. + +* 2.3.0-BETA2 (2013-05-10) + + * 97bee20: Pass exceptions from the ExceptionListener to Monolog + * be42dbc: [HttpFoundation][File][UploadedFile] Fix guessClientExtension() method + * a5441b2: Fixed parsing of leading blank lines in folded scalars. Closes #7989. + * e8d5d16: Fixed Loader import + * bd0c48c: [Console] moved the IO configuration to its own method + * fdb4b1f: [Console] moved --help support to allow proper behavior with other passed options + * dd0e138: Eased translationNodeVisitor overriding in TranslationExtension + * 853f681: fixed request scope issues (refs #7457) + * 60edc58: Fixed fatal error in normalize/denormalizeObject. + * 78e3710: ProxyManager Bridge + * 41805c0: [Crawler] Add proper validation of node argument of method add + * 7933971: [Form] Added radio button for empty value to expanded single-choice fields + * 0586c7e: made some optimization when parsing YAML files + * 1856df3: [Security] fixed wrong merge (refs #4776) + * 5b7e1e6: added a missing check for the provider key + * f1c2ab7: [DependencyInjection] Add a method map to avoid computing method names from service names + * ea633f5: [HttpKernel] Avoid updating the context if the request did not change + * 997d549: [HttpFoundation] Avoid a few unnecessary str_replace() calls + * f5e7f24: [HttpFoundation] Optimize ServerBag::getHeaders() + * 59b78c7: [Validator] Fixed: $traverse and $deep is passed to the visitor from Validator::validate() + * bcb5400: [Form] Fixed transform()/reverseTransform() to always throw TransformationFailedExceptions + * 7b2ebbf: [Form] Fixed: String validation groups are never interpreted as callbacks + * 0610750: if the repository method returns an array ensure that it's internal poin... + * dcced01: [Form] Improved multi-byte handling of NumberToLocalizedStringTransformer + * 90a20d7: [Translation] Made translation domain defaults in Translator consistent with TranslatorInterface + * 549a308: [Form] Fixed CSRF error messages to be translated and added "csrf_message" option diff --git a/vendor/symfony/symfony/CONTRIBUTING.md b/vendor/symfony/symfony/CONTRIBUTING.md new file mode 100644 index 0000000..383bec8 --- /dev/null +++ b/vendor/symfony/symfony/CONTRIBUTING.md @@ -0,0 +1,11 @@ +Contributing +------------ + +Symfony2 is an open source, community-driven project. If you'd like to contribute, +please read the [Contributing Code][1] part of the documentation. If you're submitting +a pull request, please follow the guidelines in the [Submitting a Patch][2] section +and use the [Pull Request Template][3]. + +[1]: http://symfony.com/doc/current/contributing/code/index.html +[2]: http://symfony.com/doc/current/contributing/code/patches.html#check-list +[3]: http://symfony.com/doc/current/contributing/code/patches.html#make-a-pull-request diff --git a/vendor/symfony/symfony/CONTRIBUTORS.md b/vendor/symfony/symfony/CONTRIBUTORS.md new file mode 100644 index 0000000..4cc61bf --- /dev/null +++ b/vendor/symfony/symfony/CONTRIBUTORS.md @@ -0,0 +1,807 @@ +CONTRIBUTORS +============ + +Symfony2 is the result of the work of many people who made the code better +(see http://symfony.com/contributors for more information): + + - Fabien Potencier (fabpot) + - Bernhard Schussek (bschussek) + - Victor Berchet (victor) + - Jordi Boggiano (seldaek) + - Johannes S (johannes) + - Kris Wallsmith (kriswallsmith) + - Tobias Schultze (tobion) + - Christophe Coevoet (stof) + - Pascal Borreli (pborreli) + - Karma Dordrak (drak) + - Lukas Kahwe Smith (lsmith) + - Ryan Weaver (weaverryan) + - Joseph Bielawski (stloyd) + - Jeremy Mikola (jmikola) + - Igor Wiedler (igorw) + - Benjamin Eberlei (beberlei) + - Jean-François Simon (jfsimon) + - Hugo Hamon (hhamon) + - Eriksen Costa (eriksencosta) + - Martin Hasoň (hason) + - Jakub Zalas (jakubzalas) + - Jonathan Wage (jwage) + - William Durand (couac) + - Alexandre Salomé (alexandresalome) + - ornicar + - stealth35 ‏ (stealth35) + - Alexander Mols (asm89) + - Bulat Shakirzyanov (avalanche123) + - Francis Besset (francisbesset) + - Henrik Bjørnskov (henrikbjorn) + - Miha Vrhovnik + - Bilal Amarni (bamarni) + - Romain Neutron (romain) + - Florin Patan (florinpatan) + - Konstantin Kudryashov (everzet) + - Saša Stamenković (umpirsky) + - Arnaud Le Blanc (arnaud-lb) + - Eric Clemmons (ericclemmons) + - Dariusz Górecki (canni) + - Henrik Westphal (snc) + - Deni + - Marc Weistroff (futurecat) + - Jordan Alliot (jalliot) + - Arnout Boks (aboks) + - Hidenori Goto (hidenorigoto) + - Fran Moreno (franmomu) + - Andrej Hudec (pulzarraider) + - Lee McDermott + - Brandon Turner + - Daniel Holmes (dholmes) + - Brikou Carré (brikou) + - John Wards (johnwards) + - Grégoire Pineau (lyrixx) + - Antoine Hérault (herzult) + - Bart van den Burg (burgov) + - Tim Nagel (merk) + - Włodzimierz Gajda (gajdaw) + - Michel Weimerskirch (mweimerskirch) + - Christian Raue + - Michal Piotrowski (eventhorizon) + - Toni Uebernickel (havvg) + - Colin Frei + - lenar + - Fabien Pennequin (fabienpennequin) + - excelwebzone + - woodspire + - Mario A. Alvarez Garcia (nomack84) + - Douglas Greenshields (shieldo) + - Kevin Bond (kbond) + - Richard Miller (mr_r_miller) + - Jacob Dreesen (jdreesen) + - Richard Shank (iampersistent) + - Sebastian Hörl (blogsh) + - Gábor Egyed (1ed) + - David Buchmann (dbu) + - Juti Noppornpitak + - Robert Schönthal (digitalkaoz) + - Felix Labrecque + - Michał Pipa (michal.pipa) + - Gordon Franke (gimler) + - Daniel Gomes (danielcsgomes) + - Tigran Azatyan (tigranazatyan) + - Pierre Minnieur (pminnieur) + - Larry Garfield (crell) + - Arnaud Kleinpeter (nanocom) + - Jonathan Ingram (jonathaningram) + - Sebastiaan Stok (sstok) + - Jérémie Augustin (jaugustin) + - Javier Eguiluz (javier.eguiluz) + - Matthieu Ouellette-Vachon (maoueh) + - Amal Raghav (kertz) + - Artur Kotyrba + - Pablo Godel (pgodel) + - Helmer Aaviksoo + - Clément JOBEILI (dator) + - Hiromi Hishida (77web) + - Julien Brochet (mewt) + - Rafael Dohms (rdohms) + - Benjamin Dulau (dbenjamin) + - Andreas Hucks (meandmymonkey) + - Noel Guilbert (noel) + - Guilherme Blanco (guilhermeblanco) + - Martin Schuhfuß (usefulthink) + - Thomas Rabaix (rande) + - Dennis Benkert (denderello) + - Marcel Beerta (mazen) + - Albert Casademont (acasademont) + - Matthieu Bontemps (mbontemps) + - fivestar + - Dominique Bongiraud + - Leszek Prabucki (l3l0) + - Danny Berger (dpb587) + - Eric GELOEN (gelo) + - Dustin Whittle (dustinwhittle) + - jeff + - Clemens Tolboom + - Justin Hileman (bobthecow) + - Sven Paulus (subsven) + - Dmitrii Chekaliuk (lazyhammer) + - Xavier Perez + - Rui Marinho (ruimarinho) + - Dorian Villet (gnutix) + - Ray + - Joseph Rouff (rouffj) + - Francois Zaninotto + - Alexander Kotynia (olden) + - François Zaninotto (fzaninotto) + - Xavier Montaña Carreras (xmontana) + - Katsuhiro OGAWA + - Andréia Bohner (andreia) + - Peter Kokot (maastermedia) + - Terje Bråten + - Alif Rachmawadi + - boombatower + - Florian Klein (docteurklein) + - Adrien Brault (adrienbrault) + - jules boussekeyt (gordonslondon) + - Jan Sorgalla (jsor) + - Ait Boudad Abdellatif (aitboudad) + - jdhoek + - geoffrey + - Wodor Wodorski + - Elnur Abdurrakhimov (elnur) + - Matthew Lewinski (lewinski) + - Kim Hemsø Rasmussen + - Wouter De Jong (wouterj) + - Dirk Pahl (dirkaholic) + - Wouter Van Hecke + - Gyula Sallai (salla) + - Michael Holm (hollo) + - Yaroslav Kiliba + - Sebastian Bergmann + - arjen + - Matthias Pigulla (mpdude) + - sun (sun) + - Manuel Kiessling (manuelkiessling) + - Sergey Linnik + - Bertrand Zuchuat (garfield-fr) + - Beau Simensen (simensen) + - Grégoire Paris (greg0ire) + - Tamas Szijarto + - Thomas Adam + - Grégoire Passault (gregwar) + - Uwe Jäger (uwej711) + - Aurelijus Valeiša (aurelijus) + - Gustavo Piltcher + - Stepan Tanasiychuk (stfalcon) + - Albert Jessurum (ajessu) + - Tiago Ribeiro (fixe) + - Adrian Rudnik (kreischweide) + - Francesc Rosàs (frosas) + - Julien Galenski (ruian) + - Bongiraud Dominique + - Michel Salib (michelsalib) + - Jeanmonod David (jeanmonod) + - Thomas Lallement (raziel057) + - Ricard Clau (ricardclau) + - Niklas Fiekas + - Konstantin Myakshin (koc) + - Erin Millard + - Manuel Reinhard (sprain) + - Francesco Levorato + - Michele Orselli (orso) + - Tom Van Looy (tvlooy) + - Brouznouf + - Peter Kruithof (pkruithof) + - Kristen Gilden (kgilden) + - hossein zolfi (ocean) + - Greg Thornton (xdissent) + - Lars Strojny + - Costin Bereveanu (schniper) + - Markus Lanthaler (lanthaler) + - Jérôme Vieilledent (lolautruche) + - realmfoo + - Pavel Volokitin (pvolok) + - Tobias Naumann + - Ismael Ambrosi (iambrosi) + - Shein Alexey + - Joe Lencioni + - Chekote + - Kai + - Denis Gorbachev (starfall) + - Laszlo Korte + - Alessandro Desantis (alessandro1997) + - hubert lecorche (hlecorche) + - Eugene Leonovich (rybakit) + - Oscar Cubo Medina (ocubom) + - Karel Souffriau + - Christophe L. (christophelau) + - Thomas Tourlourat (armetiz) + - Michael Ridgway + - Pavel Campr (pcampr) + - janschoenherr + - Emanuele Gaspari (inmarelibero) + - Brian King + - Jan Schumann + - Antonio J. García Lagar (ajgarlag) + - Olivier Dolbeau (odolbeau) + - Robert Kiss (kepten) + - Asier Illarramendi (doup) + - Javier López (loalf) + - Chris Heng (gigablah) + - Christoph Mewes (xrstf) + - Vitaliy Tverdokhlib (vitaliytv) + - Jonas Flodén (flojon) + - Shigenibu Nishikawa + - Marcin Sikoń (marphi) + - Miquel Rodríguez Telep (mrtorrent) + - Filippo Tessarotto + - Adam Harvey + - Laurent Bachelier (laurentb) + - Fabian Lange (codingfabian) + - Yoshio HANAWA + - Tiago Brito (blackmx) + - Kevin McBride + - Pablo Díez (pablodip) + - Michael Piecko (michael.piecko) + - Sebastian Krebs + - Manuel de Ruiter (manuel) + - Iker Ibarguren (ikerib) + - Ricardo Oliveira (ricardolotr) + - ondrowan + - Roman Marintsenko (inori) + - Stéphane PY (steph_py) + - mcben + - Christian Flothmann (xabbuh) + - Maks Slesarenko + - Vicent Soria Durá (vicentgodella) + - alexpods + - Erik Trapman (eriktrapman) + - De Cock Xavier (xdecock) + - Matthijs van den Bos + - Joel Wurtz + - Nils Adermann (naderman) + - Gábor Fási + - Leevi Graham + - Michaël Perrin (michael.perrin) + - sasezaki + - Loïc Chardonnet (gnusat) + - Xavier HAUSHERR + - Steven Surowiec + - Marek Kalnik (marekkalnik) + - Chris Smith + - Anthon Pang + - Ryan + - Alexander Deruwe (aderuwe) + - Ivan Rey (ivanrey) + - Jan Kramer (jankramer) + - Marcin Chyłek (songoq) + - Ned Schwartz + - Ziumin + - Lenar Lõhmus + - Zach Badgett (zachbadgett) + - Aurélien Fredouelle + - Karoly Negyesi (chx) + - jamogon + - Miha Vrhovnik + - Geoffrey Tran (geoff) + - Florian Rey (nervo) + - Maks + - Christian Schaefer (caefer) + - Elliot Anderson (elliot) + - Patrick Kaufmann + - Marco Pivetta (ocramius) + - Ben Ramsey (ramsey) + - Christian Jul Jensen + - Chris Jones (leek) + - vitaliytv + - Markus Bachmann (baachi) + - aubx + - Max Rath (drak3) + - Ruben Gonzalez (rubenrua) + - Sinan Eldem + - DerManoMann + - alquerci + - Nahuel Cuesta (ncuesta) + - Chris Boden (cboden) + - Roumen Damianoff (roumen) + - Pierre du Plessis (pierredup) + - Josip Kruslin + - Åsmund Garfors + - Jeremy David (jeremy.david) + - Tobias Sjösten (tobiassjosten) + - Dustin Dobervich (dustin10) + - Sebastian Marek (proofek) + - Erkhembayar Gantulga (erheme318) + - Ken Marfilla (marfillaster) + - Rostyslav Kinash + - jfcixmedia + - Vincent Simonin + - Stefan Warman + - yktd26 + - Jakub Kucharovic + - umpirski + - Antoine Corcy + - cedric lombardot (cedriclombardot) + - John Kary (johnkary) + - Hossein Bukhamsin + - Pierre-Yves LEBECQ (pylebecq) + - Fabrice Bernhard (fabriceb) + - Oleg Zinchenko (cystbear) + - Johannes Klauss (cloppy) + - fzerorubigd + - Mark Sonnabaum + - develop + - Atsuhiro KUBO (iteman) + - Samy Dindane (dinduks) + - yclian + - Jérôme Tamarelle (gromnan) + - Jérémy Romey (jeremyfreeagent) + - Pascal Helfenstein + - Matt Daum (daum) + - Baldur Rensch + - Alex Xandra Albert Sim + - Yuen-Chi Lian + - Joshua Nye + - avorobiev + - Mark Challoner + - Andrew Tchircoff (andrewtch) + - BilgeXA + - michaelwilliams + - Casper Valdemar Poulsen + - Eduardo Gulias (egulias) + - Josiah (josiah) + - John Bohn (jbohn) + - Nicolas Schwartz (nicoschwartz) + - Andrew Hilobok + - Christian Soronellas Vallespí (theunic) + - Benjamin Grandfond (benjamin) + - Degory Valentine + - Krzysiek Łabuś + - Andrew Udvare + - Xavier Lacot (xavier) + - Olivier Maisonneuve + - Iwan van Staveren (istaveren) + - cgonzalez + - matt foster + - Evan S Kaufman (evanskaufman) + - Jayson Xu (superjavason) + - Jan Prieser + - James Michael DuPont + - Tom Klingenberg + - Gunther Konig + - Christopher Hall (mythmakr) + - Paul Kamer (pkamer) + - Pierre Vanliefland (pvanliefland) + - Martin Parsiegla (spea) + - Luis Cordova (cordoval) + - Philipp Kräutli (pkraeutli) + - Stefano Sala (stefano.sala) + - Vitaliy Zakharov (zakharovvi) + - frost-nzcr4 + - Abhoryo + - Fabian Vogler (fabian) + - Maksim Kotlyar (makasim) + - Tugdual Saunier (tucksaun) + - Neil Ferreira + - Tony Malzhacker + - Cyril Quintin (cyqui) + - Gerard van Helden (drm) + - Johnny Peck (johnnypeck) + - Kirill chEbba Chebunin + - Benjamin Leveque (benji07) + - Gustavo Falco (gfalco) + - Matt Robinson (inanimatt) + - julien pauli (jpauli) + - mwsaz + - Benoît Bourgeois + - Filipe Guerra + - corphi + - grizlik + - Derek ROTH + - Shin Ohno (ganchiku) + - Drew Butler (nodrew) + - Alexander Miehe (engerim) + - Titouan Galopin (tgalopin) + - Don Pinkster + - Maksim Muruev + - Emil Einarsson + - Thibault Duplessis + - Marc Abramowitz + - Martijn Evers + - Mathias Rohnstock (drmonty) + - Harry Walter (haswalt) + - Michael Roterman (wtfzdotnet) + - Arno Geurts + - Adán Lobato (adanlobato) + - Mikhail Yurasov + - Sam Williams + - Moritz Borgmann + - Daniel Cestari + - Magnus Nordlander (magnusnordlander) + - Adam Monsen (meonkeys) + - LOUARDI Abdeltif (ouardisoft) + - Robert Gruendler (pulse00) + - ragtek (ragtek) + - Simon Terrien (sterrien) + - Benoît Merlet (trompette) + - Jan Behrens + - Raul Fraile (raulfraile) + - sensio + - Théophile Helleboid - chtitux + - The Whole Life to Learn + - xaav + - Mahmoud Mostafa (mahmoud) + - Juti Noppornpitak + - Mei Gwilym + - ttomor + - Sander Coolen + - Nicolas Le Goff (nlegoff) + - Manuele Menozzi + - Anton Babenko (antonbabenko) + - Irmantas Šiupšinskas (irmantas) + - dantleech + - Tero Alén (tero) + - Artem (digi) + - dantleech + - Vadim Tyukov (vatson) + - Sortex + - arjenjb + - chispita + - Wojciech Sznapka + - Máximo Cuadros (mcuadros) + - Alex Bogomazov + - julien.galenski + - Sébastien Lavoie + - Per Sandström (per) + - Lin Clark + - Troy McCabe + - Ville Mattila + - Sescandell (sescandell) + - Ben Davies + - Max Beutel + - Marcos Quesada (marcos_quesada) + - Dan Finnie + - Martijn Evers + - Benjamin Paap (benjaminpaap) + - Sergii Smertin (nfx) + - Eddie Jaoude + - Nerijus Arlauskas + - Haritz Iturbe (hizai) + - SPolischook + - Diego Sapriza + - Joan Cruz + - inspiran + - Cristobal Dabed + - matteo giachino + - Daniel Mecke (daniel_mecke) + - Alex Demchenko (pilot) + - Tomasz Kowalczyk (thunderer) + - Vincent AUBERT (vincent) + - Benoit Garret + - DerManoMann + - Roland Franssen (ro0) + - Jochen Bayer (jocl) + - Jeremy Bush + - Evan Villemez + - Péter Buri (burci) + - Davide Borsatto (davide.borsatto) + - kaiwa + - Albert Ganiev (helios-ag) + - Neil Katin + - Gustavo Adrian + - Brooks Boyd + - Roger Webb + - Nicolas Fabre (nfabre) + - Felicitus + - Paul Matthews + - Philipp Strube + - Clement Herreman (clemherreman) + - Marco + - Alberto Aldegheri + - heccjj + - Alexandre Melard + - modi + - Sergey Yuferev + - Richard van den Brand (ricbra) + - Quique Porta + - Aharon Perkel + - Malaney J. Hill + - Andy Cox (ringo) + - Sebastian Göttschkes (sgoettschkes) + - erikaheidi + - Pierre Tachoire + - Ludek Stepan + - Balázs Benyó (duplabe) + - Marc Morera (mmoreram) + - Sebastian Utz + - Keri Henare (kerihenare) + - Cédric Lahouste (rapotor) + - Anthony Ferrara + - Janusz Jablonski + - George Giannoulopoulos + - Chris Wilkinson (thewilkybarkid) + - Ilya Biryukov + - Jason Desrosiers + - m.chwedziak + - Lance McNearney + - Alberto Pirovano (geezmo) + - Gabor Toth (tgabi333) + - Xavier Briand (xavierbriand) + - Evan Kaufman + - Romain Geissler + - Marcus Stöhr (dafish) + - Eduardo Oliveira (entering) + - Carsten Nielsen (phreaknerd) + - Jay Severson + - René Kerner + - Adrien Samson (adriensamson) + - Samuel Gordalina (gordalina) + - Timothy Anido (xanido) + - Martin Eckhardt + - Michael Dowling (mtdowling) + - Robert Queck + - mlively + - Fabian Steiner (fabstei) + - Thomas Chmielowiec (chmielot) + - Jānis Lukss + - fdgdfg (psampaz) + - Maxwell Vandervelde + - kaywalker + - Sebastian Ionescu + - Simon Neidhold + - Kevin Dew + - James Cowgill + - Patrik Gmitter (patie) + - Thomas Ploch (tploch) + - Benjamin Bender + - Konrad Mohrfeldt + - Benoit Lévêque (benoit_leveque) + - jskvara + - Mephistofeles + - Hoffmann András + - pscheit + - Ramon Kleiss (akathos) + - Nicolas Badey (nico-b) + - Gunnar Lium + - povilas + - Lars Strojny + - Bouke Haarsma + - Harm van Tilborg + - Martin Eckhardt + - Leonid Terentyev + - Przemysław Piechota (kibao) + - Tom Adam (tomadam) + - Francisco Facioni (fran6co) + - Paweł Wacławczyk (pwc) + - Eric Caron + - 2manypeople + - Thomas Bibb + - Josef Cech + - Andrey Esaulov (andremaha) + - hicham ELGUAROUANI (hiiimoo) + - Stefan Koopmanschap (skoop) + - Ivan Kurnosov + - stloyd + - Andrew Coulton + - Chris Tickner (tickner) + - Luis Muñoz + - Thomas Chmielowiec + - Christoph Nissle (derstoffel) + - Benjamin Zikarsky + - Romain Dorgueil + - Patrick Allaert + - Grayson Koonce (breerly) + - Andy Stanberry + - alefranz + - alsar + - Mike Meier + - efeen + - Bob den Otter (bopp) + - Simone Fumagalli (hpatoio) + - Alessio Baglio (ioalessio) + - John Bafford (jbafford) + - Jérôme Macias (jeromemacias) + - Jordi Llonch (jordillonch) + - Cédric Dugat (ph3nol) + - Philip Dahlstrøm (phidah) + - Rénald Casagraude (rcasagraude) + - Robin Duval (robin-duval) + - Artem Lopata (bumz) + - Iltar van der Berg + - Alexey Popkov + - Artyom Protaskin + - Nathanael d. Noblet + - helmer + - Bram Van der Sype (brammm) + - Diego Saint Esteben (dii3g0) + - Julien Moulin (lizjulien) + - dened + - devel + - gedrox + - hirocaster + - Andrey Chernykh + - François Pluchino + - Alexey Prilipko + - Jörn Lang (j.lang) + - Jan Marek (janmarek) + - Dan Patrick (mdpatrick) + - Rares Vlaseanu (raresvla) + - tante kinast (tante) + - Alexander Zogheb + - Florian Pfitzer + - Linnik Sergey + - David Christmann + - root + - Pierre Rineau + - Ari Pringle (apringle) + - Dan Ordille (dordille) + - David Zuelke (dzuelke) + - Jan Eichhorn (exeu) + - Julien DIDIER (juliendidier) + - Martin Mayer (martin) + - Grzegorz Łukaszewicz (newicz) + - partugal + - Robert Campbell + - Matt Lehner + - cyrillej + - Alex Pods + - timaschew + - Christian Morgan + - Haritz + - Grummfy + - Rowan Manning + - Kevin Herrera + - David Windell + - Gabriel Birke + - Alan Chen + - Maerlyn + - Even André Fiskvik + - Franz Liedke + - Rafał Wrzeszcz + - Frédéric Hardy + - Lenar Lõhmus + - Cristian Gonzalez + - Juan M Martínez + - Gilles Gauthier + - ddebree + - Alex + - Klaas Naaijkens + - Rafał + - Masao Maeda (brtriver) + - Dave Marshall (davedevelopment) + - David Marín Carreño (davefx) + - Denis Klementjev (dklementjev) + - Kévin Dunglas (dunglas) + - Vincent Composieux (eko) + - Osman Üngür (import) + - Jorge Martin (jorgemartind) + - giulio de donato (liuggio) + - Matthew Davis (mdavis1982) + - Muriel Lusseau (metalmumu) + - Pablo Monterde Perez (plebs) + - Jimmy Leger (redpanda) + - Baptiste Clavié (talus) + - Yanick Witschi + - ramonornela + - Till Klampaeckel + - srsbiz + - Nicolas A. Bérard-Nault + - Gladhon + - Saem Ghani + - Alexey Popkov + - Piotr Błasiak + - Arnaud Buathier (arnapou) + - chesteroni (chesteroni) + - Simon CONSTANS (kosssi) + - Mauricio Lopez (sanctuary29) + - Wotre + - goohib + - Xavier HAUSHERR + - Myke79 + - Brian Debuire + - Lars Vierbergen + - Sylvain Lorinet + - jc + - BenjaminBeck + - Christian Eikermann + - Vladimir Sazhin + - Vyacheslav Slinko + - Johannes + - Jörg Rühl + - patrick-mcdougle + - Giacomo Gallico + - andreabreu98 + - Michael Schneider + - Jerome Tamarelle + - xanido + - Kaipi Yann + - Samuel Laulhau + - Oleg Stepura + - James Michael DuPont + - Tammy D + - Ondrej Slinták + - vlechemin + - Skorney + - André Neves + - Pierre-Louis LAUNAY + - djama + - Sébastien HOUZE + - Abdulkadir N. A. + - Sema + - Thorsten Hallwas + - Vincent + - Chris Smith + - kwiateusz + - David Soria Parra + - Sergiy Sokolenko + - dinitrol + - Penny Leach + - oscartv + - Philipp Rieber + - DanSync + - Peter Zwosta + - parhs + - Oncle Tom + - Xavier Amado + - Andreas Forsblom + - Christian Stocker + - dorkitude + - tirnanog06 + - phc + - Besnik Br + - sualko + - Nicolas Roudaire + - Alex Olmos (alexolmos) + - Juan Ases García (ases) + - Bernd Matzner (bmatzner) + - Chris Sedlmayr (catchamonkey) + - Kousuke Ebihara (co3k) + - Christoph Schaefer (cvschaefer) + - Damien Alexandre (damienalexandre) + - Damon Jones (damon__jones) + - Daniel Londero (dlondero) + - Adel ELHAIBA (eadel) + - Fabien Dosse (fabd) + - Yohan Giarelli (frequence-web) + - Massimiliano Arione (garak) + - Vladislav Krupenkin (ideea) + - joris de wit (jdewit) + - Jérémy CROMBEZ (jeremy) + - Jorge Maiden (jorgemaiden) + - Justin Rainbow (jrainbow) + - Sébastien Armand (khepin) + - Krzysztof Menżyk (krymen) + - Martin Ledgard (le6o) + - Matthieu Moquet (mattketmo) + - Matt Drollette (mdrollette) + - Florent CAILHOL (ooflorent) + - Petr Jaroš (petajaros) + - Philipp Hoffmann (philipphoffmann) + - Alex Carol (picard89) + - Daniel Perez Pinazo (pitiflautico) + - Rich Sage (richsage) + - Ruud Kamphuis (ruudk) + - Sarah Khalil (saro0h) + - Sebastian Busch (sebu) + - Markus Tacker (tacker) + - Tyler Stroud (tystr) + - Víctor Mateo (victormateo) + - Eugene Babushkin (warl) + - Florent Cailhol + - craigmarvelley + - Stano Turza + - Teo + - drublic + - Andreas Streichardt + - smokeybear87 + - Gustavo Adrian + - Anthon Pang + - Michael + - Xavier REN + - max + - Mohamed Karnichi (amiral) + - Muharrem Demirci (mdemirci) + - Evgeny Z (meze) + - Nicolas de Marqué (nicola) + - Pierre Geyer (ptheg) + - Erik Saunier (snickers) + - Tony Piper (tonypiper) + - Vladislav Vlastovskiy (vlastv) diff --git a/vendor/symfony/symfony/LICENSE b/vendor/symfony/symfony/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/UPGRADE-2.1.md b/vendor/symfony/symfony/UPGRADE-2.1.md new file mode 100644 index 0000000..ecb2a18 --- /dev/null +++ b/vendor/symfony/symfony/UPGRADE-2.1.md @@ -0,0 +1,1478 @@ +UPGRADE FROM 2.0 to 2.1 +======================= + +### General + + * The merging strategy for `assets_base_urls` and `base_urls` has changed. + + Unlike most configuration blocks, successive values for `assets_base_urls` + will overwrite each other instead of being merged. This behavior was chosen + because developers will typically define base URL's for each environment. + Given that most projects tend to inherit configurations (e.g. + `config_test.yml` imports `config_dev.yml`) and/or share a common base + configuration (i.e. `config.yml`), merging could yield a set of base URL's + for multiple environments. + + * The priorities for the built-in listeners have changed. + + ``` + 2.0 2.1 + security.firewall kernel.request 64 8 + locale listener kernel.request 0 16 + router listener early_request 255 n/a + request 0 32 + ``` + +### Doctrine + + * The DoctrineBundle is moved from the Symfony repository to the Doctrine repository. + Therefore you should change the namespace of this bundle in your AppKernel.php: + + Before: `new Symfony\Bundle\DoctrineBundle\DoctrineBundle()` + + After: `new Doctrine\Bundle\DoctrineBundle\DoctrineBundle()` + +### HttpFoundation + + * Locale management was moved from the Session class to the Request class. + + ##### Configuring the default locale + + Before: + + ``` + framework: + session: + default_locale: fr + ``` + + After: + + ``` + framework: + default_locale: fr + ``` + + ##### Retrieving the locale from a Twig template + + Before: `{{ app.request.session.locale }}` or `{{ app.session.locale }}` + + After: `{{ app.request.locale }}` + + ##### Retrieving the locale from a PHP template + + Before: `$view['session']->getLocale()` + + After: `$view['request']->getLocale()` + + ##### Retrieving the locale from PHP code + + Before: `$session->getLocale()` + + After: `$request->getLocale()` + + ##### Simulate old behavior + + You can simulate that the locale for the user is still stored in the session by + registering a listener that looks like the following if the parameter which + handles the locale value in the request is `_locale`: + + ``` + namespace XXX; + + use Symfony\Component\HttpKernel\Event\GetResponseEvent; + use Symfony\Component\HttpKernel\KernelEvents; + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + + class LocaleListener implements EventSubscriberInterface + { + private $defaultLocale; + + public function __construct($defaultLocale = 'en') + { + $this->defaultLocale = $defaultLocale; + } + + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + if (!$request->hasPreviousSession()) { + return; + } + + if ($locale = $request->attributes->get('_locale')) { + $request->getSession()->set('_locale', $locale); + } else { + $request->setLocale($request->getSession()->get('_locale', $this->defaultLocale)); + } + } + + static public function getSubscribedEvents() + { + return array( + // must be registered before the default Locale listener + KernelEvents::REQUEST => array(array('onKernelRequest', 17)), + ); + } + } + ``` + + * 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. + +### Security + + * `Symfony\Component\Security\Core\User\UserInterface::equals()` has moved to + `Symfony\Component\Security\Core\User\EquatableInterface::isEqualTo()`. + + You must rename the `equals()` method in your implementation of the `User` + class to `isEqualTo()` and implement `EquatableInterface`. Apart from that, + no other changes are required. + + Alternatively, you may use the default implementation provided by + `AbstractToken::hasUserChanged()` if you have no need of custom comparison + logic. In this case, do not implement `EquatableInterface` and remove your + comparison method. + + Before: + + ``` + class User implements UserInterface + { + // ... + public function equals(UserInterface $user) { /* ... */ } + // ... + } + ``` + + After: + + ``` + class User implements UserInterface, EquatableInterface + { + // ... + public function isEqualTo(UserInterface $user) { /* ... */ } + // ... + } + ``` + + * The custom factories for the firewall configuration are now + registered during the build method of bundles instead of being registered + by the end-user. This means that you will need to remove the 'factories' + keys in your security configuration. + + Before: + + ``` yaml + security: + factories: + - "%kernel.root_dir%/../src/Acme/DemoBundle/Resources/config/security_factories.yml" + ``` + + ``` yaml + # src/Acme/DemoBundle/Resources/config/security_factories.yml + services: + security.authentication.factory.custom: + class: Acme\DemoBundle\DependencyInjection\Security\Factory\CustomFactory + tags: + - { name: security.listener.factory } + ``` + + After: + + ``` + namespace Acme\DemoBundle; + + use Symfony\Component\HttpKernel\Bundle\Bundle; + use Symfony\Component\DependencyInjection\ContainerBuilder; + use Acme\DemoBundle\DependencyInjection\Security\Factory\CustomFactory; + + class AcmeDemoBundle extends Bundle + { + public function build(ContainerBuilder $container) + { + parent::build($container); + + $extension = $container->getExtension('security'); + $extension->addSecurityListenerFactory(new CustomFactory()); + } + } + ``` + + * The Firewall listener is now registered after the Router listener. This + means that specific Firewall URLs (like /login_check and /logout) must now + have proper routes defined in your routing configuration. Also, if you have + a custom 404 error page, make sure that you do not use any security related + features such as `is_granted` on it. + + * The user provider configuration has been refactored. The configuration + for the chain provider and the memory provider has been changed: + + 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] } + ``` + + * `MutableAclInterface::setParentAcl` now accepts `null`, review any + implementations of this interface to reflect this change. + + * The `UserPassword` constraint has moved from the Security Bundle to the Security Component: + + Before: + + ``` + use Symfony\Bundle\SecurityBundle\Validator\Constraint\UserPassword; + use Symfony\Bundle\SecurityBundle\Validator\Constraint as SecurityAssert; + ``` + + After: + + ``` + use Symfony\Component\Security\Core\Validator\Constraint\UserPassword; + use Symfony\Component\Security\Core\Validator\Constraint as SecurityAssert; + ``` + +### Form + +#### BC Breaks in Form Types and Options + + * A third argument `$options` was added to the methods `buildView()` and + `buildViewBottomUp()` in `FormTypeInterface` and `FormTypeExtensionInterface`. + Furthermore, `buildViewBottomUp()` was renamed to `finishView()`. At last, + all methods in these types now receive instances of `FormBuilderInterface` + where they received instances of `FormBuilder` before. You need to change the + method signatures in your form types and extensions as shown below. + + Before: + + ``` + use Symfony\Component\Form\FormBuilder; + + public function buildForm(FormBuilder $builder, array $options) + ``` + + After: + + ``` + use Symfony\Component\Form\FormBuilderInterface; + + public function buildForm(FormBuilderInterface $builder, array $options) + ``` + + * The method `createBuilder` was removed from `FormTypeInterface` for performance + reasons. It is now not possible anymore to use custom implementations of + `FormBuilderInterface` for specific form types. + + If you are in such a situation, you can implement a custom `ResolvedFormTypeInterface` + where you create your own `FormBuilderInterface` implementation. You also need to + register a custom `ResolvedFormTypeFactoryInterface` implementation under the service + name "form.resolved_type_factory" in order to replace the default implementation. + + * If you previously inherited from `FieldType`, you should now inherit from + `FormType`. You should also set the option `compound` to `false` if your field + is not supposed to contain child fields. + + `FieldType` was deprecated and will be removed in Symfony 2.3. + + Before: + + ``` + public function getParent(array $options) + { + return 'field'; + } + ``` + + After: + + ``` + public function getParent() + { + return 'form'; + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'compound' => false, + )); + } + ``` + + The changed signature of `getParent()` is explained in the next step. + The new method `setDefaultOptions` is described in the section "Deprecations". + + * No options are passed to `getParent()` of `FormTypeInterface` anymore. If + you previously dynamically inherited from `FormType` or `FieldType`, you can now + dynamically set the "compound" option instead. + + Before: + + ``` + public function getParent(array $options) + { + return $options['expanded'] ? 'form' : 'field'; + } + ``` + + After: + + ``` + use Symfony\Component\OptionsResolver\OptionsResolverInterface; + use Symfony\Component\OptionsResolver\Options; + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $compound = function (Options $options) { + return $options['expanded']; + }; + + $resolver->setDefaults(array( + 'compound' => $compound, + )); + } + + public function getParent() + { + return 'form'; + } + ``` + + The new method `setDefaultOptions` is described in the section "Deprecations". + + * The "data_class" option now *must* be set if a form maps to an object. If + you leave it empty, the form will expect an array, an instance of \ArrayAccess + or a scalar value and fail with a corresponding exception. + + Likewise, if a form maps to an array or an instance of \ArrayAccess, the option + *must* be left null now. + + Form mapped to an instance of `Person`: + + ``` + use Symfony\Component\OptionsResolver\OptionsResolverInterface; + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'Acme\Demo\Person', + )); + } + ``` + + * The mapping of property paths to arrays has changed. + + Previously, a property path "street" mapped to both a field `$street` of + a class (or its accessors `getStreet()` and `setStreet()`) and an index + `['street']` of an array or an object implementing `\ArrayAccess`. + + Now, the property path "street" only maps to a class field (or accessors), + while the property path "[street]" only maps to indices. + + If you defined property paths manually in the "property_path" option, you + should revise them and adjust them if necessary. + + Before: + + ``` + $builder->add('name', 'text', array( + 'property_path' => 'address.street', + )); + ``` + + After (if the address object is an array): + + ``` + $builder->add('name', 'text', array( + 'property_path' => 'address[street]', + )); + ``` + + If address is an object in this case, the code given in "Before" + works without changes. + + * Form and field names must now start with a letter, digit or underscore + and only contain letters, digits, underscores, hyphens and colons. + + * In the collection type's template, the default name of the prototype field + has changed from `$$name$$` to `__name__`. + + You can now customize the name of the prototype field by changin the + "prototype_name" option. You are advised to prepend and append two + underscores wherever you specify a value for the field's "prototype_name" + option. + + ``` + $builder->add('tags', 'collection', array('prototype_name' => '__proto__')); + + // results in the name "__proto__" in the template + ``` + + * The "read_only" option now renders as `readonly="readonly"`, use + "disabled" instead for `disabled="disabled"`. + + * Child forms are no longer automatically validated. That means that you must + explicitly set the `Valid` constraint in your model if you want to validate + objects modified by child forms. + + If you don't want to set the `Valid` constraint, or if there is no reference + from the data of the parent form to the data of the child form, you can + enable BC behavior by setting the "cascade_validation" option to `true` + on the parent form. + +#### BC Breaks in Themes and HTML + + * FormType and FieldType were merged and require you to adapt your form + themes. + + The block `field_widget` and all references to it should be renamed to + `form_widget_simple`: + + Before: + + ``` + {% block url_widget %} + {% spaceless %} + {% set type = type|default('url') %} + {{ block('field_widget') }} + {% endspaceless %} + {% endblock url_widget %} + ``` + + After: + + ``` + {% block url_widget %} + {% spaceless %} + {% set type = type|default('url') %} + {{ block('form_widget_simple') }} + {% endspaceless %} + {% endblock url_widget %} + ``` + + All other `field_*` blocks and references to them should be renamed to + `form_*`. If you previously defined both a `field_*` and a `form_*` + block, you can merge them into a single `form_*` block and check the new + Boolean variable `compound` instead: + + Before: + + ``` + {% block form_errors %} + {% spaceless %} + ... form code ... + {% endspaceless %} + {% endblock form_errors %} + + {% block field_errors %} + {% spaceless %} + ... field code ... + {% endspaceless %} + {% endblock field_errors %} + ``` + + After: + + ``` + {% block form_errors %} + {% spaceless %} + {% if compound %} + ... form code ... + {% else %} + ... field code ... + {% endif %} + {% endspaceless %} + {% endblock form_errors %} + ``` + + Furthermore, the block `generic_label` was merged into `form_label`. You + should now override `form_label` in order to customize labels. + + Last but not least, the block `widget_choice_options` was renamed to + `choice_widget_options` to be consistent with the rest of the default + theme. + + * The strategy for generating the `id` and `name` HTML attributes for + checkboxes and radio buttons in a choice field has changed. + + Instead of appending the choice value, a generated integer is now appended + by default. Take care if your JavaScript relies on that. If you want to + read the actual choice value, read the `value` attribute instead. + + * In the choice field type's template, the `_form_is_choice_selected` method + used to identify a selected choice has been replaced with the `selectedchoice` + filter. Similarly, the `_form_is_choice_group` method used to check if a + choice is grouped has been removed and can be checked with the `iterable` + test. + + Before: + + ``` + {% for choice, label in choices %} + {% if _form_is_choice_group(label) %} + + {% for nestedChoice, nestedLabel in label %} + ... options tags ... + {% endfor %} + + {% else %} + + {% endif %} + {% endfor %} + ``` + + After: + + ``` + {% for label, choice in choices %} + {% if choice is iterable %} + + {% for nestedChoice, nestedLabel in choice %} + ... options tags ... + {% endfor %} + + {% else %} + + {% endif %} + {% endfor %} + ``` + + * Creation of default labels has been moved to the view layer. You will need + to incorporate this logic into any custom `form_label` templates to + accommodate those cases when the `label` option has not been explicitly + set. + + ``` + {% block form_label %} + {% if label is empty %} + {% set label = name|humanize %} + {% endif %} + + {# ... #} + + {% endblock %} + ```` + + * Custom styling of individual rows of a collection form has been removed for + performance reasons. Instead, all rows now have the same block name, where + the word "entry" replaces the previous occurrence of the row index. + + Before: + + ``` + {% block _author_tags_0_label %} + {# ... #} + {% endblock %} + + {% block _author_tags_1_label %} + {# ... #} + {% endblock %} + ``` + + After: + + ``` + {% block _author_tags_entry_label %} + {# ... #} + {% endblock %} + ``` + + * The method `renderBlock()` of the helper for the PHP Templating component was + renamed to `block()`. Its first argument is now expected to be a `FormView` + instance. + + Before: + + ``` + renderBlock('widget_attributes') ?> + ``` + + After: + + ``` + block($form, 'widget_attributes') ?> + ``` + +#### Other BC Breaks + + * The order of the first two arguments of the methods `createNamed` and + `createNamedBuilder` in `FormFactoryInterface` was reversed to be + consistent with the rest of the component. You should scan your code + for occurrences of these methods and reverse the parameters. + + Before: + + ``` + $form = $factory->createNamed('text', 'firstName'); + ``` + + After: + + ``` + $form = $factory->createNamed('firstName', 'text'); + ``` + + * The implementation of `ChoiceList` was changed heavily. As a result, + `ArrayChoiceList` was replaced. If you have custom classes that extend + this class, you must now extend `SimpleChoiceList` and pass choices + to the parent constructor. + + Before: + + ``` + class MyChoiceList extends ArrayChoiceList + { + protected function load() + { + parent::load(); + + // load choices + + $this->choices = $choices; + } + } + ``` + + After: + + ``` + class MyChoiceList extends SimpleChoiceList + { + public function __construct() + { + // load choices + + parent::__construct($choices); + } + } + ``` + + If you need to load the choices lazily -- that is, as soon as they are + accessed for the first time -- you can extend `LazyChoiceList` instead + and load the choices by overriding `loadChoiceList()`. + + ``` + class MyChoiceList extends LazyChoiceList + { + protected function loadChoiceList() + { + // load choices + + return new SimpleChoiceList($choices); + } + } + ``` + + `PaddedChoiceList`, `MonthChoiceList` and `TimezoneChoiceList` were removed. + Their functionality was merged into `DateType`, `TimeType` and `TimezoneType`. + + `EntityChoiceList` was adapted. The methods `getEntities()`, + `getEntitiesByKeys()`, `getIdentifier()` and `getIdentifierValues()` were + removed or made private. Instead of the first two, you can now use + `getChoices()` and `getChoicesByValues()`. For the latter two, no + replacement exists. + + * HTML attributes are now passed in the `label_attr` variable for the `form_label` function. + + Before: + + ``` + {{ form_label(form.name, 'Your Name', { 'attr': {'class': 'foo'} }) }} + ``` + + After: + + ``` + {{ form_label(form.name, 'Your Name', { 'label_attr': {'class': 'foo'} }) }} + ``` + + * `EntitiesToArrayTransformer` and `EntityToIdTransformer` were removed. + The former was replaced by `CollectionToArrayTransformer` in combination + with `EntityChoiceList`, the latter is not required in the core anymore. + + * The following transformers were renamed: + + * `ArrayToBooleanChoicesTransformer` to `ChoicesToBooleanArrayTransformer` + * `ScalarToBooleanChoicesTransformer` to `ChoiceToBooleanArrayTransformer` + * `ArrayToChoicesTransformer` to `ChoicesToValuesTransformer` + * `ScalarToChoiceTransformer` to `ChoiceToValueTransformer` + + to be consistent with the naming in `ChoiceListInterface`. + + * `FormUtil::toArrayKey()` and `FormUtil::toArrayKeys()` were removed. + They were merged into ChoiceList and have no public equivalent anymore. + + * The `add()`, `remove()`, `setParent()`, `bind()` and `setData()` methods in + the Form class now throw an exception if the form is already bound. + + If you used these methods on bound forms, you should consider moving your + logic to an event listener that observes `FormEvents::PRE_BIND` or + `FormEvents::BIND`. + +#### Deprecations + + * The following methods of `FormTypeInterface` and `FormTypeExtensionInterface` + are deprecated and will be removed in Symfony 2.3: + + * `getDefaultOptions` + * `getAllowedOptionValues` + + You should use the newly added `setDefaultOptions` instead, which gives you + access to the OptionsResolverInterface instance and with that a lot more power. + + Before: + + ``` + public function getDefaultOptions(array $options) + { + return array( + 'gender' => 'male', + ); + } + + public function getAllowedOptionValues(array $options) + { + return array( + 'gender' => array('male', 'female'), + ); + } + ``` + + After: + + ``` + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'gender' => 'male', + )); + + $resolver->setAllowedValues(array( + 'gender' => array('male', 'female'), + )); + } + ``` + + You can specify options that depend on other options using closures. + + Before: + + ``` + public function getDefaultOptions(array $options) + { + $defaultOptions = array(); + + if ($options['multiple']) { + $defaultOptions['empty_data'] = array(); + } + + return $defaultOptions; + } + ``` + + After: + + ``` + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'empty_data' => function (Options $options, $value) { + return $options['multiple'] ? array() : $value; + } + )); + } + ``` + + The second argument `$value` contains the current default value and + does not have to be specified if not needed. + + * The following methods in `FormBuilder` were deprecated and have a new + equivalent: + + * `prependClientTransformer`: `addViewTransformer`, with `true` as second argument + * `appendClientTransformer`: `addViewTransformer` + * `getClientTransformers`: `getViewTransformers` + * `resetClientTransformers`: `resetViewTransformers` + * `prependNormTransformer`: `addModelTransformer` + * `appendNormTransformer`: `addModelTransformer`, with `true` as second argument + * `getNormTransformers`: `getModelTransformers` + * `resetNormTransformers`: `resetModelTransformers` + + The deprecated methods will be removed in Symfony 2.3. You are advised to + update your application. + + Before: + + ``` + $builder->appendClientTransformer(new MyTransformer()); + ``` + + After: + + ``` + $builder->addViewTransformer(new MyTransformer()); + ``` + + * The following events were deprecated and have a new equivalent: + + * `FormEvents::SET_DATA`: `FormEvents::PRE_SET_DATA` + * `FormEvents::BIND_CLIENT_DATA`: `FormEvents::PRE_BIND` + * `FormEvents::BIND_NORM_DATA`: `FormEvents::BIND` + + The deprecated events will be removed in Symfony 2.3. + + Furthermore, the event classes `DataEvent` and `FilterDataEvent` were + deprecated and replaced by the generic `FormEvent`. You are advised to + code your listeners against the new event now. The deprecated events will + be removed in Symfony 2.3. + + Before: + + ``` + $builder->addListener(FormEvents::BIND_CLIENT_DATA, function (FilterDataEvent $event) { + // ... + }); + ``` + + After: + + ``` + $builder->addListener(FormEvents::PRE_BIND, function (FormEvent $event) { + // ... + }); + ``` + + * The interface `FormValidatorInterface` was deprecated and will be removed + in Symfony 2.3. + + If you implemented custom validators using this interface, you can + substitute them by event listeners listening to the `FormEvents::POST_BIND` + (or any other of the `*BIND` events). In case you used the CallbackValidator + class, you should now pass the callback directly to `addEventListener`. + + * The method `guessMinLength()` of `FormTypeGuesserInterface` was deprecated + and will be removed in Symfony 2.3. You should use the new method + `guessPattern()` instead which may return any regular expression that + is inserted in the HTML5 attribute `pattern`. + + Before: + + ``` + public function guessMinLength($class, $property) + { + if (/* condition */) { + return new ValueGuess($minLength, Guess::LOW_CONFIDENCE); + } + } + ``` + + After: + + ``` + public function guessPattern($class, $property) + { + if (/* condition */) { + return new ValueGuess('.{'.$minLength.',}', Guess::LOW_CONFIDENCE); + } + } + ``` + + * Setting the option "property_path" to `false` was deprecated and will be unsupported + as of Symfony 2.3. + + You should use the new option "mapped" instead in order to set that you don't want + a field to be mapped to its parent's data. + + Before: + + ``` + $builder->add('termsAccepted', 'checkbox', array( + 'property_path' => false, + )); + ``` + + After: + + ``` + $builder->add('termsAccepted', 'checkbox', array( + 'mapped' => false, + )); + ``` + + * The following methods in `Form` were deprecated and will be removed in + Symfony 2.3: + + * `getTypes` + * `getErrorBubbling` + * `getNormTransformers` + * `getClientTransformers` + * `getAttribute` + * `hasAttribute` + * `getClientData` + * `getChildren` + * `hasChildren` + * `bindRequest` + + Before: + + ``` + $form->getErrorBubbling() + ``` + + After: + + ``` + $form->getConfig()->getErrorBubbling(); + ``` + + The method `getClientData` has a new equivalent that is named `getViewData`. + You can access all other methods on the `FormConfigInterface` object instead. + + Instead of `getChildren` and `hasChildren`, you should now use `all` and + `count`. + + Before: + + ``` + if ($form->hasChildren()) { + ``` + + After: + + ``` + if (count($form) > 0) { + ``` + + Instead of `bindRequest`, you should now simply call `bind`: + + Before: + + ``` + $form->bindRequest($request); + ``` + + After: + + ``` + $form->bind($request); + ``` + + * The option "validation_constraint" was deprecated and will be removed + in Symfony 2.3. You should use the option "constraints" instead, + where you can pass one or more constraints for a form. + + Before: + + ``` + $builder->add('name', 'text', array( + 'validation_constraint' => new NotBlank(), + )); + ``` + + After: + + ``` + $builder->add('name', 'text', array( + 'constraints' => new NotBlank(), + )); + ``` + + Unlike previously, you can also pass a list of constraints now: + + ``` + $builder->add('name', 'text', array( + 'constraints' => array( + new NotBlank(), + new MinLength(3), + ), + )); + ``` + + Be aware that constraints will now only be validated if they belong + to the validated group! So if you validate a form in group "Custom" + and previously did: + + ``` + $builder->add('name', 'text', array( + 'validation_constraint' => new NotBlank(), + )); + ``` + + Then you need to add the constraint to the group "Custom" now: + + ``` + $builder->add('name', 'text', array( + 'constraints' => new NotBlank(array('groups' => 'Custom')), + )); + ``` + + * The options "data_timezone" and "user_timezone" in `DateType`, + `DateTimeType` and `TimeType` were deprecated and will be removed in + Symfony 2.3. They were renamed to "model_timezone" and "view_timezone". + + Before: + + ``` + $builder->add('scheduledFor', 'date', array( + 'data_timezone' => 'UTC', + 'user_timezone' => 'America/New_York', + )); + ``` + + After: + + ``` + $builder->add('scheduledFor', 'date', array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'America/New_York', + )); + ``` + + * The methods `addType`, `hasType` and `getType` in `FormFactory` are deprecated + and will be removed in Symfony 2.3. You should use the methods with the same + name on the `FormRegistry` instead. + + Before: + + ``` + $this->get('form.factory')->addType(new MyFormType()); + ``` + + After: + + ``` + $registry = $this->get('form.registry'); + + $registry->addType($registry->resolveType(new MyFormType())); + ``` + + * The following methods in class `FormView` were deprecated and will be + removed in Symfony 2.3: + + * `set` + * `has` + * `get` + * `all` + * `getVars` + * `addChild` + * `getChild` + * `getChildren` + * `removeChild` + * `hasChild` + * `hasChildren` + * `getParent` + * `hasParent` + * `setParent` + + You should access the public properties `vars`, `children` and `parent` + instead. + + Before: + + ``` + $view->set('help', 'A text longer than six characters'); + $view->set('error_class', 'max_length_error'); + ``` + + After: + + ``` + $view->vars = array_replace($view->vars, array( + 'help' => 'A text longer than six characters', + 'error_class' => 'max_length_error', + )); + ``` + + Before: + + ``` + echo $view->get('error_class'); + ``` + + After: + + ``` + echo $view->vars['error_class']; + ``` + + Before: + + ``` + if ($view->hasChildren()) { ... + ``` + + After: + + ``` + if (count($view->children)) { ... + ``` + +### Validator + + * The methods `setMessage()`, `getMessageTemplate()` and + `getMessageParameters()` in the `ConstraintValidator` class were deprecated and will + be removed in Symfony 2.3. + + If you have implemented custom validators, you should use the + `addViolation()` method on the `ExecutionContext` object instead. + + Before: + + ``` + public function isValid($value, Constraint $constraint) + { + // ... + if (!$valid) { + $this->setMessage($constraint->message, array( + '{{ value }}' => $value, + )); + + return false; + } + } + ``` + + After: + + ``` + public function isValid($value, Constraint $constraint) + { + // ... + if (!$valid) { + $this->context->addViolation($constraint->message, array( + '{{ value }}' => $value, + )); + + return false; + } + } + ``` + + * The method `setPropertyPath()` in the ExecutionContext class + was removed. + + You should use the `addViolationAtSubPath()` method on the + `ExecutionContext` object instead. + + Before: + + ``` + public function isPropertyValid(ExecutionContext $context) + { + // ... + $propertyPath = $context->getPropertyPath().'.property'; + $context->setPropertyPath($propertyPath); + $context->addViolation('Error Message', array(), null); + } + ``` + + After: + + ``` + public function isPropertyValid(ExecutionContext $context) + { + // ... + $context->addViolationAtSubPath('property', 'Error Message', array(), null); + + } + ``` + + * The method `isValid` of `ConstraintValidatorInterface` was renamed to + `validate` and its return value was dropped. + + `ConstraintValidator` still contains the deprecated `isValid` method and + forwards `validate` calls to `isValid` by default. This BC layer will be + removed in Symfony 2.3. You are advised to rename your methods. You should + also remove the return values, which have never been used by the framework. + + Before: + + ``` + public function isValid($value, Constraint $constraint) + { + // ... + if (!$valid) { + $this->context->addViolation($constraint->message, array( + '{{ value }}' => $value, + )); + + return false; + } + } + ``` + + After: + + ``` + public function validate($value, Constraint $constraint) + { + // ... + if (!$valid) { + $this->context->addViolation($constraint->message, array( + '{{ value }}' => $value, + )); + + return; + } + } + ``` + + * Core translation messages changed. A dot is added at the end of each message. + Overwritten core translations need to be fixed. + + * Collections (arrays or instances of `\Traversable`) in properties + annotated with `Valid` are not traversed recursively by default anymore. + + This means that if a collection contains an entry which is again a + collection, the inner collection won't be traversed anymore as it + happened before. You can set the BC behavior by setting the new property + `deep` of `Valid` to `true`. + + Before: + + ``` + /** @Assert\Valid */ + private $recursiveCollection; + ``` + + After: + + ``` + /** @Assert\Valid(deep = true) */ + private $recursiveCollection; + ``` + + * The `Size`, `Min` and `Max` constraints were deprecated and will be removed in + Symfony 2.3. You should use the new constraint `Range` instead. + + Before: + + ``` + /** @Assert\Size(min = 2, max = 16) */ + private $numberOfCpus; + ``` + + After: + + ``` + /** @Assert\Range(min = 2, max = 16) */ + private $numberOfCpus; + ``` + + Before: + + ``` + /** @Assert\Min(2) */ + private $numberOfCpus; + ``` + + After: + + ``` + /** @Assert\Range(min = 2) */ + private $numberOfCpus; + ``` + + * The `MinLength` and `MaxLength` constraints were deprecated and will be + removed in Symfony 2.3. You should use the new constraint `Length` instead. + + Before: + + ``` + /** @Assert\MinLength(8) */ + private $password; + ``` + + After: + + ``` + /** @Assert\Length(min = 8) */ + private $password; + ``` + + * The classes `ValidatorContext` and `ValidatorFactory` were deprecated and + will be removed in Symfony 2.3. You should use the new entry point + `Validation` instead. + + Before: + + ``` + $validator = ValidatorFactory::buildDefault(array('path/to/mapping.xml')) + ->getValidator(); + ``` + + After: + + ``` + $validator = Validation::createValidatorBuilder() + ->addXmlMapping('path/to/mapping.xml') + ->getValidator(); + ``` + +### Session + + * The namespace of the Session class changed from `Symfony\Component\HttpFoundation\Session` + to `Symfony\Component\HttpFoundation\Session\Session`. + + * Using `get` to retrieve flash messages now returns an array. + + ##### Retrieving the flash messages from a Twig template + + Before: + + ``` + {% if app.session.hasFlash('notice') %} +
    + {{ app.session.getFlash('notice') }} +
    + {% endif %} + ``` + After: + + ``` + {% for flashMessage in app.session.flashbag.get('notice') %} +
    + {{ flashMessage }} +
    + {% endfor %} + ``` + + You can process all flash messages in a single loop with: + + ``` + {% for type, flashMessages in app.session.flashbag.all() %} + {% for flashMessage in flashMessages %} +
    + {{ flashMessage }} +
    + {% endfor %} + {% endfor %} + ``` + + * Session handler drivers should implement `\SessionHandlerInterface` or extend from + `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeHandlerInterface` base class and renamed + to `Handler\FooSessionHandler`. E.g. `PdoSessionStorage` becomes `Handler\PdoSessionHandler`. + + * Refactor code using `$session->*flash*()` methods to use `$session->getFlashBag()->*()`. + +### Serializer + + * The key names created by the `GetSetMethodNormalizer` have changed from + all lowercased to camelCased (e.g. `mypropertyvalue` to `myPropertyValue`). + + * The `item` element is now converted to an array when deserializing XML. + + ``` xml + + + <![CDATA[title1]]><![CDATA[title2]]> + + ``` + + Before: + + Array() + + After: + + Array( + [item] => Array( + [0] => Array( + [title] => title1 + ) + [1] => Array( + [title] => title2 + ) + ) + ) + +### Routing + + * The UrlMatcher urldecodes the route parameters only once, they were + decoded twice before. Note that the `urldecode()` calls have been changed for a + single `rawurldecode()` in order to support `+` for input paths. + + * Two new parameters have been added to the DIC: `router.request_context.host` + and `router.request_context.scheme`. You can customize them for your + functional tests or for generating urls with the right host and scheme + when your are in the cli context. + +### FrameworkBundle + + * 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 + + Before: + + ``` + framework: + session: + lifetime: 3600 + path: \ + domain: example.com + secure: true + httponly: true + ``` + + After: + + ``` + framework: + session: + cookie_lifetime: 3600 + cookie_path: \ + cookie_domain: example.com + cookie_secure: true + cookie_httponly: true + ``` + +Added `handler_id`, defaults to `session.handler.native_file`. + + ``` + framework: + session: + storage_id: session.storage.native + handler_id: session.handler.native_file + ``` + +To use mock session storage use the following. `handler_id` is irrelevant in this context. + + ``` + framework: + session: + storage_id: session.storage.mock_file + ``` + +### WebProfilerBundle + + * You must clear old profiles after upgrading to 2.1. If you are using a + database then you will need to remove the table. diff --git a/vendor/symfony/symfony/UPGRADE-2.2.md b/vendor/symfony/symfony/UPGRADE-2.2.md new file mode 100644 index 0000000..7900a47 --- /dev/null +++ b/vendor/symfony/symfony/UPGRADE-2.2.md @@ -0,0 +1,647 @@ +UPGRADE FROM 2.1 to 2.2 +======================= + +### TwigBridge + + * The `render` tag signature and arguments changed. + + Before: + + ``` + {% render 'BlogBundle:Post:list' with { 'limit': 2 }, { 'alt': 'BlogBundle:Post:error' } %} + ``` + + After: + + ``` + {% render controller('BlogBundle:Post:list', { 'limit': 2 }), { 'alt': 'BlogBundle:Post:error' } %} + {# Or: #} + {{ render(controller('BlogBundle:Post:list', { 'limit': 2 }), { 'alt': 'BlogBundle:Post:error'}) }} + ``` + + Note: The function is the preferred way. + +#### Deprecations + + * The `standalone` option is deprecated and will replaced with the `strategy` option in 2.3. + * The values `true`, `false`, `js` for the `standalone` option were deprecated and replaced respectively with the `esi`, `inline`, `hinclude` in 2.3. + + + Before: + + ``` + {% render 'BlogBundle:Post:list' with { 'limit': 2 }, {'standalone': true} %} + {% render 'BlogBundle:Post:list' with { 'limit': 2 }, {'standalone': false} %} + {% render 'BlogBundle:Post:list' with { 'limit': 2 }, {'standalone': 'js'} %} + ``` + + After: + + ``` + {{ render(controller('BlogBundle:Post:list', { 'limit': 2 }), { 'strategy': 'esi'}) }} + {{ render(controller('BlogBundle:Post:list', { 'limit': 2 }), { 'strategy': 'inline'}) }} + {{ render(controller('BlogBundle:Post:list', { 'limit': 2 }), { 'strategy': 'hinclude'}) }} + ``` + + +### HttpFoundation + + * The MongoDbSessionHandler default field names and timestamp type have changed. + + The `sess_` prefix was removed from default field names. The session ID is + now stored in the `_id` field by default. The session date is now stored as a + `MongoDate` instead of `MongoTimestamp`, which also makes it possible to use + TTL collections in MongoDB 2.2+ instead of relying on the `gc()` method. + + * The Stopwatch functionality was moved from HttpKernel\Debug to its own component + + * The _method request parameter support has been disabled by default (call + Request::enableHttpMethodParameterOverride() to enable it). + +#### Deprecations + + * The `Request::splitHttpAcceptHeader()` is deprecated and will be removed in 2.3. + + You should now use the `AcceptHeader` class which give you fluent methods to + parse request accept-* headers. Some examples: + + ``` + $accept = AcceptHeader::fromString($request->headers->get('Accept')); + if ($accept->has('text/html') { + $item = $accept->get('html'); + $charset = $item->getAttribute('charset', 'utf-8'); + $quality = $item->getQuality(); + } + + // accepts items are sorted by descending quality + $accepts = AcceptHeader::fromString($request->headers->get('Accept'))->all(); + + ``` + +### Form + + * The PasswordType is now not trimmed by default. + + * The class FormException is now an interface. The old class is still available + under the name Symfony\Component\Form\Exception\Exception, but will probably + be removed before 2.2. If you created FormException instances manually, + you are now advised to create any of the other exceptions in the + Symfony\Component\Form\Exception namespace or to create custom exception + classes for your purpose. + + * Translating validation errors is now optional. You can still do so + manually if you like, or you can simplify your templates to simply output + the already translated message. + + Before: + + ``` + {{ + error.messagePluralization is null + ? error.messageTemplate|trans(error.messageParameters, 'validators') + : error.messageTemplate|transchoice(error.messagePluralization, error.messageParameters, 'validators') + }} + ``` + + After: + + ``` + {{ error.message }} + ``` + + * FormType, ModelType and PropertyPathMapper now have constructors. If you + extended these classes, you should call the parent constructor now. + Note that you are not recommended to extend FormType nor ModelType. You should + extend AbstractType instead and use the Form component's own inheritance + mechanism (`AbstractType::getParent()`). + + Before: + + ``` + use Symfony\Component\Form\Extensions\Core\DataMapper\PropertyPathMapper; + + class CustomMapper extends PropertyPathMapper + { + public function __construct() + { + // ... + } + + // ... + } + ``` + + After: + + ``` + use Symfony\Component\Form\Extensions\Core\DataMapper\PropertyPathMapper; + + class CustomMapper extends PropertyPathMapper + { + public function __construct() + { + parent::__construct(); + + // ... + } + + // ... + } + ``` + +#### Deprecations + + * The methods `getParent()`, `setParent()` and `hasParent()` in + `FormBuilderInterface` were deprecated and will be removed in Symfony 2.3. + You should not rely on these methods in your form type because the parent + of a form can change after building it. + + * The class PropertyPath and related classes were deprecated and moved to a + dedicated component PropertyAccess. If you used any of these classes or + interfaces, you should adapt the namespaces now. During the move, + InvalidPropertyException was renamed to NoSuchPropertyException. + + Before: + + ``` + use Symfony\Component\Form\Util\PropertyPath; + use Symfony\Component\Form\Util\PropertyPathBuilder; + use Symfony\Component\Form\Util\PropertyPathInterface; + use Symfony\Component\Form\Util\PropertyPathIterator; + use Symfony\Component\Form\Util\PropertyPathIteratorInterface; + use Symfony\Component\Form\Exception\InvalidPropertyException; + use Symfony\Component\Form\Exception\InvalidPropertyPathException; + use Symfony\Component\Form\Exception\PropertyAccessDeniedException; + ``` + + After: + + ``` + use Symfony\Component\PropertyAccess\PropertyPath; + use Symfony\Component\PropertyAccess\PropertyPathBuilder; + use Symfony\Component\PropertyAccess\PropertyPathInterface; + use Symfony\Component\PropertyAccess\PropertyPathIterator; + use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface; + use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; + use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException; + use Symfony\Component\PropertyAccess\Exception\PropertyAccessDeniedException; + ``` + + Also, `FormUtil::singularify()` was split away into a class StringUtil + in the new component. + + Before: + + ``` + use Symfony\Component\Form\Util\FormUtil; + + $singular = FormUtil::singularify($plural); + ``` + + After: + + ``` + use Symfony\Component\PropertyAccess\StringUtil; + + $singular = StringUtil::singularify($plural); + ``` + + The methods `getValue()` and `setValue()` were moved to a new class + PropertyAccessor. + + Before: + + ``` + use Symfony\Component\Form\Util\PropertyPath; + + $propertyPath = new PropertyPath('some.path'); + + $value = $propertyPath->getValue($object); + $propertyPath->setValue($object, 'new value'); + ``` + + After (alternative 1): + + ``` + use Symfony\Component\PropertyAccess\PropertyAccess; + + $propertyAccessor = PropertyAccess::getPropertyAccessor(); + + $value = $propertyAccessor->getValue($object, 'some.path'); + $propertyAccessor->setValue($object, 'some.path', 'new value'); + ``` + + After (alternative 2): + + ``` + use Symfony\Component\PropertyAccess\PropertyAccess; + use Symfony\Component\PropertyAccess\PropertyPath; + + $propertyAccessor = PropertyAccess::getPropertyAccessor(); + $propertyPath = new PropertyPath('some.path'); + + $value = $propertyAccessor->getValue($object, $propertyPath); + $propertyAccessor->setValue($object, $propertyPath, 'new value'); + ``` + +### Routing + + * RouteCollection does not behave like a tree structure anymore but as a flat + array of Routes. So when using PHP to build the RouteCollection, you must + make sure to add routes to the sub-collection before adding it to the parent + collection (this is not relevant when using YAML or XML for Route definitions). + + Before: + + ``` + $rootCollection = new RouteCollection(); + $subCollection = new RouteCollection(); + $rootCollection->addCollection($subCollection); + $subCollection->add('foo', new Route('/foo')); + ``` + + After: + + ``` + $rootCollection = new RouteCollection(); + $subCollection = new RouteCollection(); + $subCollection->add('foo', new Route('/foo')); + $rootCollection->addCollection($subCollection); + ``` + + Also one must call `addCollection` from the bottom to the top hierarchy. + So the correct sequence is the following (and not the reverse): + + ``` + $childCollection->addCollection($grandchildCollection); + $rootCollection->addCollection($childCollection); + ``` + + * The methods `RouteCollection::getParent()` and `RouteCollection::getRoot()` + have been deprecated and will be removed in Symfony 2.3. + * Misusing the `RouteCollection::addPrefix` method to add defaults, requirements + or options without adding a prefix is not supported anymore. So if you called `addPrefix` + with an empty prefix or `/` only (both have no relevance), like + `addPrefix('', $defaultsArray, $requirementsArray, $optionsArray)` + you need to use the new dedicated methods `addDefaults($defaultsArray)`, + `addRequirements($requirementsArray)` or `addOptions($optionsArray)` instead. + * The `$options` parameter to `RouteCollection::addPrefix()` has been deprecated + because adding options has nothing to do with adding a path prefix. If you want to add options + to all child routes of a RouteCollection, you can use `addOptions()`. + * The method `RouteCollection::getPrefix()` has been deprecated + because it suggested that all routes in the collection would have this prefix, which is + not necessarily true. On top of that, since there is no tree structure anymore, this method + is also useless. + * `RouteCollection::addCollection(RouteCollection $collection)` should now only be + used with a single parameter. The other params `$prefix`, `$default`, `$requirements` and `$options` + will still work, but have been deprecated. The `addPrefix` method should be used for this + use-case instead. + Before: `$parentCollection->addCollection($collection, '/prefix', array(...), array(...))` + After: + ``` + $collection->addPrefix('/prefix', array(...), array(...)); + $parentCollection->addCollection($collection); + ``` + +### Validator + + * Interfaces were created for the classes `ConstraintViolation`, + `ConstraintViolationList`, `GlobalExecutionContext` and `ExecutionContext`. + If you type hinted against any of these classes, you are recommended to + type hint against their interfaces now. + + Before: + + ``` + use Symfony\Component\Validator\ExecutionContext; + + public function validateCustomLogic(ExecutionContext $context) + ``` + + After: + + ``` + use Symfony\Component\Validator\ExecutionContextInterface; + + public function validateCustomLogic(ExecutionContextInterface $context) + ``` + + For all implementations of `ConstraintValidatorInterface`, this change is + mandatory for the `initialize` method: + + Before: + + ``` + use Symfony\Component\Validator\ConstraintValidatorInterface; + use Symfony\Component\Validator\ExecutionContext; + + class MyValidator implements ConstraintValidatorInterface + { + public function initialize(ExecutionContext $context) + { + // ... + } + } + ``` + + After: + + ``` + use Symfony\Component\Validator\ConstraintValidatorInterface; + use Symfony\Component\Validator\ExecutionContextInterface; + + class MyValidator implements ConstraintValidatorInterface + { + public function initialize(ExecutionContextInterface $context) + { + // ... + } + } + ``` + + * The sources of the pluralized messages in translation files have changed + from the singular to the pluralized version. If you created custom + translation files for validator errors, you should adapt them. + + Before: + + + You must select at least {{ limit }} choices. + Sie müssen mindestens {{ limit }} Möglichkeit wählen.|Sie müssen mindestens {{ limit }} Möglichkeiten wählen. + + + After: + + + You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices. + Sie müssen mindestens {{ limit }} Möglichkeit wählen.|Sie müssen mindestens {{ limit }} Möglichkeiten wählen. + + + Check the file src/Symfony/Component/Validator/Resources/translations/validators.en.xlf + for the new message sources. + +#### Deprecations + + * The interface `ClassMetadataFactoryInterface` was deprecated and will be + removed in Symfony 2.3. You should implement `MetadataFactoryInterface` + instead, which changes the name of the method `getClassMetadata` to + `getMetadataFor` and accepts arbitrary values (e.g. class names, objects, + numbers etc.). In your implementation, you should throw a + `NoSuchMetadataException` if you don't support metadata for the given value. + + Before: + + ``` + use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface; + + class MyMetadataFactory implements ClassMetadataFactoryInterface + { + public function getClassMetadata($class) + { + // ... + } + } + ``` + + After: + + ``` + use Symfony\Component\Validator\MetadataFactoryInterface; + use Symfony\Component\Validator\Exception\NoSuchMetadataException; + + class MyMetadataFactory implements MetadataFactoryInterface + { + public function getMetadataFor($value) + { + if (is_object($value)) { + $value = get_class($value); + } + + if (!is_string($value) || (!class_exists($value) && !interface_exists($value))) { + throw new NoSuchMetadataException(...); + } + + // ... + } + } + ``` + + The return value of `ValidatorInterface::getMetadataFactory()` was also + changed to return `MetadataFactoryInterface`. Make sure to replace calls to + `getClassMetadata` by `getMetadataFor` on the return value of this method. + + Before: + + ``` + $metadataFactory = $validator->getMetadataFactory(); + $metadata = $metadataFactory->getClassMetadata('Vendor\MyClass'); + ``` + + After: + + ``` + $metadataFactory = $validator->getMetadataFactory(); + $metadata = $metadataFactory->getMetadataFor('Vendor\MyClass'); + ``` + + * The class `GraphWalker` and the accessor `ExecutionContext::getGraphWalker()` + were deprecated and will be removed in Symfony 2.3. You should use the + methods `ExecutionContextInterface::validate()` and + `ExecutionContextInterface::validateValue()` instead. + + Before: + + ``` + use Symfony\Component\Validator\ExecutionContext; + + public function validateCustomLogic(ExecutionContext $context) + { + if (/* ... */) { + $path = $context->getPropertyPath(); + $group = $context->getGroup(); + + if (!empty($path)) { + $path .= '.'; + } + + $context->getGraphWalker()->walkReference($someObject, $group, $path.'myProperty', false); + } + } + ``` + + After: + + ``` + use Symfony\Component\Validator\ExecutionContextInterface; + + public function validateCustomLogic(ExecutionContextInterface $context) + { + if (/* ... */) { + $context->validate($someObject, 'myProperty'); + } + } + ``` + + * The method `ExecutionContext::addViolationAtSubPath()` was deprecated and + will be removed in Symfony 2.3. You should use `addViolationAt()` instead. + + Before: + + ``` + use Symfony\Component\Validator\ExecutionContext; + + public function validateCustomLogic(ExecutionContext $context) + { + if (/* ... */) { + $context->addViolationAtSubPath('myProperty', 'This value is invalid'); + } + } + ``` + + After: + + ``` + use Symfony\Component\Validator\ExecutionContextInterface; + + public function validateCustomLogic(ExecutionContextInterface $context) + { + if (/* ... */) { + $context->addViolationAt('myProperty', 'This value is invalid'); + } + } + ``` + + * The methods `ExecutionContext::getCurrentClass()`, `ExecutionContext::getCurrentProperty()` + and `ExecutionContext::getCurrentValue()` were deprecated and will be removed + in Symfony 2.3. Use the methods `getClassName()`, `getPropertyName()` and + `getValue()` instead. + + Before: + + ``` + use Symfony\Component\Validator\ExecutionContext; + + public function validateCustomLogic(ExecutionContext $context) + { + $class = $context->getCurrentClass(); + $property = $context->getCurrentProperty(); + $value = $context->getCurrentValue(); + + // ... + } + ``` + + After: + + ``` + use Symfony\Component\Validator\ExecutionContextInterface; + + public function validateCustomLogic(ExecutionContextInterface $context) + { + $class = $context->getClassName(); + $property = $context->getPropertyName(); + $value = $context->getValue(); + + // ... + } + ``` + +### FrameworkBundle + + * The `render` method of the `actions` templating helper signature and arguments changed: + + Before: + + ``` + render('BlogBundle:Post:list', array('limit' => 2), array('alt' => 'BlogBundle:Post:error')) ?> + ``` + + After: + + ``` + render($view['router']->generate('post_list', array('limit' => 2)), array('alt' => 'BlogBundle:Post:error')) ?> + ``` + + where `post_list` is the route name for the `BlogBundle:Post:list` + controller, or if you don't want to create a route: + + ``` + render(new ControllerReference('BlogBundle:Post:list', array('limit' => 2)), array('alt' => 'BlogBundle:Post:error')) ?> + ``` + +#### Configuration + + * The 2.2 version introduces a new parameter `trusted_proxies` that replaces + `trust_proxy_headers` in the framework configuration. + + Before: + + ``` + # app/config/config.yml + framework: + trust_proxy_headers: false + ``` + + After: + + ``` + # app/config/config.yml + framework: + trusted_proxies: ['127.0.0.1', '10.0.0.1'] # a list of proxy IPs you trust + ``` + +### Security + + * The existing ``UserPassword`` validator constraint class has been modified. + Its namespace has been changed to better fit the Symfony coding conventions. + + Before: + + ``` + use Symfony\Component\Security\Core\Validator\Constraint\UserPassword; + ``` + + After: (note the `s` at the end of `Constraint`) + + ``` + use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; + ``` + + * The new ``UserPassword`` validator constraint class now accepts a new + ``service`` option that allows to specify a custom validator service name in + order to validate the current logged-in user's password. + + ``` + use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; + + $constraint = new UserPassword(array( + 'service' => 'my.custom.validator.user_password', + )); + ``` + +#### Deprecations + + * The two previous ``UserPassword`` and ``UserPasswordValidator`` classes in + the ``Symfony\Component\Security\Core\Validator\Constraint`` namespace have + been deprecated and will be removed in 2.3. + + Before: + + ``` + use Symfony\Component\Security\Core\Validator\Constraint\UserPassword; + use Symfony\Component\Security\Core\Validator\Constraint\UserPasswordValidator; + ``` + + After: + + ``` + use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; + use Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator; + ``` + +### Serializer + + * All serializer interfaces (Serializer, Normalizer, Encoder) have been + extended with an optional `$context` array. This was necessary to allow for + more complex use-cases that require context information during the + (de)normalization and en-/decoding steps. diff --git a/vendor/symfony/symfony/UPGRADE-2.3.md b/vendor/symfony/symfony/UPGRADE-2.3.md new file mode 100644 index 0000000..02b36e6 --- /dev/null +++ b/vendor/symfony/symfony/UPGRADE-2.3.md @@ -0,0 +1,279 @@ +UPGRADE FROM 2.2 to 2.3 +======================= + +Form +---- + + * Although this was not officially supported nor documented, it was possible to + set the option "validation_groups" to false, resulting in the group "Default" + being validated. Now, if you set "validation_groups" to false, the validation + of a form will be skipped (except for a few integrity checks on the form). + + If you want to validate a form in group "Default", you should either + explicitly set "validation_groups" to "Default" or alternatively set it to + null. + + Before: + + ``` + // equivalent notations for validating in group "Default" + "validation_groups" => null + "validation_groups" => "Default" + "validation_groups" => false + + // notation for skipping validation + "validation_groups" => array() + ``` + + After: + + ``` + // equivalent notations for validating in group "Default" + "validation_groups" => null + "validation_groups" => "Default" + + // equivalent notations for skipping validation + "validation_groups" => false + "validation_groups" => array() + ``` + * The array type hint from DataMapperInterface was removed. You should adapt + implementations of that interface accordingly. + + Before: + + ``` + use Symfony\Component\Form\DataMapperInterface; + + class MyDataMapper + { + public function mapFormsToData(array $forms, $data) + { + // ... + } + + public function mapDataToForms($data, array $forms) + { + // ... + } + } + ``` + + After: + + ``` + use Symfony\Component\Form\DataMapperInterface; + + class MyDataMapper + { + public function mapFormsToData($forms, $data) + { + // ... + } + + public function mapDataToForms($data, $forms) + { + // ... + } + } + ``` + + Instead of an array, the methods here are now passed a + RecursiveIteratorIterator containing an InheritDataAwareIterator by default, + so you don't need to handle forms inheriting their parent data (former + "virtual forms") in the data mapper anymore. + + Before: + + ``` + use Symfony\Component\Form\Util\VirtualFormAwareIterator; + + public function mapFormsToData(array $forms, $data) + { + $iterator = new \RecursiveIteratorIterator( + new VirtualFormAwareIterator($forms) + ); + + foreach ($iterator as $form) { + // ... + } + } + ``` + + After: + + ``` + public function mapFormsToData($forms, $data) + { + foreach ($forms as $form) { + // ... + } + } + ``` + + * The *_SET_DATA events are now guaranteed to be fired *after* the children + were added by the FormBuilder (unless setData() is called manually). Before, + the *_SET_DATA events were sometimes thrown before adding child forms, + which made it impossible to remove child forms dynamically. + + A consequence of this change is that you need to set the "auto_initialize" + option to `false` for `FormInterface` instances that you pass to + `FormInterface::add()`: + + Before: + + ``` + $form = $factory->create('form'); + $form->add($factory->createNamed('field', 'text')); + ``` + + This code will now throw an exception with the following message: + + Automatic initialization is only supported on root forms. You should set the + "auto_initialize" option to false on the field "field". + + Consequently, you need to set the "auto_initialize" option: + + After (Alternative 1): + + ``` + $form = $factory->create('form'); + $form->add($factory->createNamed('field', 'text', array( + 'auto_initialize' => false, + ))); + ``` + + The problem also disappears if you work with `FormBuilder` instances instead + of `Form` instances: + + After (Alternative 2): + + ``` + $builder = $factory->createBuilder('form'); + $builder->add($factory->createBuilder('field', 'text')); + $form = $builder->getForm(); + ``` + + The best solution is in most cases to let `add()` handle the field creation: + + After (Alternative 3): + + ``` + $form = $factory->create('form'); + $form->add('field', 'text'); + ``` + + After (Alternative 4): + + ``` + $builder = $factory->createBuilder('form'); + $builder->add('field', 'text'); + $form = $builder->getForm(); + ``` + +PropertyAccess +-------------- + + * PropertyAccessor was changed to continue its search for a property or method + even if a non-public match was found. This means that the property "author" + in the following class will now correctly be found: + + ``` + class Article + { + public $author; + + private function getAuthor() + { + // ... + } + } + ``` + + Although this is uncommon, similar cases exist in practice. + + Instead of the PropertyAccessDeniedException that was thrown here, the more + generic NoSuchPropertyException is thrown now if no public property nor + method are found by the PropertyAccessor. PropertyAccessDeniedException was + removed completely. + + Before: + + ``` + use Symfony\Component\PropertyAccess\Exception\PropertyAccessDeniedException; + use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; + + try { + $value = $accessor->getValue($article, 'author'); + } catch (PropertyAccessDeniedException $e) { + // Method/property was found but not public + } catch (NoSuchPropertyException $e) { + // Method/property was not found + } + ``` + + After: + + ``` + use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; + + try { + $value = $accessor->getValue($article, 'author'); + } catch (NoSuchPropertyException $e) { + // Method/property was not found or not public + } + ``` + +DomCrawler +---------- + + * `Crawler::each()` and `Crawler::reduce()` now return Crawler instances + instead of DomElement instances: + + Before: + + ``` + $data = $crawler->each(function ($node, $i) { + return $node->nodeValue; + }); + ``` + + After: + + ``` + $data = $crawler->each(function ($crawler, $i) { + return $crawler->text(); + }); + ``` + +Console +------- + + * New verbosity levels have been added, therefore if you used to do check + the output verbosity level directly for VERBOSITY_VERBOSE you probably + want to update it to a greater than comparison: + + Before: + + ``` + if (OutputInterface::VERBOSITY_VERBOSE === $output->getVerbosity()) { ... } + ``` + + After: + + ``` + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { ... } + ``` + +BrowserKit +---------- + + * If you are receiving responses with non-3xx Status Code and Location header + please be aware that you won't be able to use auto-redirects on these kind + of responses. + + If you are correctly passing 3xx Status Code with Location header, you + don't have to worry about the change. + + If you were using responses with Location header and non-3xx Status Code, + you have to update your code to manually create another request to URL + grabbed from the Location header. \ No newline at end of file diff --git a/vendor/symfony/symfony/UPGRADE-3.0.md b/vendor/symfony/symfony/UPGRADE-3.0.md new file mode 100644 index 0000000..a2c1d74 --- /dev/null +++ b/vendor/symfony/symfony/UPGRADE-3.0.md @@ -0,0 +1,425 @@ +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: + + * `registerNamespaces()` -> `addPrefixes()` + * `registerPrefixes()` -> `addPrefixes()` + * `registerNamespaces()` -> `addPrefix()` + * `registerPrefixes()` -> `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. + +### Form + + * The methods `Form::bind()` and `Form::isBound()` were removed. You should + use `Form::submit()` and `Form::isSubmitted()` instead. + + Before: + + ``` + $form->bind(array(...)); + ``` + + After: + + ``` + $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: + + ``` + if ('POST' === $request->getMethod()) { + $form->bind($request); + + if ($form->isValid()) { + // ... + } + } + ``` + + After: + + ``` + $form->handleRequest($request); + + if ($form->isValid()) { + // ... + } + ``` + + If you want to test whether the form was submitted separately, you can use + the method `isSubmitted()`: + + ``` + $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: + + ``` + $builder->addEventListener(FormEvents::PRE_BIND, function (FormEvent $event) { + // ... + }); + ``` + + After: + + ``` + $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { + // ... + }); + ``` + + * The option "virtual" was renamed to "inherit_data". + + Before: + + ``` + $builder->add('address', 'form', array( + 'virtual' => true, + )); + ``` + + After: + + ``` + $builder->add('address', 'form', array( + 'inherit_data' => true, + )); + ``` + + * The class VirtualFormAwareIterator was renamed to InheritDataAwareIterator. + + Before: + + ``` + use Symfony\Component\Form\Util\VirtualFormAwareIterator; + + $iterator = new VirtualFormAwareIterator($forms); + ``` + + After: + + ``` + 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: + + ``` + use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase + + class MyTypeTest extends TypeTestCase + { + // ... + } + ``` + + After: + + ``` + use Symfony\Component\Form\Test\TypeTestCase; + + class MyTypeTest extends TypeTestCase + { + // ... + } + ``` + + * The `FormItegrationTestCase` and `FormPerformanceTestCase` classes were moved form the `Symfony\Component\Form\Tests` namespace to the `Symfony\Component\Form\Test` namespace. + + +### FrameworkBundle + + * The `enctype` method of the `form` helper was removed. You should use the + new method `start` instead. + + Before: + + ``` +
    enctype($form) ?>> + ... +
    + ``` + + After: + + ``` + 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: + + ``` + $form = $this->createForm('my_form', $formData, array( + 'method' => 'PUT', + 'action' => $this->generateUrl('target_route'), + )); + ``` + + Alternative 2: + + ``` + $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: + + ``` + start($form, array('method' => 'GET', 'action' => 'http://example.com')) ?> + ... + end($form) ?> + ``` + +### 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: + + * `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: + + * `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` + +### 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: + + * `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: + + ``` + use Symfony\Component\PropertyAccess\PropertyAccess; + + $accessor = PropertyAccess::getPropertyAccessor(); + ``` + + After: + + ``` + 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: + + ``` + article_edit: + pattern: /article/{id} + requirements: { '_method': 'POST|PUT', '_scheme': 'https', 'id': '\d+' } + + + POST|PUT + https + \d+ + + + $route = new Route(); + $route->setPattern('/article/{id}'); + $route->setRequirement('_method', 'POST|PUT'); + $route->setRequirement('_scheme', 'https'); + ``` + + After: + + ``` + article_edit: + path: /article/{id} + methods: [POST, PUT] + schemes: https + requirements: { 'id': '\d+' } + + + \d+ + + + $route = new Route(); + $route->setPath('/article/{id}'); + $route->setMethods(array('POST', 'PUT')); + $route->setSchemes('https'); + ``` + +### Translator + + * The `Translator::setFallbackLocale()` method has been removed in favor of + `Translator::setFallbackLocales()`. + +### Twig Bridge + + * 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: + + ``` +
    + ... +
    + ``` + + After: + + ``` + {{ 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: + + ``` + $form = $this->createForm('my_form', $formData, array( + 'method' => 'PUT', + 'action' => $this->generateUrl('target_route'), + )); + ``` + + Alternative 2: + + ``` + $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: + + ``` + {{ form_start(form, {'method': 'GET', 'action': 'http://example.com'}) }} + ... + {{ form_end(form) }} + ``` + +### Validator + + * The constraints `Optional` and `Required` were moved to the + `Symfony\Component\Validator\Constraints\` namespace. You should adapt + the path wherever you used them. + + Before: + + ``` + use Symfony\Component\Validator\Constraints as Assert; + + /** + * @Assert\Collection({ + * "foo" = @Assert\Collection\Required(), + * "bar" = @Assert\Collection\Optional(), + * }) + */ + private $property; + ``` + + After: + + ``` + use Symfony\Component\Validator\Constraints as Assert; + + /** + * @Assert\Collection({ + * "foo" = @Assert\Required(), + * "bar" = @Assert\Optional(), + * }) + */ + private $property; + ``` + +### Yaml + + * The ability to pass file names to `Yaml::parse()` has been removed. + + Before: + + ``` + Yaml::parse($fileName); + ``` + + After: + + ``` + Yaml::parse(file_get_contents($fileName)); + ``` diff --git a/vendor/symfony/symfony/autoload.php.dist b/vendor/symfony/symfony/autoload.php.dist new file mode 100644 index 0000000..0bb2bc3 --- /dev/null +++ b/vendor/symfony/symfony/autoload.php.dist @@ -0,0 +1,15 @@ +=') && gc_enabled()) { + // Disabling Zend Garbage Collection to prevent segfaults with PHP5.4+ + // https://bugs.php.net/bug.php?id=53976 + gc_disable(); +} + +$loader = require_once __DIR__.'/vendor/autoload.php'; + +use Doctrine\Common\Annotations\AnnotationRegistry; + +AnnotationRegistry::registerLoader(array($loader, 'loadClass')); + +return $loader; diff --git a/vendor/symfony/symfony/composer.json b/vendor/symfony/symfony/composer.json new file mode 100644 index 0000000..9e2eee3 --- /dev/null +++ b/vendor/symfony/symfony/composer.json @@ -0,0 +1,87 @@ +{ + "name": "symfony/symfony", + "type": "library", + "description": "The Symfony PHP framework", + "keywords": ["framework"], + "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.3", + "symfony/icu": "~1.0", + "doctrine/common": "~2.2", + "twig/twig": "~1.11", + "psr/log": "~1.0" + }, + "replace": { + "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/doctrine-bridge": "self.version", + "symfony/dom-crawler": "self.version", + "symfony/event-dispatcher": "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/locale": "self.version", + "symfony/monolog-bridge": "self.version", + "symfony/options-resolver": "self.version", + "symfony/process": "self.version", + "symfony/propel1-bridge": "self.version", + "symfony/property-access": "self.version", + "symfony/proxy-manager-bridge": "self.version", + "symfony/routing": "self.version", + "symfony/security": "self.version", + "symfony/security-bundle": "self.version", + "symfony/serializer": "self.version", + "symfony/stopwatch": "self.version", + "symfony/swiftmailer-bridge": "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/web-profiler-bundle": "self.version", + "symfony/yaml": "self.version" + }, + "require-dev": { + "doctrine/data-fixtures": "1.0.*", + "doctrine/dbal": "~2.2", + "doctrine/orm": "~2.2,>=2.2.3", + "monolog/monolog": "~1.3", + "propel/propel1": "1.6.*", + "ircmaxell/password-compat": "1.0.*", + "ocramius/proxy-manager": ">=0.3.1,<0.4-dev" + }, + "autoload": { + "psr-0": { "Symfony\\": "src/" }, + "classmap": [ + "src/Symfony/Component/HttpFoundation/Resources/stubs", + "src/Symfony/Component/Intl/Resources/stubs" + ], + "files": [ "src/Symfony/Component/Intl/Resources/stubs/functions.php" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} 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..9c747b8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/CHANGELOG.md @@ -0,0 +1,20 @@ +CHANGELOG +========= + +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..3b07bf4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.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\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 boolean + */ + public function dispatchEvent($eventName, EventArgs $eventArgs = null) + { + if (isset($this->listeners[$eventName])) { + $eventArgs = $eventArgs === null ? 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 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|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..7f91749 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.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\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; + $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($param, $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..331a465 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.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 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) + { + if (is_dir($mappingConfig['dir'])) { + $this->drivers[$mappingConfig['type']][$mappingConfig['prefix']] = realpath($mappingConfig['dir']); + } else { + throw new \InvalidArgumentException(sprintf('Invalid Doctrine mapping path given. Cannot load Doctrine mapping/bundle named "%s".', $mappingName)); + } + } + + /** + * 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'; + } + + return null; + } + + /** + * 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) + { + $cacheDriver = $objectManager[$cacheName.'_driver']; + $cacheDriverService = $this->getObjectManagerElementName($objectManager['name'].'_'.$cacheName); + + switch ($cacheDriver['type']) { + case 'service': + $container->setAlias($cacheDriverService, new Alias($cacheDriver['id'], false)); + + return; + 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', $objectManager['name'])), $memcacheInstance); + $cacheDef->addMethodCall('setMemcache', array(new Reference($this->getObjectManagerElementName(sprintf('%s_memcache_instance', $objectManager['name']))))); + 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', $objectManager['name'])), $memcachedInstance); + $cacheDef->addMethodCall('setMemcached', array(new Reference($this->getObjectManagerElementName(sprintf('%s_memcached_instance', $objectManager['name']))))); + 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', $objectManager['name'])), $redisInstance); + $cacheDef->addMethodCall('setRedis', array(new Reference($this->getObjectManagerElementName(sprintf('%s_redis_instance', $objectManager['name']))))); + 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); + // generate a unique namespace for the given application + $namespace = 'sf2'.$this->getMappingResourceExtension().'_'.$objectManager['name'].'_'.md5($container->getParameter('kernel.root_dir').$container->getParameter('kernel.environment')); + $cacheDef->addMethodCall('setNamespace', array($namespace)); + + $container->setDefinition($cacheDriverService, $cacheDef); + } + + /** + * 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(); +} 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..30479c8 --- /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..d450e49 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.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\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; + } + + $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; + }; + + $subscribersPerCon = $this->groupByConnection($container->findTaggedServiceIds($this->tagPrefix.'.event_subscriber')); + foreach ($subscribersPerCon as $con => $subscribers) { + $em = $this->getEventManager($con); + + uasort($subscribers, $sortFunc); + foreach ($subscribers as $id => $instance) { + $em->addMethodCall('addEventSubscriber', array(new Reference($id))); + } + } + + $listenersPerCon = $this->groupByConnection($container->findTaggedServiceIds($this->tagPrefix.'.event_listener'), true); + foreach ($listenersPerCon as $con => $listeners) { + $em = $this->getEventManager($con); + + uasort($listeners, $sortFunc); + foreach ($listeners as $id => $instance) { + $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..983f1f4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php @@ -0,0 +1,167 @@ + + * + * 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\HttpKernel\Kernel; + +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; + + /** + * @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 to get the metadata driver service names + * @param string $enabledParameter service container parameter that must be + * present to enable the mapping. Set to false + * to not do any check, optional. + */ + public function __construct($driver, array $namespaces, array $managerParameters, $driverPattern, $enabledParameter = false) + { + $this->driver = $driver; + $this->namespaces = $namespaces; + $this->managerParameters = $managerParameters; + $this->driverPattern = $driverPattern; + $this->enabledParameter = $enabledParameter; + } + + /** + * Register mappings 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); + $chainDriverDef = $container->getDefinition($chainDriverDefService); + foreach ($this->namespaces as $namespace) { + $chainDriverDef->addMethodCall('addDriver', array($mappingDriverDef, $namespace)); + } + } + + /** + * Get the service name of the metadata chain driver that the mappings + * should be registered with. The default implementation loops over the + * managerParameters and applies the first non-empty parameter it finds to + * the driverPattern. + * + * @param ContainerBuilder $container + * + * @return string a service definition name + * + * @throws ParameterNotFoundException if non of the managerParameters has a + * non-empty value. + */ + protected function getChainDriverServiceName(ContainerBuilder $container) + { + foreach ($this->managerParameters as $param) { + if ($container->hasParameter($param)) { + $name = $container->getParameter($param); + if ($name) { + return sprintf($this->driverPattern, $name); + } + } + } + + throw new ParameterNotFoundException('None of the managerParameters resulted in a valid name'); + } + + /** + * 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; + } + + /** + * 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 boolean 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..421fd44 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.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\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/Form/ChoiceList/EntityChoiceList.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/EntityChoiceList.php new file mode 100644 index 0000000..74042a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/EntityChoiceList.php @@ -0,0 +1,424 @@ + + * + * 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 Symfony\Component\Form\Exception\RuntimeException; +use Symfony\Component\Form\Exception\StringCastException; +use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList; +use Doctrine\Common\Persistence\ObjectManager; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * A choice list presenting a list of Doctrine entities as choices + * + * @author Bernhard Schussek + */ +class EntityChoiceList extends ObjectChoiceList +{ + /** + * @var ObjectManager + */ + private $em; + + /** + * @var string + */ + private $class; + + /** + * @var \Doctrine\Common\Persistence\Mapping\ClassMetadata + */ + private $classMetadata; + + /** + * Contains the query builder that builds the query for fetching the + * entities + * + * This property should only be accessed through queryBuilder. + * + * @var EntityLoaderInterface + */ + private $entityLoader; + + /** + * The identifier field, if the identifier is not composite + * + * @var array + */ + private $idField = null; + + /** + * Whether to use the identifier for index generation + * + * @var Boolean + */ + private $idAsIndex = false; + + /** + * Whether to use the identifier for value generation + * + * @var Boolean + */ + private $idAsValue = false; + + /** + * Whether the entities have already been loaded. + * + * @var Boolean + */ + private $loaded = false; + + /** + * The preferred entities. + * + * @var array + */ + private $preferredEntities = array(); + + /** + * Creates a new entity choice list. + * + * @param ObjectManager $manager An EntityManager instance + * @param string $class The class name + * @param string $labelPath The property path used for the label + * @param EntityLoaderInterface $entityLoader An optional query builder + * @param array $entities An array of choices + * @param array $preferredEntities An array of preferred choices + * @param string $groupPath A property path pointing to the property used + * to group the choices. Only allowed if + * the choices are given as flat array. + * @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths. + */ + public function __construct(ObjectManager $manager, $class, $labelPath = null, EntityLoaderInterface $entityLoader = null, $entities = null, array $preferredEntities = array(), $groupPath = null, PropertyAccessorInterface $propertyAccessor = null) + { + $this->em = $manager; + $this->entityLoader = $entityLoader; + $this->classMetadata = $manager->getClassMetadata($class); + $this->class = $this->classMetadata->getName(); + $this->loaded = is_array($entities) || $entities instanceof \Traversable; + $this->preferredEntities = $preferredEntities; + + $identifier = $this->classMetadata->getIdentifierFieldNames(); + + if (1 === count($identifier)) { + $this->idField = $identifier[0]; + $this->idAsValue = true; + + if (in_array($this->classMetadata->getTypeOfField($this->idField), array('integer', 'smallint', 'bigint'))) { + $this->idAsIndex = true; + } + } + + if (!$this->loaded) { + // Make sure the constraints of the parent constructor are + // fulfilled + $entities = array(); + } + + parent::__construct($entities, $labelPath, $preferredEntities, $groupPath, null, $propertyAccessor); + } + + /** + * Returns the list of entities + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getChoices() + { + if (!$this->loaded) { + $this->load(); + } + + return parent::getChoices(); + } + + /** + * Returns the values for the entities + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getValues() + { + if (!$this->loaded) { + $this->load(); + } + + return parent::getValues(); + } + + /** + * Returns the choice views of the preferred choices as nested array with + * the choice groups as top-level keys. + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getPreferredViews() + { + if (!$this->loaded) { + $this->load(); + } + + return parent::getPreferredViews(); + } + + /** + * Returns the choice views of the choices that are not preferred as nested + * array with the choice groups as top-level keys. + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getRemainingViews() + { + if (!$this->loaded) { + $this->load(); + } + + return parent::getRemainingViews(); + } + + /** + * Returns the entities corresponding to the given values. + * + * @param array $values + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getChoicesForValues(array $values) + { + if (!$this->loaded) { + // Optimize performance in case we have an entity loader and + // a single-field identifier + if ($this->idAsValue && $this->entityLoader) { + if (empty($values)) { + return array(); + } + + return $this->entityLoader->getEntitiesByIds($this->idField, $values); + } + + $this->load(); + } + + return parent::getChoicesForValues($values); + } + + /** + * Returns the values corresponding to the given entities. + * + * @param array $entities + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getValuesForChoices(array $entities) + { + if (!$this->loaded) { + // 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->idAsValue) { + $values = array(); + + foreach ($entities as $entity) { + if ($entity instanceof $this->class) { + // Make sure to convert to the right format + $values[] = $this->fixValue(current($this->getIdentifierValues($entity))); + } + } + + return $values; + } + + $this->load(); + } + + return parent::getValuesForChoices($entities); + } + + /** + * Returns the indices corresponding to the given entities. + * + * @param array $entities + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getIndicesForChoices(array $entities) + { + if (!$this->loaded) { + // Optimize performance for single-field identifiers. We already + // know that the IDs are used as indices + + // Attention: This optimization does not check choices for existence + if ($this->idAsIndex) { + $indices = array(); + + foreach ($entities as $entity) { + if ($entity instanceof $this->class) { + // Make sure to convert to the right format + $indices[] = $this->fixIndex(current($this->getIdentifierValues($entity))); + } + } + + return $indices; + } + + $this->load(); + } + + return parent::getIndicesForChoices($entities); + } + + /** + * Returns the entities corresponding to the given values. + * + * @param array $values + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getIndicesForValues(array $values) + { + if (!$this->loaded) { + // Optimize performance for single-field identifiers. + + // Attention: This optimization does not check values for existence + if ($this->idAsIndex && $this->idAsValue) { + return $this->fixIndices($values); + } + + $this->load(); + } + + return parent::getIndicesForValues($values); + } + + /** + * Creates a new unique index for this entity. + * + * If the entity has a single-field identifier, this identifier is used. + * + * Otherwise a new integer is generated. + * + * @param mixed $entity The choice to create an index for + * + * @return integer|string A unique index containing only ASCII letters, + * digits and underscores. + */ + protected function createIndex($entity) + { + if ($this->idAsIndex) { + return $this->fixIndex(current($this->getIdentifierValues($entity))); + } + + return parent::createIndex($entity); + } + + /** + * Creates a new unique value for this entity. + * + * If the entity has a single-field identifier, this identifier is used. + * + * Otherwise a new integer is generated. + * + * @param mixed $entity The choice to create a value for + * + * @return integer|string A unique value without character limitations. + */ + protected function createValue($entity) + { + if ($this->idAsValue) { + return (string) current($this->getIdentifierValues($entity)); + } + + return parent::createValue($entity); + } + + /** + * {@inheritdoc} + */ + protected function fixIndex($index) + { + $index = parent::fixIndex($index); + + // If the ID is a single-field integer identifier, it is used as + // index. Replace any leading minus by underscore to make it a valid + // form name. + if ($this->idAsIndex && $index < 0) { + $index = strtr($index, '-', '_'); + } + + return $index; + } + + /** + * Loads the list with entities. + */ + private function load() + { + if ($this->entityLoader) { + $entities = $this->entityLoader->getEntities(); + } else { + $entities = $this->em->getRepository($this->class)->findAll(); + } + + try { + // The second parameter $labels is ignored by ObjectChoiceList + parent::initialize($entities, array(), $this->preferredEntities); + } catch (StringCastException $e) { + throw new StringCastException(str_replace('argument $labelPath', 'option "property"', $e->getMessage()), null, $e); + } + + $this->loaded = true; + } + + /** + * Returns the values of the identifier fields of an entity. + * + * Doctrine must know about this entity, that is, the entity must already + * be persisted or added to the identity map before. Otherwise an + * exception is thrown. + * + * @param object $entity The entity for which to get the identifier + * + * @return array The identifier values + * + * @throws RuntimeException If the entity does not exist in Doctrine's identity map + */ + private function getIdentifierValues($entity) + { + if (!$this->em->contains($entity)) { + throw new RuntimeException( + 'Entities passed to the choice field must be managed. Maybe ' . + 'persist them in the entity manager?' + ); + } + + $this->em->initializeObject($entity); + + return $this->classMetadata->getIdentifierValues($entity); + } +} 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..4f53986 --- /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/ORMQueryBuilderLoader.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php new file mode 100644 index 0000000..ef9eb14 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.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\Form\ChoiceList; + +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Doctrine\ORM\QueryBuilder; +use Doctrine\DBAL\Connection; + +/** + * Getting Entities through the ORM QueryBuilder + */ +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|\Closure $queryBuilder + * @param EntityManager $manager + * @param string $class + * + * @throws UnexpectedTypeException + */ + public function __construct($queryBuilder, $manager = null, $class = null) + { + // If a query builder was passed, it must be a closure or QueryBuilder + // instance + if (!($queryBuilder instanceof QueryBuilder || $queryBuilder instanceof \Closure)) { + throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder or \Closure'); + } + + if ($queryBuilder instanceof \Closure) { + $queryBuilder = $queryBuilder($manager->getRepository($class)); + + if (!$queryBuilder instanceof QueryBuilder) { + throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\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; + $where = $qb->expr()->in($alias.'.'.$identifier, ':'.$parameter); + + return $qb->andWhere($where) + ->getQuery() + ->setParameter($parameter, $values, Connection::PARAM_STR_ARRAY) + ->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..6392474 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.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\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(); + } + + 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..a98b2d2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.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\Form; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\PropertyAccess\PropertyAccess; + +class DoctrineOrmExtension extends AbstractExtension +{ + protected $registry; + + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + } + + protected function loadTypes() + { + return array( + new Type\EntityType($this->registry, PropertyAccess::getPropertyAccessor()), + ); + } + + 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..d857de7 --- /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\ORM\Mapping\ClassMetadataInfo; +use Doctrine\Common\Persistence\Mapping\MappingException; +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; + +class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface +{ + protected $registry; + + private $cache; + + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + $this->cache = array(); + } + + /** + * {@inheritDoc} + */ + public function guessType($class, $property) + { + if (!$ret = $this->getMetadata($class)) { + return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); + } + + list($metadata, $name) = $ret; + + if ($metadata->hasAssociation($property)) { + $multiple = $metadata->isCollectionValuedAssociation($property); + $mapping = $metadata->getAssociationMapping($property); + + return new TypeGuess('entity', array('em' => $name, 'class' => $mapping['targetEntity'], 'multiple' => $multiple), Guess::HIGH_CONFIDENCE); + } + + switch ($metadata->getTypeOfField($property)) { + case 'array': + return new TypeGuess('collection', array(), Guess::MEDIUM_CONFIDENCE); + case 'boolean': + return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE); + case 'datetime': + case 'vardatetime': + case 'datetimetz': + return new TypeGuess('datetime', array(), Guess::HIGH_CONFIDENCE); + case 'date': + return new TypeGuess('date', array(), Guess::HIGH_CONFIDENCE); + case 'time': + return new TypeGuess('time', array(), Guess::HIGH_CONFIDENCE); + case 'decimal': + case 'float': + return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE); + case 'integer': + case 'bigint': + case 'smallint': + return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE); + case 'string': + return new TypeGuess('text', array(), Guess::MEDIUM_CONFIDENCE); + case 'text': + return new TypeGuess('textarea', array(), Guess::MEDIUM_CONFIDENCE); + default: + return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); + } + } + + /** + * {@inheritDoc} + */ + public function guessRequired($class, $property) + { + $classMetadatas = $this->getMetadata($class); + + if (!$classMetadatas) { + return null; + } + + /* @var ClassMetadataInfo $classMetadata */ + $classMetadata = $classMetadatas[0]; + + // Check whether the field exists and is nullable or not + if ($classMetadata->hasField($property)) { + if (!$classMetadata->isNullable($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); + } + + return null; + } + + /** + * {@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('decimal', '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)) { + $mapping = $ret[0]->getFieldMapping($property); + + if (in_array($ret[0]->getTypeOfField($property), array('decimal', 'float'))) { + return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); + } + } + } + + protected function getMetadata($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..41f36e0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.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\Doctrine\Form\EventListener; + +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 Doctrine\Common\Collections\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 && 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..8162396 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.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\Bridge\Doctrine\Form\Type; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Symfony\Component\Form\Exception\RuntimeException; +use Doctrine\Common\Persistence\ObjectManager; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList; +use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface; +use Symfony\Bridge\Doctrine\Form\EventListener\MergeDoctrineCollectionListener; +use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +abstract class DoctrineType extends AbstractType +{ + /** + * @var ManagerRegistry + */ + protected $registry; + + /** + * @var array + */ + private $choiceListCache = array(); + + /** + * @var PropertyAccessorInterface + */ + private $propertyAccessor; + + public function __construct(ManagerRegistry $registry, PropertyAccessorInterface $propertyAccessor = null) + { + $this->registry = $registry; + $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor(); + } + + public function buildForm(FormBuilderInterface $builder, array $options) + { + if ($options['multiple']) { + $builder + ->addEventSubscriber(new MergeDoctrineCollectionListener()) + ->addViewTransformer(new CollectionToArrayTransformer(), true) + ; + } + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $choiceListCache =& $this->choiceListCache; + $registry = $this->registry; + $propertyAccessor = $this->propertyAccessor; + $type = $this; + + $loader = function (Options $options) use ($type) { + if (null !== $options['query_builder']) { + return $type->getLoader($options['em'], $options['query_builder'], $options['class']); + } + + return null; + }; + + $choiceList = function (Options $options) use (&$choiceListCache, $propertyAccessor) { + // Support for closures + $propertyHash = is_object($options['property']) + ? spl_object_hash($options['property']) + : $options['property']; + + $choiceHashes = $options['choices']; + + // Support for recursive arrays + if (is_array($choiceHashes)) { + // A second parameter ($key) is passed, so we cannot use + // spl_object_hash() directly (which strictly requires + // one parameter) + array_walk_recursive($choiceHashes, function (&$value) { + $value = spl_object_hash($value); + }); + } + + $preferredChoiceHashes = $options['preferred_choices']; + + if (is_array($preferredChoiceHashes)) { + array_walk_recursive($preferredChoiceHashes, function (&$value) { + $value = spl_object_hash($value); + }); + } + + // Support for custom loaders (with query builders) + $loaderHash = is_object($options['loader']) + ? spl_object_hash($options['loader']) + : $options['loader']; + + // Support for closures + $groupByHash = is_object($options['group_by']) + ? spl_object_hash($options['group_by']) + : $options['group_by']; + + $hash = md5(json_encode(array( + spl_object_hash($options['em']), + $options['class'], + $propertyHash, + $loaderHash, + $choiceHashes, + $preferredChoiceHashes, + $groupByHash + ))); + + if (!isset($choiceListCache[$hash])) { + $choiceListCache[$hash] = new EntityChoiceList( + $options['em'], + $options['class'], + $options['property'], + $options['loader'], + $options['choices'], + $options['preferred_choices'], + $options['group_by'], + $propertyAccessor + ); + } + + return $choiceListCache[$hash]; + }; + + $emNormalizer = function (Options $options, $em) use ($registry) { + /* @var ManagerRegistry $registry */ + if (null !== $em) { + return $registry->getManager($em); + } + + $em = $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; + }; + + $resolver->setDefaults(array( + 'em' => null, + 'property' => null, + 'query_builder' => null, + 'loader' => $loader, + 'choices' => null, + 'choice_list' => $choiceList, + 'group_by' => null, + )); + + $resolver->setRequired(array('class')); + + $resolver->setNormalizers(array( + 'em' => $emNormalizer, + )); + + $resolver->setAllowedTypes(array( + 'loader' => array('null', 'Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface'), + )); + } + + /** + * 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 'choice'; + } +} 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..51b47be --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.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\Doctrine\Form\Type; + +use Doctrine\Common\Persistence\ObjectManager; +use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; + +class EntityType extends DoctrineType +{ + /** + * Return the default loader object. + * + * @param ObjectManager $manager + * @param mixed $queryBuilder + * @param string $class + * @return ORMQueryBuilderLoader + */ + public function getLoader(ObjectManager $manager, $queryBuilder, $class) + { + return new ORMQueryBuilderLoader( + $queryBuilder, + $manager, + $class + ); + } + + public function getName() + { + return 'entity'; + } +} 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..9d822e5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php @@ -0,0 +1,174 @@ + + * + * 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\Platforms\MySqlPlatform; +use Doctrine\DBAL\Driver\Connection; + +/** + * DBAL based session storage. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class DbalSessionHandler implements \SessionHandlerInterface +{ + /** + * @var Connection + */ + private $con; + + /** + * @var string + */ + private $tableName; + + /** + * Constructor. + * + * @param Connection $con An instance of Connection. + * @param string $tableName Table name. + */ + public function __construct(Connection $con, $tableName = 'sessions') + { + $this->con = $con; + $this->tableName = $tableName; + } + + /** + * {@inheritdoc} + */ + public function open($path = null, $name = null) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + // do nothing + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($id) + { + try { + $this->con->executeQuery("DELETE FROM {$this->tableName} WHERE sess_id = :id", array( + 'id' => $id, + )); + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($lifetime) + { + try { + $this->con->executeQuery("DELETE FROM {$this->tableName} WHERE sess_time < :time", array( + 'time' => time() - $lifetime, + )); + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function read($id) + { + try { + $data = $this->con->executeQuery("SELECT sess_data FROM {$this->tableName} WHERE sess_id = :id", array( + 'id' => $id, + ))->fetchColumn(); + + if (false !== $data) { + return base64_decode($data); + } + + // session does not exist, create it + $this->createNewSession($id); + + return ''; + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e); + } + } + + /** + * {@inheritdoc} + */ + public function write($id, $data) + { + $platform = $this->con->getDatabasePlatform(); + + // this should maybe be abstracted in Doctrine DBAL + if ($platform instanceof MySqlPlatform) { + $sql = "INSERT INTO {$this->tableName} (sess_id, sess_data, sess_time) VALUES (%1\$s, %2\$s, %3\$d) " + ."ON DUPLICATE KEY UPDATE sess_data = VALUES(sess_data), sess_time = CASE WHEN sess_time = %3\$d THEN (VALUES(sess_time) + 1) ELSE VALUES(sess_time) END"; + } else { + $sql = "UPDATE {$this->tableName} SET sess_data = %2\$s, sess_time = %3\$d WHERE sess_id = %1\$s"; + } + + try { + $rowCount = $this->con->exec(sprintf( + $sql, + $this->con->quote($id), + //session data can contain non binary safe characters so we need to encode it + $this->con->quote(base64_encode($data)), + time() + )); + + if (!$rowCount) { + // No session exists in the database to update. This happens when we have called + // session_regenerate_id() + $this->createNewSession($id, $data); + } + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * Creates a new session with the given $id and $data + * + * @param string $id + * @param string $data + * + * @return Boolean + */ + private function createNewSession($id, $data = '') + { + $this->con->exec(sprintf("INSERT INTO {$this->tableName} (sess_id, sess_data, sess_time) VALUES (%s, %s, %d)", + $this->con->quote($id), + //session data can contain non binary safe characters so we need to encode it + $this->con->quote(base64_encode($data)), + time() + )); + + return true; + } +} 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..b7b4b91 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandlerSchema.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\HttpFoundation; + +use Doctrine\DBAL\Schema\Schema; + +/** + * DBAL Session Storage Schema. + * + * @author Johannes M. Schmitt + */ +final class DbalSessionHandlerSchema extends Schema +{ + private $tableName; + + public function __construct($tableName = 'sessions') + { + parent::__construct(); + + $this->tableName = $tableName; + $this->addSessionTable(); + } + + public function addToSchema(Schema $schema) + { + foreach ($this->getTables() as $table) { + $schema->_addTable($table); + } + } + + private function addSessionTable() + { + $table = $this->createTable($this->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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/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..3d103e2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.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\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)) { + foreach ($params as $index => $param) { + if (!is_string($params[$index])) { + continue; + } + + // non utf-8 strings break json encoding + if (!preg_match('#[\p{L}\p{N} ]#u', $params[$index])) { + $params[$index] = self::BINARY_DATA_VALUE; + continue; + } + + // detect if the too long string must be shorten + if (function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($params[$index])) { + if (self::MAX_STRING_LENGTH < mb_strlen($params[$index], $encoding)) { + $params[$index] = mb_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, $encoding).' [...]'; + continue; + } + } else { + if (self::MAX_STRING_LENGTH < strlen($params[$index])) { + $params[$index] = substr($params[$index], 0, self::MAX_STRING_LENGTH - 6).' [...]'; + continue; + } + } + } + } + + 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); + } +} 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..80eeb37 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ManagerRegistry.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\Doctrine; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Doctrine\Common\Persistence\AbstractManagerRegistry; + +/** + * References Doctrine connections and entity/document managers. + * + * @author Lukas Kahwe Smith + */ +abstract class ManagerRegistry extends AbstractManagerRegistry implements ContainerAwareInterface +{ + /** + * @var ContainerInterface + */ + protected $container; + + /** + * @inheritdoc + */ + protected function getService($name) + { + return $this->container->get($name); + } + + /** + * @inheritdoc + */ + protected function resetService($name) + { + $this->container->set($name, null); + } + + /** + * @inheritdoc + */ + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } +} 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..9aecb01 --- /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 Symfony2 components. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Bridge/Doctrine/ + $ composer.phar install --dev + $ 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..2086f71 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/RegistryInterface.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\Bridge\Doctrine; + +use Doctrine\Common\Persistence\ManagerRegistry as ManagerRegistryInterface; +use Doctrine\ORM\Configuration; +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..999f469 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.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\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; +use PDO, DateTime; + +/** + * 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 \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * new DoctrineTokenProvider for the RemembeMe authentication service + * + * @param \Doctrine\DBAL\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..487b1d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.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\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 $class; + private $repository; + private $property; + private $metadata; + + public function __construct(ManagerRegistry $registry, $class, $property = null, $managerName = null) + { + $em = $registry->getManager($managerName); + $this->class = $class; + $this->metadata = $em->getClassMetadata($class); + + if (false !== strpos($this->class, ':')) { + $this->class = $this->metadata->getName(); + } + + $this->repository = $em->getRepository($class); + $this->property = $property; + } + + /** + * {@inheritdoc} + */ + public function loadUserByUsername($username) + { + if (null !== $this->property) { + $user = $this->repository->findOneBy(array($this->property => $username)); + } else { + if (!$this->repository instanceof UserProviderInterface) { + throw new \InvalidArgumentException(sprintf('The Doctrine repository "%s" must implement UserProviderInterface.', get_class($this->repository))); + } + + $user = $this->repository->loadUserByUsername($username); + } + + if (null === $user) { + throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username)); + } + + return $user; + } + + /** + * {@inheritDoc} + */ + public function refreshUser(UserInterface $user) + { + if (!$user instanceof $this->class) { + throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user))); + } + + if ($this->repository instanceof UserProviderInterface) { + $refreshedUser = $this->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->metadata->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." + ); + } + + if (null === $refreshedUser = $this->repository->find($id)) { + throw new UsernameNotFoundException(sprintf('User with id %s not found', json_encode($id))); + } + } + + return $refreshedUser; + } + + /** + * {@inheritDoc} + */ + public function supportsClass($class) + { + return $class === $this->class || is_subclass_of($class, $this->class); + } +} 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..b5985ca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.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\Doctrine\Tests; + +use Symfony\Bridge\Doctrine\ContainerAwareEventManager; +use Symfony\Component\DependencyInjection\Container; + +class ContainerAwareEventManagerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\DependencyInjection\Container')) { + $this->markTestSkipped('The "DependencyInjection" component is not available'); + } + + $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..0679356 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.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\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 +{ + protected function setUp() + { + if (!class_exists('Doctrine\DBAL\Platforms\MySqlPlatform')) { + $this->markTestSkipped('Doctrine DBAL is not available.'); + } + + if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) { + $this->markTestSkipped('The "HttpKernel" component is not available'); + } + } + + 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()); + + $collected_queries = $c->getQueries(); + $this->assertEquals($expected, $collected_queries['default'][0]['params'][0]); + $this->assertEquals($explainable, $collected_queries['default'][0]['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)); + + $collected_queries = $c->getQueries(); + $this->assertEquals($expected, $collected_queries['default'][0]['params'][0]); + $this->assertEquals($explainable, $collected_queries['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..52d653b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DataFixtures/ContainerAwareLoaderTest.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\Doctrine\Tests\DataFixtures; + +use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader; +use Symfony\Bridge\Doctrine\Tests\Fixtures\ContainerAwareFixture; + +class ContainerAwareLoaderTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\DependencyInjection\Container')) { + $this->markTestSkipped('The "DependencyInjection" component is not available'); + } + + if (!class_exists('Doctrine\Common\DataFixtures\Loader')) { + $this->markTestSkipped('Doctrine Data Fixtures is not available.'); + } + } + + 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..e679aaf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.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\Tests\DependencyInjection\Compiler; + +use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class RegisterEventListenersAndSubscribersPassTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\DependencyInjection\Container')) { + $this->markTestSkipped('The "DependencyInjection" component is not available'); + } + } + + 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); + $this->assertEquals(array('c', 'd', 'e', 'b', 'a'), $this->getServiceOrder($container, 'addEventSubscriber')); + } + + 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 $call) { + list($name, $arguments) = $call; + 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/DoctrineOrmTestCase.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DoctrineOrmTestCase.php new file mode 100644 index 0000000..005c807 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DoctrineOrmTestCase.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\Tests; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\EntityManager; + +abstract class DoctrineOrmTestCase extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Doctrine\Common\Version')) { + $this->markTestSkipped('Doctrine Common is not available.'); + } + + if (!class_exists('Doctrine\DBAL\Platforms\MySqlPlatform')) { + $this->markTestSkipped('Doctrine DBAL is not available.'); + } + + if (!class_exists('Doctrine\ORM\EntityManager')) { + $this->markTestSkipped('Doctrine ORM is not available.'); + } + } + + /** + * @return EntityManager + */ + public static function createTestEntityManager($paths = array()) + { + if (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers())) { + self::markTestSkipped('This test requires SQLite support in your environment'); + } + $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); + } +} 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..0fe3d14 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.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\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="SingleIdentEntity") + * @var \Symfony\Bridge\Doctrine\Tests\Form\Fixtures\SingleIdentEntity + */ + public $single; + + /** + * @ORM\ManyToOne(targetEntity="CompositeIdentEntity") + * @ORM\JoinColumns({ + * @ORM\JoinColumn(name="composite_id1", referencedColumnName="id1"), + * @ORM\JoinColumn(name="composite_id2", referencedColumnName="id2") + * }) + * @var \Symfony\Bridge\Doctrine\Tests\Form\Fixtures\CompositeIdentEntity + */ + public $composite; +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIdentEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIdentEntity.php new file mode 100644 index 0000000..9d26314 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIdentEntity.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 CompositeIdentEntity 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/Fixtures/CompositeStringIdentEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdentEntity.php new file mode 100644 index 0000000..43c71f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdentEntity.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 CompositeStringIdentEntity +{ + /** @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; + } +} 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/DoubleIdentEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleIdentEntity.php new file mode 100644 index 0000000..2ac1ad3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleIdentEntity.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 DoubleIdentEntity +{ + /** @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/ItemGroupEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/ItemGroupEntity.php new file mode 100644 index 0000000..04d2ddf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/ItemGroupEntity.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 ItemGroupEntity +{ + /** @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/NoToStringSingleIdentEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/NoToStringSingleIdentEntity.php new file mode 100644 index 0000000..a5ecb3d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/NoToStringSingleIdentEntity.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 NoToStringSingleIdentEntity +{ + /** @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/SingleIdentEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIdentEntity.php new file mode 100644 index 0000000..09ee18a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIdentEntity.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 SingleIdentEntity +{ + /** @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/SingleStringIdentEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdentEntity.php new file mode 100644 index 0000000..50f53b7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdentEntity.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 SingleStringIdentEntity +{ + /** @Id @Column(type="string") */ + protected $id; + + /** @Column(type="string") */ + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/EntityChoiceListTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/EntityChoiceListTest.php new file mode 100644 index 0000000..cce4578 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/EntityChoiceListTest.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\Bridge\Doctrine\Tests\Form\ChoiceList; + +use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; +use Symfony\Bridge\Doctrine\Tests\DoctrineOrmTestCase; +use Symfony\Bridge\Doctrine\Tests\Fixtures\ItemGroupEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdentEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\NoToStringSingleIdentEntity; +use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Doctrine\ORM\Tools\SchemaTool; + +class EntityChoiceListTest extends DoctrineOrmTestCase +{ + const ITEM_GROUP_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\ItemGroupEntity'; + + const SINGLE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity'; + + const SINGLE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdentEntity'; + + const COMPOSITE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity'; + + private $em; + + protected function setUp() + { + if (!class_exists('Symfony\Component\Form\Form')) { + $this->markTestSkipped('The "Form" component is not available'); + } + + parent::setUp(); + + $this->em = $this->createTestEntityManager(); + + $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_STRING_IDENT_CLASS), + $this->em->getClassMetadata(self::COMPOSITE_IDENT_CLASS), + ); + + try { + $schemaTool->dropSchema($classes); + } catch (\Exception $e) { + } + + try { + $schemaTool->createSchema($classes); + } catch (\Exception $e) { + } + } + + protected function tearDown() + { + parent::tearDown(); + + $this->em = null; + } + + /** + * @expectedException \Symfony\Component\Form\Exception\StringCastException + * @expectedMessage Entity "Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity" passed to the choice field must have a "__toString()" method defined (or you can also override the "property" option). + */ + public function testEntitiesMustHaveAToStringMethod() + { + $entity1 = new NoToStringSingleIdentEntity(1, 'Foo'); + $entity2 = new NoToStringSingleIdentEntity(2, 'Bar'); + + // Persist for managed state + $this->em->persist($entity1); + $this->em->persist($entity2); + + $choiceList = new EntityChoiceList( + $this->em, + self::SINGLE_IDENT_CLASS, + null, + null, + array( + $entity1, + $entity2, + ) + ); + + $choiceList->getValues(); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testChoicesMustBeManaged() + { + $entity1 = new SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + + // no persist here! + + $choiceList = new EntityChoiceList( + $this->em, + self::SINGLE_IDENT_CLASS, + 'name', + null, + array( + $entity1, + $entity2, + ) + ); + + // triggers loading -> exception + $choiceList->getChoices(); + } + + public function testFlattenedChoicesAreManaged() + { + $entity1 = new SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + + // Persist for managed state + $this->em->persist($entity1); + $this->em->persist($entity2); + + $choiceList = new EntityChoiceList( + $this->em, + self::SINGLE_IDENT_CLASS, + 'name', + null, + array( + $entity1, + $entity2, + ) + ); + + $this->assertSame(array(1 => $entity1, 2 => $entity2), $choiceList->getChoices()); + } + + public function testEmptyChoicesAreManaged() + { + $entity1 = new SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + + // Persist for managed state + $this->em->persist($entity1); + $this->em->persist($entity2); + + $choiceList = new EntityChoiceList( + $this->em, + self::SINGLE_IDENT_CLASS, + 'name', + null, + array() + ); + + $this->assertSame(array(), $choiceList->getChoices()); + } + + public function testNestedChoicesAreManaged() + { + $entity1 = new SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + + // Oh yeah, we're persisting with fire now! + $this->em->persist($entity1); + $this->em->persist($entity2); + + $choiceList = new EntityChoiceList( + $this->em, + self::SINGLE_IDENT_CLASS, + 'name', + null, + array( + 'group1' => array($entity1), + 'group2' => array($entity2), + ), + array() + ); + + $this->assertSame(array(1 => $entity1, 2 => $entity2), $choiceList->getChoices()); + $this->assertEquals(array( + 'group1' => array(1 => new ChoiceView($entity1, '1', 'Foo')), + 'group2' => array(2 => new ChoiceView($entity2, '2', 'Bar')) + ), $choiceList->getRemainingViews()); + } + + public function testGroupBySupportsString() + { + $item1 = new ItemGroupEntity(1, 'Foo', 'Group1'); + $item2 = new ItemGroupEntity(2, 'Bar', 'Group1'); + $item3 = new ItemGroupEntity(3, 'Baz', 'Group2'); + $item4 = new ItemGroupEntity(4, 'Boo!', null); + + $this->em->persist($item1); + $this->em->persist($item2); + $this->em->persist($item3); + $this->em->persist($item4); + + $choiceList = new EntityChoiceList( + $this->em, + self::ITEM_GROUP_CLASS, + 'name', + null, + array( + $item1, + $item2, + $item3, + $item4, + ), + array(), + 'groupName' + ); + + $this->assertEquals(array(1 => $item1, 2 => $item2, 3 => $item3, 4 => $item4), $choiceList->getChoices()); + $this->assertEquals(array( + 'Group1' => array(1 => new ChoiceView($item1, '1', 'Foo'), 2 => new ChoiceView($item2, '2', 'Bar')), + 'Group2' => array(3 => new ChoiceView($item3, '3', 'Baz')), + 4 => new ChoiceView($item4, '4', 'Boo!') + ), $choiceList->getRemainingViews()); + } + + public function testGroupByInvalidPropertyPathReturnsFlatChoices() + { + $item1 = new ItemGroupEntity(1, 'Foo', 'Group1'); + $item2 = new ItemGroupEntity(2, 'Bar', 'Group1'); + + $this->em->persist($item1); + $this->em->persist($item2); + + $choiceList = new EntityChoiceList( + $this->em, + self::ITEM_GROUP_CLASS, + 'name', + null, + array( + $item1, + $item2, + ), + array(), + 'child.that.does.not.exist' + ); + + $this->assertEquals(array( + 1 => $item1, + 2 => $item2 + ), $choiceList->getChoices()); + } + + public function testPossibleToProvideShorthandEntityName() + { + $shorthandName = 'SymfonyTestsDoctrine:SingleIdentEntity'; + + $item1 = new SingleIdentEntity(1, 'Foo'); + $item2 = new SingleIdentEntity(2, 'Bar'); + + $this->em->persist($item1); + $this->em->persist($item2); + + $choiceList = new EntityChoiceList( + $this->em, + $shorthandName, + null, + null, + null, + array(), + null + ); + + $this->assertEquals(array(1, 2), $choiceList->getValuesForChoices(array($item1, $item2))); + $this->assertEquals(array(1, 2), $choiceList->getIndicesForChoices(array($item1, $item2))); + } + + // Ticket #3446 + public function testGetEmptyArrayChoicesForEmptyValues() + { + $qb = $this->em->createQueryBuilder()->select('s')->from(self::SINGLE_IDENT_CLASS, 's'); + $entityLoader = new ORMQueryBuilderLoader($qb); + $choiceList = new EntityChoiceList( + $this->em, + self::SINGLE_IDENT_CLASS, + null, + $entityLoader + ); + + $this->assertEquals(array(), $choiceList->getChoicesForValues(array())); + } + + // https://github.com/symfony/symfony/issues/3635 + public function testSingleNonIntIdFallsBackToGeneration() + { + $entity1 = new SingleStringIdentEntity('Id 1', 'Foo'); + $entity2 = new SingleStringIdentEntity('Id 2', 'Bar'); + + // Persist for managed state + $this->em->persist($entity1); + $this->em->persist($entity2); + $this->em->flush(); + + $choiceList = new EntityChoiceList( + $this->em, + self::SINGLE_STRING_IDENT_CLASS, + 'name' + ); + + $this->assertSame(array(0 => $entity1, 1 => $entity2), $choiceList->getChoices()); + } + + public function testMinusReplacedByUnderscoreInNegativeIntIds() + { + $entity1 = new SingleIdentEntity(-1, 'Foo'); + $entity2 = new SingleIdentEntity(1, 'Bar'); + + // Persist for managed state + $this->em->persist($entity1); + $this->em->persist($entity2); + $this->em->flush(); + + $choiceList = new EntityChoiceList( + $this->em, + self::SINGLE_IDENT_CLASS, + 'name' + ); + + $this->assertSame(array('_1' => $entity1, 1 => $entity2), $choiceList->getChoices()); + $this->assertSame(array('_1', 1), $choiceList->getIndicesForChoices(array($entity1, $entity2))); + $this->assertSame(array('_1', 1), $choiceList->getIndicesForValues(array('-1', '1'))); + } + + public function testMinusReplacedByUnderscoreIfNotLoaded() + { + $entity1 = new SingleIdentEntity(-1, 'Foo'); + $entity2 = new SingleIdentEntity(1, 'Bar'); + + // Persist for managed state + $this->em->persist($entity1); + $this->em->persist($entity2); + $this->em->flush(); + + $choiceList = new EntityChoiceList( + $this->em, + self::SINGLE_IDENT_CLASS, + 'name' + ); + + // no getChoices()! + + $this->assertSame(array('_1', 1), $choiceList->getIndicesForChoices(array($entity1, $entity2))); + $this->assertSame(array('_1', 1), $choiceList->getIndicesForValues(array('-1', '1'))); + } +} 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..82c587a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.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\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..27a3951 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.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\Form\Type; + +use Symfony\Component\Form\Test\FormPerformanceTestCase; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity; +use Doctrine\ORM\Tools\SchemaTool; +use Symfony\Bridge\Doctrine\Tests\DoctrineOrmTestCase; +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\SingleIdentEntity'; + + /** + * @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() + { + if (!class_exists('Symfony\Component\Form\Form')) { + $this->markTestSkipped('The "Form" component is not available'); + } + + if (!class_exists('Doctrine\DBAL\Platforms\MySqlPlatform')) { + $this->markTestSkipped('Doctrine DBAL is not available.'); + } + + if (!class_exists('Doctrine\Common\Version')) { + $this->markTestSkipped('Doctrine Common is not available.'); + } + + if (!class_exists('Doctrine\ORM\EntityManager')) { + $this->markTestSkipped('Doctrine ORM is not available.'); + } + + $this->em = DoctrineOrmTestCase::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 SingleIdentEntity($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('entity', 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('entity', 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('entity', 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..427323f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -0,0 +1,771 @@ + + * + * 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\Exception\UnexpectedTypeException; +use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase; +use Symfony\Bridge\Doctrine\Tests\DoctrineOrmTestCase; +use Symfony\Bridge\Doctrine\Tests\Fixtures\ItemGroupEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdentEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeStringIdentEntity; +use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension; +use Doctrine\ORM\Tools\SchemaTool; +use Doctrine\Common\Collections\ArrayCollection; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class EntityTypeTest extends TypeTestCase +{ + const ITEM_GROUP_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\ItemGroupEntity'; + const SINGLE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity'; + const SINGLE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdentEntity'; + const COMPOSITE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity'; + const COMPOSITE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeStringIdentEntity'; + + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $emRegistry; + + protected function setUp() + { + if (!class_exists('Symfony\Component\Form\Form')) { + $this->markTestSkipped('The "Form" component is not available'); + } + + if (!class_exists('Doctrine\DBAL\Platforms\MySqlPlatform')) { + $this->markTestSkipped('Doctrine DBAL is not available.'); + } + + if (!class_exists('Doctrine\Common\Version')) { + $this->markTestSkipped('Doctrine Common is not available.'); + } + + if (!class_exists('Doctrine\ORM\EntityManager')) { + $this->markTestSkipped('Doctrine ORM is not available.'); + } + + $this->em = DoctrineOrmTestCase::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_STRING_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', 'entity'); + } + + public function testSetDataToUninitializedEntityWithNonRequired() + { + $entity1 = new SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'required' => false, + 'property' => '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 SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'entity', 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 SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + $qb = $this->em->createQueryBuilder()->select('e')->from(self::SINGLE_IDENT_CLASS, 'e'); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'required' => false, + 'property' => '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\Form\Exception\UnexpectedTypeException + */ + public function testConfigureQueryBuilderWithNonQueryBuilderAndNonClosure() + { + $field = $this->factory->createNamed('name', 'entity', 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', 'entity', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => function () { + return new \stdClass(); + }, + )); + + $field->submit('2'); + } + + public function testSetDataSingleNull() + { + $field = $this->factory->createNamed('name', 'entity', 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', 'entity', 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', 'entity', 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', 'entity', null, array( + 'multiple' => false, + 'expanded' => true, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )); + $field->submit(null); + + $this->assertNull($field->getData()); + $this->assertSame(array(), $field->getViewData()); + } + + public function testSubmitSingleNonExpandedNull() + { + $field = $this->factory->createNamed('name', 'entity', 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', 'entity', 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 SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'multiple' => false, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'property' => 'name', + )); + + $field->submit('2'); + + $this->assertTrue($field->isSynchronized()); + $this->assertSame($entity2, $field->getData()); + $this->assertSame('2', $field->getViewData()); + } + + public function testSubmitSingleNonExpandedCompositeIdentifier() + { + $entity1 = new CompositeIdentEntity(10, 20, 'Foo'); + $entity2 = new CompositeIdentEntity(30, 40, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'multiple' => false, + 'expanded' => false, + 'em' => 'default', + 'class' => self::COMPOSITE_IDENT_CLASS, + 'property' => '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 SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + $entity3 = new SingleIdentEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'property' => '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 SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + $entity3 = new SingleIdentEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'property' => '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 CompositeIdentEntity(10, 20, 'Foo'); + $entity2 = new CompositeIdentEntity(30, 40, 'Bar'); + $entity3 = new CompositeIdentEntity(50, 60, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::COMPOSITE_IDENT_CLASS, + 'property' => '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 CompositeIdentEntity(10, 20, 'Foo'); + $entity2 = new CompositeIdentEntity(30, 40, 'Bar'); + $entity3 = new CompositeIdentEntity(50, 60, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::COMPOSITE_IDENT_CLASS, + 'property' => '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 SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'multiple' => false, + 'expanded' => true, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'property' => '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 SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + $entity3 = new SingleIdentEntity(3, 'Bar'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'multiple' => true, + 'expanded' => true, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'property' => '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 testOverrideChoices() + { + $entity1 = new SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + $entity3 = new SingleIdentEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + // not all persisted entities should be displayed + 'choices' => array($entity1, $entity2), + 'property' => '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 ItemGroupEntity(1, 'Foo', 'Group1'); + $item2 = new ItemGroupEntity(2, 'Bar', 'Group1'); + $item3 = new ItemGroupEntity(3, 'Baz', 'Group2'); + $item4 = new ItemGroupEntity(4, 'Boo!', null); + + $this->persist(array($item1, $item2, $item3, $item4)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::ITEM_GROUP_CLASS, + 'choices' => array($item1, $item2, $item3, $item4), + 'property' => 'name', + 'group_by' => 'groupName', + )); + + $field->submit('2'); + + $this->assertSame('2', $field->getViewData()); + $this->assertEquals(array( + 'Group1' => array(1 => new ChoiceView($item1, '1', 'Foo'), 2 => new ChoiceView($item2, '2', 'Bar')), + 'Group2' => array(3 => new ChoiceView($item3, '3', 'Baz')), + '4' => new ChoiceView($item4, '4', 'Boo!') + ), $field->createView()->vars['choices']); + } + + public function testPreferredChoices() + { + $entity1 = new SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + $entity3 = new SingleIdentEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'preferred_choices' => array($entity3, $entity2), + 'property' => '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 SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + $entity3 = new SingleIdentEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'choices' => array($entity2, $entity3), + 'preferred_choices' => array($entity3), + 'property' => '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 SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + $entity3 = new SingleIdentEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'choices' => array($entity1, $entity2), + 'property' => 'name', + )); + + $field->submit('3'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testDisallowChoicesThatAreNotIncludedChoicesCompositeIdentifier() + { + $entity1 = new CompositeIdentEntity(10, 20, 'Foo'); + $entity2 = new CompositeIdentEntity(30, 40, 'Bar'); + $entity3 = new CompositeIdentEntity(50, 60, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::COMPOSITE_IDENT_CLASS, + 'choices' => array($entity1, $entity2), + 'property' => 'name', + )); + + $field->submit('2'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleIdentifier() + { + $entity1 = new SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + $entity3 = new SingleIdentEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $repository = $this->em->getRepository(self::SINGLE_IDENT_CLASS); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => $repository->createQueryBuilder('e') + ->where('e.id IN (1, 2)'), + 'property' => 'name', + )); + + $field->submit('3'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureSingleIdentifier() + { + $entity1 = new SingleIdentEntity(1, 'Foo'); + $entity2 = new SingleIdentEntity(2, 'Bar'); + $entity3 = new SingleIdentEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => function ($repository) { + return $repository->createQueryBuilder('e') + ->where('e.id IN (1, 2)'); + }, + 'property' => 'name', + )); + + $field->submit('3'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureCompositeIdentifier() + { + $entity1 = new CompositeIdentEntity(10, 20, 'Foo'); + $entity2 = new CompositeIdentEntity(30, 40, 'Bar'); + $entity3 = new CompositeIdentEntity(50, 60, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::COMPOSITE_IDENT_CLASS, + 'query_builder' => function ($repository) { + return $repository->createQueryBuilder('e') + ->where('e.id1 IN (10, 50)'); + }, + 'property' => 'name', + )); + + $field->submit('2'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testSubmitSingleStringIdentifier() + { + $entity1 = new SingleStringIdentEntity('foo', 'Foo'); + + $this->persist(array($entity1)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'multiple' => false, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_STRING_IDENT_CLASS, + 'property' => 'name', + )); + + $field->submit('foo'); + + $this->assertTrue($field->isSynchronized()); + $this->assertSame($entity1, $field->getData()); + $this->assertSame('foo', $field->getViewData()); + } + + public function testSubmitCompositeStringIdentifier() + { + $entity1 = new CompositeStringIdentEntity('foo1', 'foo2', 'Foo'); + + $this->persist(array($entity1)); + + $field = $this->factory->createNamed('name', 'entity', null, array( + 'multiple' => false, + 'expanded' => false, + 'em' => 'default', + 'class' => self::COMPOSITE_STRING_IDENT_CLASS, + 'property' => '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', 'entity', null, array( + 'class' => self::SINGLE_IDENT_CLASS, + 'required' => false, + 'property' => 'name' + )); + } + + 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..8e51f40 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.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\HttpFoundation; + +use Symfony\Bridge\Doctrine\HttpFoundation\DbalSessionHandler; + +/** + * Test class for DbalSessionHandler. + * + * @author Drak + */ +class DbalSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testConstruct() + { + $this->connection = $this->getMock('Doctrine\DBAL\Driver\Connection'); + $mock = $this->getMockBuilder('Symfony\Bridge\Doctrine\HttpFoundation\DbalSessionHandler'); + $mock->setConstructorArgs(array($this->connection)); + $this->driver = $mock->getMock(); + } +} 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..741c894 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.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\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')) + ); + } + + 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 testLogLongString() + { + $logger = $this->getMock('Symfony\\Component\\HttpKernel\\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() + { + if (!function_exists('mb_detect_encoding')) { + $this->markTestSkipped('Testing log shortening of utf8 charsets requires the mb_detect_encoding() function.'); + } + + $logger = $this->getMock('Symfony\\Component\\HttpKernel\\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, mb_detect_encoding($longString)).' [...]')) + ; + + $dbalLogger->startQuery('SQL', array( + 'short' => $shortString, + 'long' => $longString, + )); + } + +} 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..3df1be5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.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\Bridge\Doctrine\Tests\Security\User; + +use Symfony\Bridge\Doctrine\Tests\DoctrineOrmTestCase; +use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity; +use Symfony\Bridge\Doctrine\Security\User\EntityUserProvider; +use Doctrine\ORM\Tools\SchemaTool; + +class EntityUserProviderTest extends DoctrineOrmTestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\Security\Core\SecurityContext')) { + $this->markTestSkipped('The "Security" component is not available'); + } + + parent::setUp(); + } + + public function testRefreshUserGetsUserByPrimaryKey() + { + $em = $this->createTestEntityManager(); + $this->createSchema($em); + + $user1 = new CompositeIdentEntity(1, 1, 'user1'); + $user2 = new CompositeIdentEntity(1, 2, 'user2'); + + $em->persist($user1); + $em->persist($user2); + $em->flush(); + + $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity', 'name'); + + // try to change the user identity + $user1->name = 'user2'; + + $this->assertSame($user1, $provider->refreshUser($user1)); + } + + public function testRefreshUserRequiresId() + { + $em = $this->createTestEntityManager(); + + $user1 = new CompositeIdentEntity(null, null, 'user1'); + $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity', '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 = $this->createTestEntityManager(); + $this->createSchema($em); + + $user1 = new CompositeIdentEntity(1, 1, 'user1'); + + $em->persist($user1); + $em->flush(); + + $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity', 'name'); + + $user2 = new CompositeIdentEntity(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 = $this->createTestEntityManager(); + $this->createSchema($em); + + $user1 = new CompositeIdentEntity(1, 1, 'user1'); + + $em->persist($user1); + $em->flush(); + $em->clear(); + + $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity', 'name'); + + $user2 = $em->getReference('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity', array('id1' => 1, 'id2' => 1)); + $this->assertTrue($provider->supportsClass(get_class($user2))); + } + + private function getManager($em, $name = null) + { + $manager = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $manager->expects($this->once()) + ->method('getManager') + ->with($this->equalTo($name)) + ->will($this->returnValue($em)); + + return $manager; + } + + private function createSchema($em) + { + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema(array( + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity'), + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueValidatorTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueValidatorTest.php new file mode 100644 index 0000000..2c5af40 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueValidatorTest.php @@ -0,0 +1,370 @@ + + * + * 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 Symfony\Bridge\Doctrine\Tests\DoctrineOrmTestCase; +use Symfony\Component\Validator\DefaultTranslator; +use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleIdentEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity; +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\Mapping\ClassMetadata; +use Symfony\Component\Validator\Validator; +use Doctrine\ORM\Tools\SchemaTool; + +class UniqueValidatorTest extends DoctrineOrmTestCase +{ + protected function setUp() + { + parent::setUp(); + + if (!class_exists('Symfony\Component\Security\Core\SecurityContext')) { + $this->markTestSkipped('The "Security" component is not available'); + } + + if (!class_exists('Symfony\Component\Validator\Constraint')) { + $this->markTestSkipped('The "Validator" component is not available'); + } + } + + protected function createRegistryMock($entityManagerName, $em) + { + $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $registry->expects($this->any()) + ->method('getManager') + ->with($this->equalTo($entityManagerName)) + ->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)) + ; + $refl = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionProperty') + ->disableOriginalConstructor() + ->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 createValidatorFactory($uniqueValidator) + { + $validatorFactory = $this->getMock('Symfony\Component\Validator\ConstraintValidatorFactoryInterface'); + $validatorFactory->expects($this->any()) + ->method('getInstance') + ->with($this->isInstanceOf('Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity')) + ->will($this->returnValue($uniqueValidator)); + + return $validatorFactory; + } + + public function createValidator($entityManagerName, $em, $validateClass = null, $uniqueFields = null, $errorPath = null, $repositoryMethod = 'findBy', $ignoreNull = true) + { + if (!$validateClass) { + $validateClass = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity'; + } + if (!$uniqueFields) { + $uniqueFields = array('name'); + } + + $registry = $this->createRegistryMock($entityManagerName, $em); + + $uniqueValidator = new UniqueEntityValidator($registry); + + $metadata = new ClassMetadata($validateClass); + $constraint = new UniqueEntity(array( + 'fields' => $uniqueFields, + 'em' => $entityManagerName, + 'errorPath' => $errorPath, + 'repositoryMethod' => $repositoryMethod, + 'ignoreNull' => $ignoreNull + )); + $metadata->addConstraint($constraint); + + $metadataFactory = new FakeMetadataFactory(); + $metadataFactory->addMetadata($metadata); + $validatorFactory = $this->createValidatorFactory($uniqueValidator); + + return new Validator($metadataFactory, $validatorFactory, new DefaultTranslator()); + } + + private function createSchema($em) + { + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema(array( + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity'), + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleIdentEntity'), + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity'), + $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() + { + $entityManagerName = "foo"; + $em = $this->createTestEntityManager(); + $this->createSchema($em); + $validator = $this->createValidator($entityManagerName, $em); + + $entity1 = new SingleIdentEntity(1, 'Foo'); + $violationsList = $validator->validate($entity1); + $this->assertEquals(0, $violationsList->count(), "No violations found on entity before it is saved to the database."); + + $em->persist($entity1); + $em->flush(); + + $violationsList = $validator->validate($entity1); + $this->assertEquals(0, $violationsList->count(), "No violations found on entity after it was saved to the database."); + + $entity2 = new SingleIdentEntity(2, 'Foo'); + + $violationsList = $validator->validate($entity2); + $this->assertEquals(1, $violationsList->count(), "Violation found on entity with conflicting entity existing in the database."); + + $violation = $violationsList[0]; + $this->assertEquals('This value is already used.', $violation->getMessage()); + $this->assertEquals('name', $violation->getPropertyPath()); + $this->assertEquals('Foo', $violation->getInvalidValue()); + } + + public function testValidateCustomErrorPath() + { + $entityManagerName = "foo"; + $em = $this->createTestEntityManager(); + $this->createSchema($em); + $validator = $this->createValidator($entityManagerName, $em, null, null, 'bar'); + + $entity1 = new SingleIdentEntity(1, 'Foo'); + + $em->persist($entity1); + $em->flush(); + + $entity2 = new SingleIdentEntity(2, 'Foo'); + + $violationsList = $validator->validate($entity2); + $this->assertEquals(1, $violationsList->count(), "Violation found on entity with conflicting entity existing in the database."); + + $violation = $violationsList[0]; + $this->assertEquals('This value is already used.', $violation->getMessage()); + $this->assertEquals('bar', $violation->getPropertyPath()); + $this->assertEquals('Foo', $violation->getInvalidValue()); + } + + public function testValidateUniquenessWithNull() + { + $entityManagerName = "foo"; + $em = $this->createTestEntityManager(); + $this->createSchema($em); + $validator = $this->createValidator($entityManagerName, $em); + + $entity1 = new SingleIdentEntity(1, null); + $entity2 = new SingleIdentEntity(2, null); + + $em->persist($entity1); + $em->persist($entity2); + $em->flush(); + + $violationsList = $validator->validate($entity1); + $this->assertEquals(0, $violationsList->count(), "No violations found on entity having a null value."); + } + + public function testValidateUniquenessWithIgnoreNull() + { + $entityManagerName = "foo"; + $validateClass = 'Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleIdentEntity'; + $em = $this->createTestEntityManager(); + $this->createSchema($em); + $validator = $this->createValidator($entityManagerName, $em, $validateClass, array('name', 'name2'), 'bar', 'findby', false); + + $entity1 = new DoubleIdentEntity(1, 'Foo', null); + $violationsList = $validator->validate($entity1); + $this->assertEquals(0, $violationsList->count(), "No violations found on entity before it is saved to the database."); + + $em->persist($entity1); + $em->flush(); + + $violationsList = $validator->validate($entity1); + $this->assertEquals(0, $violationsList->count(), "No violations found on entity after it was saved to the database."); + + $entity2 = new DoubleIdentEntity(2, 'Foo', null); + + $violationsList = $validator->validate($entity2); + $this->assertEquals(1, $violationsList->count(), "Violation found on entity with conflicting entity existing in the database."); + + $violation = $violationsList[0]; + $this->assertEquals('This value is already used.', $violation->getMessage()); + $this->assertEquals('bar', $violation->getPropertyPath()); + $this->assertEquals('Foo', $violation->getInvalidValue()); + } + + public function testValidateUniquenessAfterConsideringMultipleQueryResults() + { + $entityManagerName = "foo"; + $em = $this->createTestEntityManager(); + $this->createSchema($em); + $validator = $this->createValidator($entityManagerName, $em); + + $entity1 = new SingleIdentEntity(1, 'foo'); + $entity2 = new SingleIdentEntity(2, 'foo'); + + $em->persist($entity1); + $em->persist($entity2); + $em->flush(); + + $violationsList = $validator->validate($entity1); + $this->assertEquals(1, $violationsList->count(), 'Violation found on entity with conflicting entity existing in the database.'); + + $violationsList = $validator->validate($entity2); + $this->assertEquals(1, $violationsList->count(), 'Violation found on entity with conflicting entity existing in the database.'); + } + + public function testValidateUniquenessUsingCustomRepositoryMethod() + { + $entityManagerName = 'foo'; + $repository = $this->createRepositoryMock(); + $repository->expects($this->once()) + ->method('findByCustom') + ->will($this->returnValue(array())) + ; + $em = $this->createEntityManagerMock($repository); + $validator = $this->createValidator($entityManagerName, $em, null, array(), null, 'findByCustom'); + + $entity1 = new SingleIdentEntity(1, 'foo'); + + $violationsList = $validator->validate($entity1); + $this->assertEquals(0, $violationsList->count(), 'Violation is using custom repository method.'); + } + + public function testValidateUniquenessWithUnrewoundArray() + { + $entity = new SingleIdentEntity(1, 'foo'); + + $entityManagerName = 'foo'; + $repository = $this->createRepositoryMock(); + $repository->expects($this->once()) + ->method('findByCustom') + ->will( + $this->returnCallback(function() use ($entity) { + $returnValue = array( + $entity, + ); + next($returnValue); + + return $returnValue; + }) + ) + ; + $em = $this->createEntityManagerMock($repository); + $validator = $this->createValidator($entityManagerName, $em, null, array(), null, 'findByCustom'); + + $violationsList = $validator->validate($entity); + $this->assertCount(0, $violationsList, 'Violation is using unrewound array as return value in the repository method.'); + } + + /** + * @group GH-1635 + */ + public function testAssociatedEntity() + { + $entityManagerName = "foo"; + $em = $this->createTestEntityManager(); + $this->createSchema($em); + $validator = $this->createValidator($entityManagerName, $em, 'Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity', array('single')); + + $entity1 = new SingleIdentEntity(1, 'foo'); + $associated = new AssociationEntity(); + $associated->single = $entity1; + + $em->persist($entity1); + $em->persist($associated); + $em->flush(); + + $violationsList = $validator->validate($associated); + $this->assertEquals(0, $violationsList->count()); + + $associated2 = new AssociationEntity(); + $associated2->single = $entity1; + + $em->persist($associated2); + $em->flush(); + + $violationsList = $validator->validate($associated2); + $this->assertEquals(1, $violationsList->count()); + } + + /** + * @group GH-1635 + */ + public function testAssociatedCompositeEntity() + { + $entityManagerName = "foo"; + $em = $this->createTestEntityManager(); + $this->createSchema($em); + $validator = $this->createValidator($entityManagerName, $em, 'Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity', array('composite')); + + $composite = new CompositeIdentEntity(1, 1, "test"); + $associated = new AssociationEntity(); + $associated->composite = $composite; + + $em->persist($composite); + $em->persist($associated); + $em->flush(); + + $this->setExpectedException( + 'Symfony\Component\Validator\Exception\ConstraintDefinitionException', + 'Associated entities are not allowed to have more than one identifier field' + ); + $violationsList = $validator->validate($associated); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/bootstrap.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/bootstrap.php new file mode 100644 index 0000000..7912352 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/bootstrap.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +$loader = require __DIR__.'/../vendor/autoload.php'; + +Doctrine\Common\Annotations\AnnotationRegistry::registerLoader(array($loader, 'loadClass')); 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..f211f61 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.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\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; + +/** + * Constraint for the Unique Entity validator + * + * @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..0d339af --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.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\Bridge\Doctrine\Validator\Constraints; + +use Doctrine\Common\Persistence\ManagerRegistry; +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; + + /** + * @param ManagerRegistry $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 (!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); + } else { + $em = $this->registry->getManagerForClass(get_class($entity)); + } + + $className = $this->context->getClassName(); + $class = $em->getClassMetadata($className); + /* @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 ($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($className); + $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]; + + $this->context->addViolationAt($errorPath, $constraint->message, array(), $criteria[$fields[0]]); + } +} 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..e657db7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/composer.json @@ -0,0 +1,50 @@ +{ + "name": "symfony/doctrine-bridge", + "type": "symfony-bridge", + "description": "Symfony Doctrine Bridge", + "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.3", + "doctrine/common": "~2.2" + }, + "require-dev": { + "symfony/stopwatch": "~2.2", + "symfony/dependency-injection": "~2.0", + "symfony/form": "~2.2", + "symfony/http-kernel": "~2.2", + "symfony/security": "~2.2", + "symfony/validator": "~2.2", + "doctrine/data-fixtures": "1.0.*", + "doctrine/dbal": "~2.2", + "doctrine/orm": "~2.2,>=2.2.3" + }, + "suggest": { + "symfony/form": "", + "symfony/validator": "", + "doctrine/data-fixtures": "", + "doctrine/dbal": "", + "doctrine/orm": "" + }, + "autoload": { + "psr-0": { "Symfony\\Bridge\\Doctrine\\": "" } + }, + "target-dir": "Symfony/Bridge/Doctrine", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..c8f58f3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + + + + 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..88ecedd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +2.1.0 +----- + + * added ChromePhpHandler 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..81766d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.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\Bridge\Monolog\Handler; + +use Monolog\Handler\ChromePHPHandler as BaseChromePhpHandler; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * 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 (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + 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/DebugHandler.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/DebugHandler.php new file mode 100644 index 0000000..7ba04ab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/DebugHandler.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\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'], + ); + } + + 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/FirePHPHandler.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php new file mode 100644 index 0000000..f36cd9f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.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\Monolog\Handler; + +use Monolog\Handler\FirePHPHandler as BaseFirePHPHandler; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * 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 (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + 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/LICENSE b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Bridge/Monolog/Logger.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Logger.php new file mode 100644 index 0000000..b675069 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Logger.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\Monolog; + +use Monolog\Logger as BaseLogger; +use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; + +/** + * Logger. + * + * @author Fabien Potencier + */ +class Logger extends BaseLogger implements LoggerInterface, DebugLoggerInterface +{ + /** + * @deprecated since 2.2, to be removed in 3.0. Use emergency() which is PSR-3 compatible. + */ + public function emerg($message, array $context = array()) + { + return parent::addRecord(BaseLogger::EMERGENCY, $message, $context); + } + + /** + * @deprecated since 2.2, to be removed in 3.0. Use critical() which is PSR-3 compatible. + */ + public function crit($message, array $context = array()) + { + return parent::addRecord(BaseLogger::CRITICAL, $message, $context); + } + + /** + * @deprecated since 2.2, to be removed in 3.0. Use error() which is PSR-3 compatible. + */ + public function err($message, array $context = array()) + { + return parent::addRecord(BaseLogger::ERROR, $message, $context); + } + + /** + * @deprecated since 2.2, to be removed in 3.0. Use warning() which is PSR-3 compatible. + */ + public function warn($message, array $context = array()) + { + return parent::addRecord(BaseLogger::WARNING, $message, $context); + } + + /** + * @see Symfony\Component\HttpKernel\Log\DebugLoggerInterface + */ + public function getLogs() + { + if ($logger = $this->getDebugLogger()) { + return $logger->getLogs(); + } + + return array(); + } + + /** + * @see Symfony\Component\HttpKernel\Log\DebugLoggerInterface + */ + 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..aac05d2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Processor/WebProcessor.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\Monolog\Processor; + +use Monolog\Processor\WebProcessor as BaseWebProcessor; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * WebProcessor override to read from the HttpFoundation's Request + * + * @author Jordi Boggiano + */ +class WebProcessor extends BaseWebProcessor +{ + public function __construct() + { + // Pass an empty array as the default null value would access $_SERVER + parent::__construct(array()); + } + + public function onKernelRequest(GetResponseEvent $event) + { + if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) { + $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..4e2253c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/README.md @@ -0,0 +1,13 @@ +Monolog Bridge +============== + +Provides integration for Monolog with various Symfony2 components. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Bridge/Monolog/ + $ composer.phar install --dev + $ phpunit 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..ae3379b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.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\Bridge\Monolog\Tests\Processor; + +use Monolog\Logger; +use Symfony\Bridge\Monolog\Processor\WebProcessor; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class WebProcessorTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Monolog\\Logger')) { + $this->markTestSkipped('Monolog is not available.'); + } + } + + public function testUsesRequestServerData() + { + $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('getRequestType') + ->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST)); + $event->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)); + + $processor = new WebProcessor(); + $processor->onKernelRequest($event); + $record = $processor($this->getRecord()); + + $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']); + } + + /** + * @param integer $level + * @param string $message + * + * @return array Record + */ + protected 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(), + ); + } +} 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..643bd92 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/monolog-bridge", + "type": "symfony-bridge", + "description": "Symfony Monolog Bridge", + "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.3", + "symfony/http-kernel": "~2.2", + "monolog/monolog": "~1.3" + }, + "autoload": { + "psr-0": { "Symfony\\Bridge\\Monolog\\": "" } + }, + "target-dir": "Symfony/Bridge/Monolog", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..9dca648 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/CHANGELOG.md new file mode 100644 index 0000000..242d576 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/CHANGELOG.md @@ -0,0 +1,15 @@ +CHANGELOG +========= + +2.2.0 +----- + + * added a collection type for the I18n behavior + * added an optional PropertyAccessorInterface parameter to ModelType and + ModelChoiceList + * [BC BREAK] ModelType now has a constructor + +2.1.0 +----- + + * added the bridge diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/DataCollector/PropelDataCollector.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/DataCollector/PropelDataCollector.php new file mode 100644 index 0000000..bbb15fb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/DataCollector/PropelDataCollector.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\Propel1\DataCollector; + +use Symfony\Bridge\Propel1\Logger\PropelLogger; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; + +/** + * The PropelDataCollector collector class collects information. + * + * @author William Durand + */ +class PropelDataCollector extends DataCollector +{ + /** + * Propel logger + * + * @var \Symfony\Bridge\Propel1\Logger\PropelLogger + */ + private $logger; + + /** + * Propel configuration + * + * @var \PropelConfiguration + */ + protected $propelConfiguration; + + /** + * Constructor + * + * @param PropelLogger $logger A Propel logger. + * @param \PropelConfiguration $propelConfiguration The Propel configuration object. + */ + public function __construct(PropelLogger $logger, \PropelConfiguration $propelConfiguration) + { + $this->logger = $logger; + $this->propelConfiguration = $propelConfiguration; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $this->data = array( + 'queries' => $this->buildQueries(), + 'querycount' => $this->countQueries(), + ); + } + + /** + * Returns the collector name. + * + * @return string The collector name. + */ + public function getName() + { + return 'propel'; + } + + /** + * Returns queries. + * + * @return array Queries + */ + public function getQueries() + { + return $this->data['queries']; + } + + /** + * Returns the query count. + * + * @return int The query count + */ + public function getQueryCount() + { + return $this->data['querycount']; + } + + /** + * Returns the total time of queries. + * + * @return float The total time of queries + */ + public function getTime() + { + $time = 0; + foreach ($this->data['queries'] as $query) { + $time += (float) $query['time']; + } + + return $time; + } + + /** + * Creates an array of Build objects. + * + * @return array An array of Build objects + */ + private function buildQueries() + { + $queries = array(); + + $outerGlue = $this->propelConfiguration->getParameter('debugpdo.logging.outerglue', ' | '); + $innerGlue = $this->propelConfiguration->getParameter('debugpdo.logging.innerglue', ': '); + + foreach ($this->logger->getQueries() as $q) { + $parts = explode($outerGlue, $q, 4); + + $times = explode($innerGlue, $parts[0]); + $con = explode($innerGlue, $parts[2]); + $memories = explode($innerGlue, $parts[1]); + + $sql = trim($parts[3]); + $con = trim($con[1]); + $time = trim($times[1]); + $memory = trim($memories[1]); + + $queries[] = array('connection' => $con, 'sql' => $sql, 'time' => $time, 'memory' => $memory); + } + + return $queries; + } + + /** + * Count queries. + * + * @return int The number of queries. + */ + private function countQueries() + { + return count($this->logger->getQueries()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/DependencyInjection/Security/UserProvider/PropelFactory.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/DependencyInjection/Security/UserProvider/PropelFactory.php new file mode 100644 index 0000000..2706d34 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/DependencyInjection/Security/UserProvider/PropelFactory.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\Propel1\DependencyInjection\Security\UserProvider; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface; +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * PropelFactory creates services for Propel user provider. + * + * @author William Durand + */ +class PropelFactory 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']) + ; + } + + public function getKey() + { + return $this->key; + } + + public function addConfiguration(NodeDefinition $node) + { + $node + ->children() + ->scalarNode('class')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('property')->defaultNull()->end() + ->end() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/ChoiceList/ModelChoiceList.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/ChoiceList/ModelChoiceList.php new file mode 100644 index 0000000..a29ae0c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/ChoiceList/ModelChoiceList.php @@ -0,0 +1,430 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Propel1\Form\ChoiceList; + +use \ModelCriteria; +use \BaseObject; +use \Persistent; + +use Symfony\Component\Form\Exception\StringCastException; +use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * Widely inspired by the EntityChoiceList. + * + * @author William Durand + * @author Toni Uebernickel + */ +class ModelChoiceList extends ObjectChoiceList +{ + /** + * The fields of which the identifier of the underlying class consists + * + * This property should only be accessed through identifier. + * + * @var array + */ + protected $identifier = array(); + + /** + * The query to retrieve the choices of this list. + * + * @var ModelCriteria + */ + protected $query; + + /** + * The query to retrieve the preferred choices for this list. + * + * @var ModelCriteria + */ + protected $preferredQuery; + + /** + * Whether the model objects have already been loaded. + * + * @var Boolean + */ + protected $loaded = false; + + /** + * Whether to use the identifier for index generation + * + * @var Boolean + */ + private $identifierAsIndex = false; + + /** + * Constructor. + * + * @see Symfony\Bridge\Propel1\Form\Type\ModelType How to use the preferred choices. + * + * @param string $class The FQCN of the model class to be loaded. + * @param string $labelPath A property path pointing to the property used for the choice labels. + * @param array $choices An optional array to use, rather than fetching the models. + * @param ModelCriteria $queryObject The query to use retrieving model data from database. + * @param string $groupPath A property path pointing to the property used to group the choices. + * @param array|ModelCriteria $preferred The preferred items of this choice. + * Either an array if $choices is given, + * or a ModelCriteria to be merged with the $queryObject. + * @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths. + */ + public function __construct($class, $labelPath = null, $choices = null, $queryObject = null, $groupPath = null, $preferred = array(), PropertyAccessorInterface $propertyAccessor = null) + { + $this->class = $class; + + $queryClass = $this->class.'Query'; + $query = new $queryClass(); + + $this->identifier = $query->getTableMap()->getPrimaryKeys(); + $this->query = $queryObject ?: $query; + $this->loaded = is_array($choices) || $choices instanceof \Traversable; + + if ($preferred instanceof ModelCriteria) { + $this->preferredQuery = $preferred->mergeWith($this->query); + } + + if (!$this->loaded) { + // Make sure the constraints of the parent constructor are + // fulfilled + $choices = array(); + $preferred = array(); + } + + if (1 === count($this->identifier) && $this->isInteger(current($this->identifier))) { + $this->identifierAsIndex = true; + } + + parent::__construct($choices, $labelPath, $preferred, $groupPath, null, $propertyAccessor); + } + + /** + * Returns the class name + * + * @return string + */ + public function getClass() + { + return $this->class; + } + + /** + * Returns the list of model objects + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getChoices() + { + if (!$this->loaded) { + $this->load(); + } + + return parent::getChoices(); + } + + /** + * Returns the values for the model objects + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getValues() + { + if (!$this->loaded) { + $this->load(); + } + + return parent::getValues(); + } + + /** + * Returns the choice views of the preferred choices as nested array with + * the choice groups as top-level keys. + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getPreferredViews() + { + if (!$this->loaded) { + $this->load(); + } + + return parent::getPreferredViews(); + } + + /** + * Returns the choice views of the choices that are not preferred as nested + * array with the choice groups as top-level keys. + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getRemainingViews() + { + if (!$this->loaded) { + $this->load(); + } + + return parent::getRemainingViews(); + } + + /** + * Returns the model objects corresponding to the given values. + * + * @param array $values + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getChoicesForValues(array $values) + { + if (!$this->loaded) { + if (1 === count($this->identifier)) { + $filterBy = 'filterBy'.current($this->identifier)->getPhpName(); + + return (array) $this->query->create() + ->$filterBy($values) + ->find(); + } + + $this->load(); + } + + return parent::getChoicesForValues($values); + } + + /** + * Returns the values corresponding to the given model objects. + * + * @param array $models + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getValuesForChoices(array $models) + { + if (!$this->loaded) { + // 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 (1 === count($this->identifier)) { + $values = array(); + foreach ($models as $model) { + if ($model instanceof $this->class) { + // Make sure to convert to the right format + $values[] = $this->fixValue(current($this->getIdentifierValues($model))); + } + } + + return $values; + } + + $this->load(); + } + + return parent::getValuesForChoices($models); + } + + /** + * Returns the indices corresponding to the given models. + * + * @param array $models + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getIndicesForChoices(array $models) + { + $indices = array(); + + if (!$this->loaded) { + // Optimize performance for single-field identifiers. We already + // know that the IDs are used as indices + + // Attention: This optimization does not check choices for existence + if ($this->identifierAsIndex) { + foreach ($models as $model) { + if ($model instanceof $this->class) { + // Make sure to convert to the right format + $indices[] = $this->fixIndex(current($this->getIdentifierValues($model))); + } + } + + return $indices; + } + + $this->load(); + } + + /* + * Overwriting default implementation. + * + * The two objects may represent the same entry in the database, + * but if they originated from different queries, there are not the same object within the code. + * + * This happens when using m:n relations with either sides model as data_class of the form. + * The choicelist will retrieve the list of available related models with a different query, resulting in different objects. + */ + $choices = $this->fixChoices($models); + foreach ($this->getChoices() as $i => $choice) { + foreach ($choices as $j => $givenChoice) { + if ($this->getIdentifierValues($choice) === $this->getIdentifierValues($givenChoice)) { + $indices[] = $i; + unset($choices[$j]); + + if (0 === count($choices)) { + break 2; + } + } + } + } + + return $indices; + } + + /** + * Returns the models corresponding to the given values. + * + * @param array $values + * + * @return array + * + * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + public function getIndicesForValues(array $values) + { + if (!$this->loaded) { + // Optimize performance for single-field identifiers. We already + // know that the IDs are used as indices and values + + // Attention: This optimization does not check values for existence + if ($this->identifierAsIndex) { + return $this->fixIndices($values); + } + + $this->load(); + } + + return parent::getIndicesForValues($values); + } + + /** + * Creates a new unique index for this model. + * + * If the model has a single-field identifier, this identifier is used. + * + * Otherwise a new integer is generated. + * + * @param mixed $model The choice to create an index for + * + * @return integer|string A unique index containing only ASCII letters, + * digits and underscores. + */ + protected function createIndex($model) + { + if ($this->identifierAsIndex) { + return current($this->getIdentifierValues($model)); + } + + return parent::createIndex($model); + } + + /** + * Creates a new unique value for this model. + * + * If the model has a single-field identifier, this identifier is used. + * + * Otherwise a new integer is generated. + * + * @param mixed $model The choice to create a value for + * + * @return integer|string A unique value without character limitations. + */ + protected function createValue($model) + { + if (1 === count($this->identifier)) { + return (string) current($this->getIdentifierValues($model)); + } + + return parent::createValue($model); + } + + /** + * Loads the list with model objects. + */ + private function load() + { + $models = (array) $this->query->find(); + + $preferred = array(); + if ($this->preferredQuery instanceof ModelCriteria) { + $preferred = (array) $this->preferredQuery->find(); + } + + try { + // The second parameter $labels is ignored by ObjectChoiceList + parent::initialize($models, array(), $preferred); + } catch (StringCastException $e) { + throw new StringCastException(str_replace('argument $labelPath', 'option "property"', $e->getMessage()), null, $e); + } + + $this->loaded = true; + } + + /** + * Returns the values of the identifier fields of an model + * + * Propel must know about this model, that is, the model must already + * be persisted or added to the idmodel map before. Otherwise an + * exception is thrown. + * + * @param object $model The model for which to get the identifier + * + * @return array + */ + private function getIdentifierValues($model) + { + if ($model instanceof Persistent) { + return array($model->getPrimaryKey()); + } + + // readonly="true" models do not implement Persistent. + if ($model instanceof BaseObject && method_exists($model, 'getPrimaryKey')) { + return array($model->getPrimaryKey()); + } + + return $model->getPrimaryKeys(); + } + + /** + * Whether this column in an integer + * + * @param \ColumnMap $column + * + * @return Boolean + */ + private function isInteger(\ColumnMap $column) + { + return $column->getPdoType() === \PDO::PARAM_INT; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/DataTransformer/CollectionToArrayTransformer.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/DataTransformer/CollectionToArrayTransformer.php new file mode 100644 index 0000000..a9671cc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/DataTransformer/CollectionToArrayTransformer.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\Propel1\Form\DataTransformer; + +use \PropelObjectCollection; +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * CollectionToArrayTransformer class. + * + * @author William Durand + * @author Pierre-Yves Lebecq + */ +class CollectionToArrayTransformer implements DataTransformerInterface +{ + public function transform($collection) + { + if (null === $collection) { + return array(); + } + + if (!$collection instanceof PropelObjectCollection) { + throw new TransformationFailedException('Expected a \PropelObjectCollection.'); + } + + return $collection->getData(); + } + + public function reverseTransform($array) + { + $collection = new PropelObjectCollection(); + + if ('' === $array || null === $array) { + return $collection; + } + + if (!is_array($array)) { + throw new TransformationFailedException('Expected an array.'); + } + + $collection->setData($array); + + return $collection; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationCollectionFormListener.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationCollectionFormListener.php new file mode 100644 index 0000000..ae39700 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationCollectionFormListener.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\Bridge\Propel1\Form\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\FormEvent; + +/** + * listener class for propel1_translatable_collection + * + * @author Patrick Kaufmann + */ +class TranslationCollectionFormListener implements EventSubscriberInterface +{ + + private $i18nClass; + private $languages; + + public function __construct($languages, $i18nClass) + { + $this->i18nClass = $i18nClass; + $this->languages = $languages; + } + + public static function getSubscribedEvents() + { + return array( + FormEvents::PRE_SET_DATA => array('preSetData', 1), + ); + } + + public function preSetData(FormEvent $event) + { + $form = $event->getForm(); + $data = $event->getData(); + + if (null === $data) { + return; + } + + if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) { + throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)'); + } + + //get the class name of the i18nClass + $temp = explode('\\', $this->i18nClass); + $dataClass = end($temp); + + $rootData = $form->getRoot()->getData(); + $foundData = false; + + $addFunction = 'add'.$dataClass; + + //add a database row for every needed language + foreach ($this->languages as $lang) { + $found = false; + + foreach ($data as $i18n) { + if (!method_exists($i18n, 'getLocale')) { + throw new UnexpectedTypeException($i18n, 'Propel i18n object'); + } + + if ($i18n->getLocale() == $lang) { + $found = true; + break; + } + } + + if (!$found) { + $currentForm = $form; + while (!$foundData) { + if (method_exists($rootData, $addFunction)) { + $foundData = true; + break; + } elseif ($currentForm->hasParent()) { + $currentForm = $currentForm->getParent(); + $rootData = $currentForm->getData(); + } else { + break; + } + } + if (!$foundData) { + throw new UnexpectedTypeException($rootData, 'Propel i18n object'); + } + + $newTranslation = new $this->i18nClass(); + $newTranslation->setLocale($lang); + + $rootData->$addFunction($newTranslation); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationFormListener.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationFormListener.php new file mode 100644 index 0000000..3f20102 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationFormListener.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\Propel1\Form\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; + +/** + * Event Listener class for propel1_translation + * + * @author Patrick Kaufmann + */ +class TranslationFormListener implements EventSubscriberInterface +{ + private $columns; + private $dataClass; + + public function __construct($columns, $dataClass) + { + $this->columns = $columns; + $this->dataClass = $dataClass; + } + + public static function getSubscribedEvents() + { + return array( + FormEvents::PRE_SET_DATA => array('preSetData', 1), + ); + } + + public function preSetData(FormEvent $event) + { + $form = $event->getForm(); + $data = $event->getData(); + + if (!$data instanceof $this->dataClass) { + return; + } + + //loop over all columns and add the input + foreach ($this->columns as $column => $options) { + if (is_string($options)) { + $column = $options; + $options = array(); + } + if (null === $options) { + $options = array(); + } + + $type = 'text'; + if (array_key_exists('type', $options)) { + $type = $options['type']; + } + $label = $column; + if (array_key_exists('label', $options)) { + $label = $options['label']; + } + + $customOptions = array(); + if (array_key_exists('options', $options)) { + $customOptions = $options['options']; + } + $options = array( + 'label' => $label.' '.strtoupper($data->getLocale()) + ); + + $options = array_merge($options, $customOptions); + + $form->add($column, $type, $options); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/PropelExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/PropelExtension.php new file mode 100644 index 0000000..b238a5e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/PropelExtension.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\Propel1\Form; + +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\PropertyAccess\PropertyAccess; + +/** + * Represents the Propel form extension, which loads the Propel functionality. + * + * @author Joseph Rouff + */ +class PropelExtension extends AbstractExtension +{ + protected function loadTypes() + { + return array( + new Type\ModelType(PropertyAccess::getPropertyAccessor()), + new Type\TranslationCollectionType(), + new Type\TranslationType() + ); + } + + protected function loadTypeGuesser() + { + return new PropelTypeGuesser(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/PropelTypeGuesser.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/PropelTypeGuesser.php new file mode 100644 index 0000000..9f36afa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/PropelTypeGuesser.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\Propel1\Form; + +use Symfony\Component\Form\FormTypeGuesserInterface; +use Symfony\Component\Form\Guess\Guess; +use Symfony\Component\Form\Guess\TypeGuess; +use Symfony\Component\Form\Guess\ValueGuess; + +/** + * Propel Type guesser. + * + * @author Fabien Potencier + */ +class PropelTypeGuesser implements FormTypeGuesserInterface +{ + private $cache = array(); + + /** + * {@inheritDoc} + */ + public function guessType($class, $property) + { + if (!$table = $this->getTable($class)) { + return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); + } + + foreach ($table->getRelations() as $relation) { + if (in_array($relation->getType(), array(\RelationMap::MANY_TO_ONE, \RelationMap::ONE_TO_MANY))) { + if ($property == $relation->getForeignTable()->getName()) { + return new TypeGuess('model', array( + 'class' => $relation->getForeignTable()->getClassName(), + 'multiple' => \RelationMap::MANY_TO_ONE === $relation->getType() ? false : true, + ), Guess::HIGH_CONFIDENCE); + } + } elseif ($relation->getType() === \RelationMap::MANY_TO_MANY) { + if (strtolower($property) == strtolower($relation->getPluralName())) { + return new TypeGuess('model', array( + 'class' => $relation->getLocalTable()->getClassName(), + 'multiple' => true, + ), Guess::HIGH_CONFIDENCE); + } + } + } + + if (!$column = $this->getColumn($class, $property)) { + return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); + } + + switch ($column->getType()) { + case \PropelColumnTypes::BOOLEAN: + case \PropelColumnTypes::BOOLEAN_EMU: + return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE); + case \PropelColumnTypes::TIMESTAMP: + case \PropelColumnTypes::BU_TIMESTAMP: + return new TypeGuess('datetime', array(), Guess::HIGH_CONFIDENCE); + case \PropelColumnTypes::DATE: + case \PropelColumnTypes::BU_DATE: + return new TypeGuess('date', array(), Guess::HIGH_CONFIDENCE); + case \PropelColumnTypes::TIME: + return new TypeGuess('time', array(), Guess::HIGH_CONFIDENCE); + case \PropelColumnTypes::FLOAT: + case \PropelColumnTypes::REAL: + case \PropelColumnTypes::DOUBLE: + case \PropelColumnTypes::DECIMAL: + return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE); + case \PropelColumnTypes::TINYINT: + case \PropelColumnTypes::SMALLINT: + case \PropelColumnTypes::INTEGER: + case \PropelColumnTypes::BIGINT: + case \PropelColumnTypes::NUMERIC: + return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE); + case \PropelColumnTypes::ENUM: + case \PropelColumnTypes::CHAR: + if ($column->getValueSet()) { + //check if this is mysql enum + $choices = $column->getValueSet(); + $labels = array_map('ucfirst', $choices); + + return new TypeGuess('choice', array('choices' => array_combine($choices, $labels)), Guess::MEDIUM_CONFIDENCE); + } + case \PropelColumnTypes::VARCHAR: + return new TypeGuess('text', array(), Guess::MEDIUM_CONFIDENCE); + case \PropelColumnTypes::LONGVARCHAR: + case \PropelColumnTypes::BLOB: + case \PropelColumnTypes::CLOB: + case \PropelColumnTypes::CLOB_EMU: + return new TypeGuess('textarea', array(), Guess::MEDIUM_CONFIDENCE); + default: + return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); + } + } + + /** + * {@inheritDoc} + */ + public function guessRequired($class, $property) + { + if ($column = $this->getColumn($class, $property)) { + return new ValueGuess($column->isNotNull(), Guess::HIGH_CONFIDENCE); + } + } + + /** + * {@inheritDoc} + */ + public function guessMaxLength($class, $property) + { + if ($column = $this->getColumn($class, $property)) { + if ($column->isText()) { + return new ValueGuess($column->getSize(), Guess::HIGH_CONFIDENCE); + } + switch ($column->getType()) { + case \PropelColumnTypes::FLOAT: + case \PropelColumnTypes::REAL: + case \PropelColumnTypes::DOUBLE: + case \PropelColumnTypes::DECIMAL: + return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); + } + } + } + + /** + * {@inheritDoc} + */ + public function guessPattern($class, $property) + { + if ($column = $this->getColumn($class, $property)) { + switch ($column->getType()) { + case \PropelColumnTypes::FLOAT: + case \PropelColumnTypes::REAL: + case \PropelColumnTypes::DOUBLE: + case \PropelColumnTypes::DECIMAL: + return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); + } + } + } + + protected function getTable($class) + { + if (isset($this->cache[$class])) { + return $this->cache[$class]; + } + + if (class_exists($queryClass = $class.'Query')) { + $query = new $queryClass(); + + return $this->cache[$class] = $query->getTableMap(); + } + } + + protected function getColumn($class, $property) + { + if (isset($this->cache[$class.'::'.$property])) { + return $this->cache[$class.'::'.$property]; + } + + $table = $this->getTable($class); + + if ($table && $table->hasColumn($property)) { + return $this->cache[$class.'::'.$property] = $table->getColumn($property); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/Type/ModelType.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/Type/ModelType.php new file mode 100644 index 0000000..16d633c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/Type/ModelType.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\Propel1\Form\Type; + +use Symfony\Bridge\Propel1\Form\ChoiceList\ModelChoiceList; +use Symfony\Bridge\Propel1\Form\DataTransformer\CollectionToArrayTransformer; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * ModelType class. + * + * @author William Durand + * @author Toni Uebernickel + * + * Example using the preferred_choices option. + * + * + * public function buildForm(FormBuilderInterface $builder, array $options) + * { + * $builder + * ->add('product', 'model', array( + * 'class' => 'Model\Product', + * 'query' => ProductQuery::create() + * ->filterIsActive(true) + * ->useI18nQuery($options['locale']) + * ->orderByName() + * ->endUse() + * , + * 'preferred_choices' => ProductQuery::create() + * ->filterByIsTopProduct(true) + * , + * )) + * ; + * } + * + */ +class ModelType extends AbstractType +{ + /** + * @var PropertyAccessorInterface + */ + private $propertyAccessor; + + public function __construct(PropertyAccessorInterface $propertyAccessor = null) + { + $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor(); + } + + public function buildForm(FormBuilderInterface $builder, array $options) + { + if ($options['multiple']) { + $builder->addViewTransformer(new CollectionToArrayTransformer(), true); + } + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $propertyAccessor = $this->propertyAccessor; + + $choiceList = function (Options $options) use ($propertyAccessor) { + return new ModelChoiceList( + $options['class'], + $options['property'], + $options['choices'], + $options['query'], + $options['group_by'], + $options['preferred_choices'], + $propertyAccessor + ); + }; + + $resolver->setDefaults(array( + 'template' => 'choice', + 'multiple' => false, + 'expanded' => false, + 'class' => null, + 'property' => null, + 'query' => null, + 'choices' => null, + 'choice_list' => $choiceList, + 'group_by' => null, + 'by_reference' => false, + )); + } + + public function getParent() + { + return 'choice'; + } + + public function getName() + { + return 'model'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/Type/TranslationCollectionType.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/Type/TranslationCollectionType.php new file mode 100644 index 0000000..4ecf7e0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/Type/TranslationCollectionType.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\Bridge\Propel1\Form\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\Exception\MissingOptionsException; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Bridge\Propel1\Form\EventListener\TranslationCollectionFormListener; + +/** + * form type for i18n-columns in propel + * + * @author Patrick Kaufmann + */ +class TranslationCollectionType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + if (!isset($options['options']['data_class']) || null === $options['options']['data_class']) { + throw new MissingOptionsException('data_class must be set'); + } + if (!isset($options['options']['columns']) || null === $options['options']['columns']) { + throw new MissingOptionsException('columns must be set'); + } + + $listener = new TranslationCollectionFormListener($options['languages'], $options['options']['data_class']); + $builder->addEventSubscriber($listener); + } + + public function getParent() + { + return 'collection'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'propel1_translation_collection'; + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setRequired(array( + 'languages' + )); + + $resolver->setDefaults(array( + 'type' => 'propel1_translation', + 'allow_add' => false, + 'allow_delete' => false, + 'options' => array( + 'data_class' => null, + 'columns' => null + ) + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/Type/TranslationType.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/Type/TranslationType.php new file mode 100644 index 0000000..1bd94a4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Form/Type/TranslationType.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\Propel1\Form\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Bridge\Propel1\Form\EventListener\TranslationFormListener; + +/** + * Translation type class + * + * @author Patrick Kaufmann + */ +class TranslationType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addEventSubscriber( + new TranslationFormListener($options['columns'], $options['data_class']) + ); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'propel1_translation'; + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setRequired(array( + 'data_class', + 'columns' + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/LICENSE b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Bridge/Propel1/Logger/PropelLogger.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Logger/PropelLogger.php new file mode 100644 index 0000000..91c0061 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Logger/PropelLogger.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\Bridge\Propel1\Logger; + +use Symfony\Component\Stopwatch\Stopwatch; +use Psr\Log\LoggerInterface; + +/** + * PropelLogger. + * + * @author Fabien Potencier + * @author William Durand + */ +class PropelLogger +{ + /** + * @var LoggerInterface + */ + protected $logger; + + /** + * @var array + */ + protected $queries; + + /** + * @var Stopwatch + */ + protected $stopwatch; + + private $isPrepared; + + /** + * 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->queries = array(); + $this->stopwatch = $stopwatch; + $this->isPrepared = false; + } + + /** + * A convenience function for logging an alert event. + * + * @param mixed $message the message to log. + */ + public function alert($message) + { + if (null !== $this->logger) { + $this->logger->alert($message); + } + } + + /** + * A convenience function for logging a critical event. + * + * @param mixed $message the message to log. + */ + public function crit($message) + { + if (null !== $this->logger) { + $this->logger->critical($message); + } + } + + /** + * A convenience function for logging an error event. + * + * @param mixed $message the message to log. + */ + public function err($message) + { + if (null !== $this->logger) { + $this->logger->error($message); + } + } + + /** + * A convenience function for logging a warning event. + * + * @param mixed $message the message to log. + */ + public function warning($message) + { + if (null !== $this->logger) { + $this->logger->warning($message); + } + } + + /** + * A convenience function for logging an critical event. + * + * @param mixed $message the message to log. + */ + public function notice($message) + { + if (null !== $this->logger) { + $this->logger->notice($message); + } + } + + /** + * A convenience function for logging an critical event. + * + * @param mixed $message the message to log. + */ + public function info($message) + { + if (null !== $this->logger) { + $this->logger->info($message); + } + } + + /** + * A convenience function for logging a debug event. + * + * @param mixed $message the message to log. + */ + public function debug($message) + { + $add = true; + + if (null !== $this->stopwatch) { + $trace = debug_backtrace(); + $method = $trace[2]['args'][2]; + + $watch = 'Propel Query '.(count($this->queries)+1); + if ('PropelPDO::prepare' === $method) { + $this->isPrepared = true; + $this->stopwatch->start($watch, 'propel'); + + $add = false; + } elseif ($this->isPrepared) { + $this->isPrepared = false; + $this->stopwatch->stop($watch); + } + } + + if ($add) { + $this->queries[] = $message; + if (null !== $this->logger) { + $this->logger->debug($message); + } + } + } + + /** + * Returns queries. + * + * @return array Queries + */ + public function getQueries() + { + return $this->queries; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/README.md b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/README.md new file mode 100644 index 0000000..abf786e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/README.md @@ -0,0 +1,13 @@ +Propel Bridge +============= + +Provides integration for Propel with various Symfony2 components. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Bridge/Propel1/ + $ composer.phar install --dev + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Security/User/PropelUserProvider.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Security/User/PropelUserProvider.php new file mode 100644 index 0000000..b92c951 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Security/User/PropelUserProvider.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\Bridge\Propel1\Security\User; + +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; + +/** + * Provides easy to use provisioning for Propel model users. + * + * @author William DURAND + */ +class PropelUserProvider implements UserProviderInterface +{ + /** + * A Model class name. + * + * @var string + */ + protected $class; + + /** + * A Query class name. + * + * @var string + */ + protected $queryClass; + + /** + * A property to use to retrieve the user. + * + * @var string + */ + protected $property; + + /** + * Default constructor + * + * @param $class The User model class. + * @param $property The property to use to retrieve a user. + */ + public function __construct($class, $property = null) + { + $this->class = $class; + $this->queryClass = $class.'Query'; + $this->property = $property; + } + + /** + * {@inheritdoc} + */ + public function loadUserByUsername($username) + { + $queryClass = $this->queryClass; + $query = $queryClass::create(); + + if (null !== $this->property) { + $filter = 'filterBy'.ucfirst($this->property); + $query->$filter($username); + } else { + $query->filterByUsername($username); + } + + if (null === $user = $query->findOne()) { + throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username)); + } + + return $user; + } + + /** + * {@inheritdoc} + */ + public function refreshUser(UserInterface $user) + { + if (!$user instanceof $this->class) { + throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user))); + } + + $queryClass = $this->queryClass; + + return $queryClass::create()->findPk($user->getPrimaryKey()); + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + return $class === $this->class; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/DataCollector/PropelDataCollectorTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/DataCollector/PropelDataCollectorTest.php new file mode 100644 index 0000000..23d6fc9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/DataCollector/PropelDataCollectorTest.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\Propel1\Tests\DataCollector; + +use Symfony\Bridge\Propel1\DataCollector\PropelDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Bridge\Propel1\Tests\Propel1TestCase; + +class PropelDataCollectorTest extends Propel1TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testCollectWithoutData() + { + $c = $this->createCollector(array()); + $c->collect(new Request(), new Response()); + + $this->assertEquals(array(), $c->getQueries()); + $this->assertEquals(0, $c->getQueryCount()); + } + + public function testCollectWithData() + { + $queries = array( + "time: 0.000 sec | mem: 1.4 MB | connection: default | SET NAMES 'utf8'", + ); + + $c = $this->createCollector($queries); + $c->collect(new Request(), new Response()); + + $this->assertEquals(array( + array( + 'sql' => "SET NAMES 'utf8'", + 'time' => '0.000 sec', + 'connection'=> 'default', + 'memory' => '1.4 MB' + ) + ), $c->getQueries()); + $this->assertEquals(1, $c->getQueryCount()); + } + + public function testCollectWithMultipleData() + { + $queries = array( + "time: 0.000 sec | mem: 1.4 MB | connection: default | SET NAMES 'utf8'", + "time: 0.012 sec | mem: 2.4 MB | connection: default | SELECT tags.NAME, image.FILENAME FROM tags LEFT JOIN image ON tags.IMAGEID = image.ID WHERE image.ID = 12", + "time: 0.012 sec | mem: 2.4 MB | connection: default | INSERT INTO `table` (`some_array`) VALUES ('| 1 | 2 | 3 |')", + ); + + $c = $this->createCollector($queries); + $c->collect(new Request(), new Response()); + + $this->assertEquals(array( + array( + 'sql' => "SET NAMES 'utf8'", + 'time' => '0.000 sec', + 'connection'=> 'default', + 'memory' => '1.4 MB' + ), + array( + 'sql' => "SELECT tags.NAME, image.FILENAME FROM tags LEFT JOIN image ON tags.IMAGEID = image.ID WHERE image.ID = 12", + 'time' => '0.012 sec', + 'connection'=> 'default', + 'memory' => '2.4 MB' + ), + array( + 'sql' => "INSERT INTO `table` (`some_array`) VALUES ('| 1 | 2 | 3 |')", + 'time' => '0.012 sec', + 'connection'=> 'default', + 'memory' => '2.4 MB' + ), + ), $c->getQueries()); + $this->assertEquals(3, $c->getQueryCount()); + $this->assertEquals(0.024, $c->getTime()); + } + + private function createCollector($queries) + { + $config = $this->getMock('\PropelConfiguration'); + $config + ->expects($this->any()) + ->method('getParameter') + ->will($this->returnArgument(1)) + ; + + $logger = $this->getMock('\Symfony\Bridge\Propel1\Logger\PropelLogger'); + $logger + ->expects($this->any()) + ->method('getQueries') + ->will($this->returnValue($queries)) + ; + + return new PropelDataCollector($logger, $config); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/Column.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/Column.php new file mode 100644 index 0000000..6b03977 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/Column.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\Propel1\Tests\Fixtures; + +class Column +{ + private $name; + + private $type; + + public function __construct($name, $type) + { + $this->name = $name; + $this->type = $type; + } + + public function getType() + { + return $this->type; + } + + public function isText() + { + if (!$this->type) { + return false; + } + + switch ($this->type) { + case \PropelColumnTypes::CHAR: + case \PropelColumnTypes::VARCHAR: + case \PropelColumnTypes::LONGVARCHAR: + case \PropelColumnTypes::BLOB: + case \PropelColumnTypes::CLOB: + case \PropelColumnTypes::CLOB_EMU: + return true; + } + + return false; + } + + public function getSize() + { + return $this->isText() ? 255 : 0; + } + + public function isNotNull() + { + return ('id' === $this->name); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/Item.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/Item.php new file mode 100644 index 0000000..70705b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/Item.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\Propel1\Tests\Fixtures; + +use \PropelPDO; + +class Item implements \Persistent +{ + private $id; + + private $value; + + private $groupName; + + private $price; + + public function __construct($id = null, $value = null, $groupName = null, $price = null) + { + $this->id = $id; + $this->value = $value; + $this->groupName = $groupName; + $this->price = $price; + } + + public function getId() + { + return $this->id; + } + + public function setId($id) + { + $this->id = $id; + } + + public function getValue() + { + return $this->value; + } + + public function getGroupName() + { + return $this->groupName; + } + + public function getPrice() + { + return $this->price; + } + + public function getPrimaryKey() + { + return $this->getId(); + } + + public function setPrimaryKey($primaryKey) + { + $this->setId($primaryKey); + } + + public function isModified() + { + return false; + } + + public function isColumnModified($col) + { + return false; + } + + public function isNew() + { + return false; + } + + public function setNew($b) + { + } + + public function resetModified() + { + } + + public function isDeleted() + { + return false; + } + + public function setDeleted($b) + { + } + + public function delete(PropelPDO $con = null) + { + } + + public function save(PropelPDO $con = null) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/ItemQuery.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/ItemQuery.php new file mode 100644 index 0000000..fe2d03e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/ItemQuery.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\Bridge\Propel1\Tests\Fixtures; + +class ItemQuery +{ + private $map = array( + 'id' => \PropelColumnTypes::INTEGER, + 'value' => \PropelColumnTypes::VARCHAR, + 'price' => \PropelColumnTypes::FLOAT, + 'is_active' => \PropelColumnTypes::BOOLEAN, + 'enabled' => \PropelColumnTypes::BOOLEAN_EMU, + 'updated_at' => \PropelColumnTypes::TIMESTAMP, + ); + + public function getTableMap() + { + // Allows to define methods in this class + // to avoid a lot of mock classes + return $this; + } + + public function getPrimaryKeys() + { + $cm = new \ColumnMap('id', new \TableMap()); + $cm->setType('INTEGER'); + + return array('id' => $cm); + } + + /** + * Method from the TableMap API + */ + public function hasColumn($column) + { + return in_array($column, array_keys($this->map)); + } + + /** + * Method from the TableMap API + */ + public function getColumn($column) + { + if ($this->hasColumn($column)) { + return new Column($column, $this->map[$column]); + } + + return null; + } + + /** + * Method from the TableMap API + */ + public function getRelations() + { + return array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/ReadOnlyItem.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/ReadOnlyItem.php new file mode 100644 index 0000000..fbedf25 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/ReadOnlyItem.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\Bridge\Propel1\Tests\Fixtures; + +class ReadOnlyItem extends \BaseObject +{ + public function getName() + { + return 'Marvin'; + } + + public function getPrimaryKey() + { + return 42; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/ReadOnlyItemQuery.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/ReadOnlyItemQuery.php new file mode 100644 index 0000000..0e77c26 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/ReadOnlyItemQuery.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\Propel1\Tests\Fixtures; + +class ReadOnlyItemQuery +{ + public function getTableMap() + { + // Allows to define methods in this class + // to avoid a lot of mock classes + return $this; + } + + public function getPrimaryKeys() + { + $cm = new \ColumnMap('id', new \TableMap()); + $cm->setType('INTEGER'); + + return array('id' => $cm); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItem.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItem.php new file mode 100644 index 0000000..c69fe45 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItem.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\Bridge\Propel1\Tests\Fixtures; + +use PropelPDO; + +class TranslatableItem implements \Persistent +{ + private $id; + + private $currentTranslations; + + private $groupName; + + private $price; + + public function __construct($id = null, $translations = array()) + { + $this->id = $id; + $this->currentTranslations = $translations; + } + + public function getId() + { + return $this->id; + } + + public function setId($id) + { + $this->id = $id; + } + + public function getGroupName() + { + return $this->groupName; + } + + public function getPrice() + { + return $this->price; + } + + public function getPrimaryKey() + { + return $this->getId(); + } + + public function setPrimaryKey($primaryKey) + { + $this->setId($primaryKey); + } + + public function isModified() + { + return false; + } + + public function isColumnModified($col) + { + return false; + } + + public function isNew() + { + return false; + } + + public function setNew($b) + { + } + + public function resetModified() + { + } + + public function isDeleted() + { + return false; + } + + public function setDeleted($b) + { + } + + public function delete(PropelPDO $con = null) + { + } + + public function save(PropelPDO $con = null) + { + } + + public function getTranslation($locale = 'de', PropelPDO $con = null) + { + if (!isset($this->currentTranslations[$locale])) { + $translation = new TranslatableItemI18n(); + $translation->setLocale($locale); + $this->currentTranslations[$locale] = $translation; + } + + return $this->currentTranslations[$locale]; + } + + public function addTranslatableItemI18n(TranslatableItemI18n $i) + { + if (!in_array($i, $this->currentTranslations)) { + $this->currentTranslations[$i->getLocale()] = $i; + $i->setItem($this); + } + } + + public function removeTranslatableItemI18n(TranslatableItemI18n $i) + { + unset($this->currentTranslations[$i->getLocale()]); + } + + public function getTranslatableItemI18ns() + { + return $this->currentTranslations; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItemI18n.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItemI18n.php new file mode 100644 index 0000000..1253b26 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItemI18n.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\Propel1\Tests\Fixtures; + +use PropelPDO; + +class TranslatableItemI18n implements \Persistent +{ + private $id; + + private $locale; + + private $value; + + private $value2; + + private $item; + + public function __construct($id = null, $locale = null, $value = null) + { + $this->id = $id; + $this->locale = $locale; + $this->value = $value; + } + + public function getId() + { + return $this->id; + } + + public function setId($id) + { + $this->id = $id; + } + + public function getPrimaryKey() + { + return $this->getId(); + } + + public function setPrimaryKey($primaryKey) + { + $this->setId($primaryKey); + } + + public function isModified() + { + return false; + } + + public function isColumnModified($col) + { + return false; + } + + public function isNew() + { + return false; + } + + public function setNew($b) + { + } + + public function resetModified() + { + } + + public function isDeleted() + { + return false; + } + + public function setDeleted($b) + { + } + + public function delete(PropelPDO $con = null) + { + } + + public function save(PropelPDO $con = null) + { + } + + public function setLocale($locale) + { + + $this->locale = $locale; + } + + public function getLocale() + { + return $this->locale; + } + + public function getItem() + { + return $this->item; + } + + public function setItem($item) + { + $this->item = $item; + } + + public function setValue($value) + { + + $this->value = $value; + } + + public function getValue() + { + return $this->value; + } + + public function setValue2($value2) + { + + $this->value2 = $value2; + } + + public function getValue2() + { + return $this->value2; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/ChoiceList/ModelChoiceListTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/ChoiceList/ModelChoiceListTest.php new file mode 100644 index 0000000..59f1367 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/ChoiceList/ModelChoiceListTest.php @@ -0,0 +1,202 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Propel1\Tests\Form\ChoiceList; + +use Symfony\Bridge\Propel1\Form\ChoiceList\ModelChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Bridge\Propel1\Tests\Fixtures\Item; +use Symfony\Bridge\Propel1\Tests\Fixtures\ReadOnlyItem; +use Symfony\Bridge\Propel1\Tests\Propel1TestCase; + +class ModelChoiceListTest extends Propel1TestCase +{ + const ITEM_CLASS = '\Symfony\Bridge\Propel1\Tests\Fixtures\Item'; + + protected function setUp() + { + if (!class_exists('Symfony\Component\Form\Form')) { + $this->markTestSkipped('The "Form" component is not available'); + } + + if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccessor')) { + $this->markTestSkipped('The "PropertyAccessor" component is not available'); + } + } + + public function testEmptyChoicesReturnsEmpty() + { + $choiceList = new ModelChoiceList( + self::ITEM_CLASS, + 'value', + array() + ); + + $this->assertSame(array(), $choiceList->getChoices()); + } + + public function testReadOnlyIsValidChoice() + { + $item = new ReadOnlyItem(); + $choiceList = new ModelChoiceList( + '\Symfony\Bridge\Propel1\Tests\Fixtures\ReadOnlyItem', + 'name', + array( + $item, + ) + ); + + $this->assertSame(array(42 => $item), $choiceList->getChoices()); + } + + public function testFlattenedChoices() + { + $item1 = new Item(1, 'Foo'); + $item2 = new Item(2, 'Bar'); + + $choiceList = new ModelChoiceList( + self::ITEM_CLASS, + 'value', + array( + $item1, + $item2, + ) + ); + + $this->assertSame(array(1 => $item1, 2 => $item2), $choiceList->getChoices()); + } + + public function testFlattenedPreferredChoices() + { + $item1 = new Item(1, 'Foo'); + $item2 = new Item(2, 'Bar'); + + $choiceList = new ModelChoiceList( + self::ITEM_CLASS, + 'value', + array( + $item1, + $item2, + ), + null, + null, + array( + $item1 + ) + ); + + $this->assertSame(array(1 => $item1, 2 => $item2), $choiceList->getChoices()); + $this->assertEquals(array(1 => new ChoiceView($item1, '1', 'Foo')), $choiceList->getPreferredViews()); + } + + public function testNestedChoices() + { + $item1 = new Item(1, 'Foo'); + $item2 = new Item(2, 'Bar'); + + $choiceList = new ModelChoiceList( + self::ITEM_CLASS, + 'value', + array( + 'group1' => array($item1), + 'group2' => array($item2), + ) + ); + + $this->assertSame(array(1 => $item1, 2 => $item2), $choiceList->getChoices()); + $this->assertEquals(array( + 'group1' => array(1 => new ChoiceView($item1, '1', 'Foo')), + 'group2' => array(2 => new ChoiceView($item2, '2', 'Bar')) + ), $choiceList->getRemainingViews()); + } + + public function testGroupBySupportsString() + { + $item1 = new Item(1, 'Foo', 'Group1'); + $item2 = new Item(2, 'Bar', 'Group1'); + $item3 = new Item(3, 'Baz', 'Group2'); + $item4 = new Item(4, 'Boo!', null); + + $choiceList = new ModelChoiceList( + self::ITEM_CLASS, + 'value', + array( + $item1, + $item2, + $item3, + $item4, + ), + null, + 'groupName' + ); + + $this->assertEquals(array(1 => $item1, 2 => $item2, 3 => $item3, 4 => $item4), $choiceList->getChoices()); + $this->assertEquals(array( + 'Group1' => array(1 => new ChoiceView($item1, '1', 'Foo'), 2 => new ChoiceView($item2, '2', 'Bar')), + 'Group2' => array(3 => new ChoiceView($item3, '3', 'Baz')), + 4 => new ChoiceView($item4, '4', 'Boo!') + ), $choiceList->getRemainingViews()); + } + + public function testGroupByInvalidPropertyPathReturnsFlatChoices() + { + $item1 = new Item(1, 'Foo', 'Group1'); + $item2 = new Item(2, 'Bar', 'Group1'); + + $choiceList = new ModelChoiceList( + self::ITEM_CLASS, + 'value', + array( + $item1, + $item2, + ), + null, + 'child.that.does.not.exist' + ); + + $this->assertEquals(array( + 1 => $item1, + 2 => $item2 + ), $choiceList->getChoices()); + } + + public function testGetValuesForChoices() + { + $item1 = new Item(1, 'Foo'); + $item2 = new Item(2, 'Bar'); + + $choiceList = new ModelChoiceList( + self::ITEM_CLASS, + 'value', + null, + null, + null, + null + ); + + $this->assertEquals(array(1, 2), $choiceList->getValuesForChoices(array($item1, $item2))); + $this->assertEquals(array(1, 2), $choiceList->getIndicesForChoices(array($item1, $item2))); + } + + public function testDifferentEqualObjectsAreChoosen() + { + $item = new Item(1, 'Foo'); + $choiceList = new ModelChoiceList( + self::ITEM_CLASS, + 'value', + array($item) + ); + + $choosenItem = new Item(1, 'Foo'); + + $this->assertEquals(array(1), $choiceList->getIndicesForChoices(array($choosenItem))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php new file mode 100644 index 0000000..13a9f7f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.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\Propel1\Tests\Form\DataTransformer; + +use \PropelObjectCollection; +use Symfony\Bridge\Propel1\Form\DataTransformer\CollectionToArrayTransformer; +use Symfony\Bridge\Propel1\Tests\Propel1TestCase; + +class CollectionToArrayTransformerTest extends Propel1TestCase +{ + private $transformer; + + protected function setUp() + { + if (!class_exists('Symfony\Component\Form\Form')) { + $this->markTestSkipped('The "Form" component is not available'); + } + + parent::setUp(); + + $this->transformer = new CollectionToArrayTransformer(); + } + + public function testTransform() + { + $result = $this->transformer->transform(new PropelObjectCollection()); + + $this->assertTrue(is_array($result)); + $this->assertEquals(0, count($result)); + } + + public function testTransformWithNull() + { + $result = $this->transformer->transform(null); + + $this->assertTrue(is_array($result)); + $this->assertEquals(0, count($result)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformThrowsExceptionIfNotPropelObjectCollection() + { + $this->transformer->transform(new DummyObject()); + } + + public function testTransformWithData() + { + $coll = new PropelObjectCollection(); + $coll->setData(array('foo', 'bar')); + + $result = $this->transformer->transform($coll); + + $this->assertTrue(is_array($result)); + $this->assertEquals(2, count($result)); + $this->assertEquals('foo', $result[0]); + $this->assertEquals('bar', $result[1]); + } + + public function testReverseTransformWithNull() + { + $result = $this->transformer->reverseTransform(null); + + $this->assertInstanceOf('\PropelObjectCollection', $result); + $this->assertEquals(0, count($result->getData())); + } + + public function testReverseTransformWithEmptyString() + { + $result = $this->transformer->reverseTransform(''); + + $this->assertInstanceOf('\PropelObjectCollection', $result); + $this->assertEquals(0, count($result->getData())); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformThrowsExceptionIfNotArray() + { + $this->transformer->reverseTransform(new DummyObject()); + } + + public function testReverseTransformWithData() + { + $inputData = array('foo', 'bar'); + + $result = $this->transformer->reverseTransform($inputData); + $data = $result->getData(); + + $this->assertInstanceOf('\PropelObjectCollection', $result); + + $this->assertTrue(is_array($data)); + $this->assertEquals(2, count($data)); + $this->assertEquals('foo', $data[0]); + $this->assertEquals('bar', $data[1]); + $this->assertsame($inputData, $data); + } +} + +class DummyObject {} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/PropelTypeGuesserTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/PropelTypeGuesserTest.php new file mode 100644 index 0000000..a9ed9a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/PropelTypeGuesserTest.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\Bridge\Propel1\Tests\Form; + +use Symfony\Bridge\Propel1\Form\PropelTypeGuesser; +use Symfony\Bridge\Propel1\Tests\Propel1TestCase; +use Symfony\Component\Form\Guess\Guess; + +class PropelTypeGuesserTest extends Propel1TestCase +{ + const CLASS_NAME = 'Symfony\Bridge\Propel1\Tests\Fixtures\Item'; + + const UNKNOWN_CLASS_NAME = 'Symfony\Bridge\Propel1\Tests\Fixtures\UnknownItem'; + + private $guesser; + + public function setUp() + { + $this->guesser = new PropelTypeGuesser(); + } + + public function testGuessMaxLengthWithText() + { + $value = $this->guesser->guessMaxLength(self::CLASS_NAME, 'value'); + + $this->assertNotNull($value); + $this->assertEquals(255, $value->getValue()); + } + + public function testGuessMaxLengthWithFloat() + { + $value = $this->guesser->guessMaxLength(self::CLASS_NAME, 'price'); + + $this->assertNotNull($value); + $this->assertNull($value->getValue()); + } + + public function testGuessMinLengthWithText() + { + $value = $this->guesser->guessPattern(self::CLASS_NAME, 'value'); + + $this->assertNull($value); + } + + public function testGuessMinLengthWithFloat() + { + $value = $this->guesser->guessPattern(self::CLASS_NAME, 'price'); + + $this->assertNotNull($value); + $this->assertNull($value->getValue()); + } + + public function testGuessRequired() + { + $value = $this->guesser->guessRequired(self::CLASS_NAME, 'id'); + + $this->assertNotNull($value); + $this->assertTrue($value->getValue()); + } + + public function testGuessRequiredWithNullableColumn() + { + $value = $this->guesser->guessRequired(self::CLASS_NAME, 'value'); + + $this->assertNotNull($value); + $this->assertFalse($value->getValue()); + } + + public function testGuessTypeWithoutTable() + { + $value = $this->guesser->guessType(self::UNKNOWN_CLASS_NAME, 'property'); + + $this->assertNotNull($value); + $this->assertEquals('text', $value->getType()); + $this->assertEquals(Guess::LOW_CONFIDENCE, $value->getConfidence()); + } + + public function testGuessTypeWithoutColumn() + { + $value = $this->guesser->guessType(self::CLASS_NAME, 'property'); + + $this->assertNotNull($value); + $this->assertEquals('text', $value->getType()); + $this->assertEquals(Guess::LOW_CONFIDENCE, $value->getConfidence()); + } + + /** + * @dataProvider dataProviderForGuessType + */ + public function testGuessType($property, $type, $confidence) + { + $value = $this->guesser->guessType(self::CLASS_NAME, $property); + + $this->assertNotNull($value); + $this->assertEquals($type, $value->getType()); + $this->assertEquals($confidence, $value->getConfidence()); + } + + public static function dataProviderForGuessType() + { + return array( + array('is_active', 'checkbox', Guess::HIGH_CONFIDENCE), + array('enabled', 'checkbox', Guess::HIGH_CONFIDENCE), + array('id', 'integer', Guess::MEDIUM_CONFIDENCE), + array('value', 'text', Guess::MEDIUM_CONFIDENCE), + array('price', 'number', Guess::MEDIUM_CONFIDENCE), + array('updated_at', 'datetime', Guess::HIGH_CONFIDENCE), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/Type/TranslationCollectionTypeTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/Type/TranslationCollectionTypeTest.php new file mode 100644 index 0000000..e9fa839 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Form/Type/TranslationCollectionTypeTest.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\Bridge\Propel1\Tests\Form\Type; + +use Symfony\Bridge\Propel1\Tests\Fixtures\Item; +use Symfony\Bridge\Propel1\Form\PropelExtension; +use Symfony\Bridge\Propel1\Tests\Fixtures\TranslatableItemI18n; +use Symfony\Bridge\Propel1\Tests\Fixtures\TranslatableItem; +use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase; + +class TranslationCollectionTypeTest extends TypeTestCase +{ + const TRANSLATION_CLASS = 'Symfony\Bridge\Propel1\Tests\Fixtures\TranslatableItem'; + const TRANSLATABLE_I18N_CLASS = 'Symfony\Bridge\Propel1\Tests\Fixtures\TranslatableItemI18n'; + const NON_TRANSLATION_CLASS = 'Symfony\Bridge\Propel1\Tests\Fixtures\Item'; + + protected function setUp() + { + parent::setUp(); + } + + protected function getExtensions() + { + return array_merge(parent::getExtensions(), array( + new PropelExtension(), + )); + } + + public function testTranslationsAdded() + { + $item = new TranslatableItem(); + $item->addTranslatableItemI18n(new TranslatableItemI18n(1, 'fr', 'val1')); + $item->addTranslatableItemI18n(new TranslatableItemI18n(2, 'en', 'val2')); + + $builder = $this->factory->createBuilder('form', null, array( + 'data_class' => self::TRANSLATION_CLASS + )); + + $builder->add('translatableItemI18ns', 'propel1_translation_collection', array( + 'languages' => array('en', 'fr'), + 'options' => array( + 'data_class' => self::TRANSLATABLE_I18N_CLASS, + 'columns' => array('value', 'value2' => array('label' => 'Label', 'type' => 'textarea')) + ) + )); + $form = $builder->getForm(); + $form->setData($item); + $translations = $form->get('translatableItemI18ns'); + + $this->assertCount(2, $translations); + $this->assertInstanceOf('Symfony\Component\Form\Form', $translations['en']); + $this->assertInstanceOf('Symfony\Component\Form\Form', $translations['fr']); + + $this->assertInstanceOf(self::TRANSLATABLE_I18N_CLASS, $translations['en']->getData()); + $this->assertInstanceOf(self::TRANSLATABLE_I18N_CLASS, $translations['fr']->getData()); + + $this->assertEquals($item->getTranslation('en'), $translations['en']->getData()); + $this->assertEquals($item->getTranslation('fr'), $translations['fr']->getData()); + + $columnOptions = $translations['fr']->getConfig()->getOption('columns'); + $this->assertEquals('value', $columnOptions[0]); + $this->assertEquals('textarea', $columnOptions['value2']['type']); + $this->assertEquals('Label', $columnOptions['value2']['label']); + } + + public function testNotPresentTranslationsAdded() + { + $item = new TranslatableItem(); + + $this->assertCount(0, $item->getTranslatableItemI18ns()); + + $builder = $this->factory->createBuilder('form', null, array( + 'data_class' => self::TRANSLATION_CLASS + )); + $builder->add('translatableItemI18ns', 'propel1_translation_collection', array( + 'languages' => array('en', 'fr'), + 'options' => array( + 'data_class' => self::TRANSLATABLE_I18N_CLASS, + 'columns' => array('value', 'value2' => array('label' => 'Label', 'type' => 'textarea')) + ) + )); + + $form = $builder->getForm(); + $form->setData($item); + + $this->assertCount(2, $item->getTranslatableItemI18ns()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testNoArrayGiven() + { + $item = new Item(null, 'val'); + + $builder = $this->factory->createBuilder('form', null, array( + 'data_class' => self::NON_TRANSLATION_CLASS + )); + $builder->add('value', 'propel1_translation_collection', array( + 'languages' => array('en', 'fr'), + 'options' => array( + 'data_class' => self::TRANSLATABLE_I18N_CLASS, + 'columns' => array('value', 'value2' => array('label' => 'Label', 'type' => 'textarea')) + ) + )); + + $form = $builder->getForm(); + $form->setData($item); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException + */ + public function testNoDataClassAdded() + { + $this->factory->createNamed('itemI18ns', 'propel1_translation_collection', null, array( + 'languages' => array('en', 'fr'), + 'options' => array( + 'columns' => array('value', 'value2') + ) + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException + */ + public function testNoLanguagesAdded() + { + $this->factory->createNamed('itemI18ns', 'propel1_translation_collection', null, array( + 'options' => array( + 'data_class' => self::TRANSLATABLE_I18N_CLASS, + 'columns' => array('value', 'value2') + ) + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException + */ + public function testNoColumnsAdded() + { + $this->factory->createNamed('itemI18ns', 'propel1_translation_collection', null, array( + 'languages' => array('en', 'fr'), + 'options' => array( + 'data_class' => self::TRANSLATABLE_I18N_CLASS + ) + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Propel1TestCase.php b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Propel1TestCase.php new file mode 100644 index 0000000..93cc808 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/Tests/Propel1TestCase.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\Bridge\Propel1\Tests; + +abstract class Propel1TestCase extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('\Propel')) { + $this->markTestSkipped('Propel is not available.'); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/composer.json b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/composer.json new file mode 100644 index 0000000..3f64580 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/propel1-bridge", + "type": "symfony-bridge", + "description": "Symfony Propel1 Bridge", + "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.3", + "symfony/http-foundation": "~2.0", + "symfony/http-kernel": "~2.0", + "symfony/form": "~2.2", + "propel/propel1": "1.6.*" + }, + "require-dev": { + "symfony/stopwatch": "~2.2" + }, + "autoload": { + "psr-0": { "Symfony\\Bridge\\Propel1\\": "" } + }, + "target-dir": "Symfony/Bridge/Propel1", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/phpunit.xml.dist new file mode 100644 index 0000000..7a5c82b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Propel1/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + + + + 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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/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..4364019 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.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\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 \ProxyManager\Factory\LazyLoadingValueHolderFactory + */ + private $factory; + + /** + * Constructor + */ + 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) { + $proxy->setProxyInitializer(null); + + $wrappedInstance = call_user_func($realInstantiator); + + 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..05f3ae1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.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\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\ContainerInterface; +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 \ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator + */ + private $proxyGenerator; + + /** + * @var \ProxyManager\GeneratorStrategy\BaseGeneratorStrategy + */ + private $classGenerator; + + /** + * Constructor + */ + public function __construct() + { + $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 (ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) { + $instantiation .= " \$this->services['$id'] ="; + } elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { + $instantiation .= " \$this->services['$id'] = \$this->scopedServices['$scope']['$id'] ="; + } + + $methodName = 'get'.Container::camelize($id).'Service'; + $proxyClass = $this->getProxyClassName($definition); + + return <<setProxyInitializer(null); + + \$wrappedInstance = \$container->$methodName(false); + + 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); + } +} 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..a266a26 --- /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 Symfony2 components. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Bridge/ProxyManager/ + $ composer.phar install --dev + $ 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..ebe8f2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.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\ProxyManager\LazyProxy\Tests; + +require_once __DIR__ . '/Fixtures/includes/foo.php'; + +use ProxyManager\Configuration; +use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Integration tests for {@see \Symfony\Component\DependencyInjection\ContainerBuilder} combined + * with the ProxyManager bridge + * + * @author Marco Pivetta + */ +class ContainerBuilderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + 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..f5025e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.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\ProxyManager\LazyProxy\Tests\Dumper; + +use ProxyManager\Configuration; +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +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..f170a94 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php @@ -0,0 +1,198 @@ +services = + $this->scopedServices = + $this->scopeStacks = array(); + + $this->set('service_container', $this); + + $this->scopes = array(); + $this->scopeChildren = array(); + } + + /** + * Gets the 'foo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @param boolean $lazyLoad whether to try lazy-loading the service with a proxy + * + * @return stdClass A stdClass instance. + */ + public function getFooService($lazyLoad = true) + { + if ($lazyLoad) { + $container = $this; + + return $this->services['foo'] = new stdClass_c1d194250ee2e2b7d2eab8b8212368a8( + function (& $wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) use ($container) { + $proxy->setProxyInitializer(null); + + $wrappedInstance = $container->getFooService(false); + + 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 + */ + 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..e1831fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt @@ -0,0 +1,27 @@ +services['foo'] = new stdClass_%s( + function (&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) use ($container) { + $proxy->setProxyInitializer(null); + + $wrappedInstance = $container->getFooService(false); + + return true; + } + ); + } + + return new \stdClass(); + } +} + +class stdClass_%s extends \stdClass implements \ProxyManager\Proxy\LazyLoadingInterface, \ProxyManager\Proxy\ValueHolderInterface +{%a}%A \ No newline at end of file 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..1fb4c01 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.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\ProxyManager\LazyProxy\Tests\Instantiator; + +use ProxyManager\Configuration; +use ProxyManager\Proxy\LazyLoadingInterface; +use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; + +/** + * Tests for {@see \Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator} + * + * @author Marco Pivetta + * + * @covers \Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator + */ +class RuntimeInstantiatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var RuntimeInstantiator + */ + protected $instantiator; + + /** + * {@inheritDoc} + */ + public 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..c21450a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.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\ProxyManager\LazyProxy\Tests\Instantiator; + +use ProxyManager\Configuration; +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; + +/** + * Tests for {@see \Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper} + * + * @author Marco Pivetta + * + * @covers \Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper + */ +class ProxyDumperTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ProxyDumper + */ + protected $dumper; + + /** + * {@inheritDoc} + */ + public 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 SymfonyBridgeProxyManagerLazyProxyTestsInstantiatorProxyDumperTest%aextends%w' + . '\Symfony\Bridge\ProxyManager\LazyProxy\Tests\Instantiator%a', + $code + ); + } + + public function testGetProxyFactoryCode() + { + $definition = new Definition(__CLASS__); + + $definition->setLazy(true); + + $code = $this->dumper->getProxyFactoryCode($definition, 'foo'); + + $this->assertStringMatchesFormat( + '%wif ($lazyLoad) {%w$container = $this;%wreturn $this->services[\'foo\'] = new ' + . 'SymfonyBridgeProxyManagerLazyProxyTestsInstantiatorProxyDumperTest_%s(%wfunction ' + . '(&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) use ($container) {' + . '%w$proxy->setProxyInitializer(null);%w$wrappedInstance = $container->getFooService(false);' + . '%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..4d3abb6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/composer.json @@ -0,0 +1,35 @@ +{ + "name": "symfony/proxy-manager-bridge", + "type": "symfony-bridge", + "description": "Symfony ProxyManager Bridge", + "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.3", + "symfony/dependency-injection": "~2.3", + "ocramius/proxy-manager": ">=0.3.1,<0.4-dev" + }, + "autoload": { + "psr-0": { + "Symfony\\Bridge\\ProxyManager\\": "" + } + }, + "target-dir": "Symfony/Bridge/ProxyManager", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..5e7c433 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Swiftmailer/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Bridge/Swiftmailer/CHANGELOG.md new file mode 100644 index 0000000..67100f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Swiftmailer/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +2.1.0 +----- + + * added a data collector diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Swiftmailer/DataCollector/MessageDataCollector.php b/vendor/symfony/symfony/src/Symfony/Bridge/Swiftmailer/DataCollector/MessageDataCollector.php new file mode 100644 index 0000000..3ae96a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Swiftmailer/DataCollector/MessageDataCollector.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\Swiftmailer\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 + */ +class MessageDataCollector extends DataCollector +{ + private $container; + private $isSpool; + + /** + * 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 + * @param Boolean $isSpool + */ + public function __construct(ContainerInterface $container, $isSpool) + { + $this->container = $container; + $this->isSpool = $isSpool; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + // only collect when Swiftmailer has already been initialized + if (class_exists('Swift_Mailer', false)) { + $logger = $this->container->get('swiftmailer.plugin.messagelogger'); + $this->data['messages'] = $logger->getMessages(); + $this->data['messageCount'] = $logger->countMessages(); + } else { + $this->data['messages'] = array(); + $this->data['messageCount'] = 0; + } + + $this->data['isSpool'] = $this->isSpool; + } + + public function getMessageCount() + { + return $this->data['messageCount']; + } + + public function getMessages() + { + return $this->data['messages']; + } + + public function isSpool() + { + return $this->data['isSpool']; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'swiftmailer'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Swiftmailer/LICENSE b/vendor/symfony/symfony/src/Symfony/Bridge/Swiftmailer/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Swiftmailer/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Bridge/Swiftmailer/composer.json b/vendor/symfony/symfony/src/Symfony/Bridge/Swiftmailer/composer.json new file mode 100644 index 0000000..9e6c90b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Swiftmailer/composer.json @@ -0,0 +1,35 @@ +{ + "name": "symfony/swiftmailer-bridge", + "type": "symfony-bridge", + "description": "Symfony Swiftmailer Bridge", + "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.3", + "swiftmailer/swiftmailer": ">=4.2.0,<5.1-dev" + }, + "suggest": { + "symfony/http-kernel": "" + }, + "autoload": { + "psr-0": { "Symfony\\Bridge\\Swiftmailer\\": "" } + }, + "target-dir": "Symfony/Bridge/Swiftmailer", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} 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..ad22216 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -0,0 +1,29 @@ +CHANGELOG +========= + +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/Extension/CodeExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/CodeExtension.php new file mode 100644 index 0000000..a6202b2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/CodeExtension.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +if (!defined('ENT_SUBSTITUTE')) { + define('ENT_SUBSTITUTE', 8); +} + +/** + * 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 = empty($fileLinkFormat) ? ini_get('xdebug.file_link_format') : $fileLinkFormat; + $this->rootDir = str_replace('\\', '/', $rootDir).'/'; + $this->charset = $charset; + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return array( + 'abbr_class' => new \Twig_Filter_Method($this, 'abbrClass', array('is_safe' => array('html'))), + 'abbr_method' => new \Twig_Filter_Method($this, 'abbrMethod', array('is_safe' => array('html'))), + 'format_args' => new \Twig_Filter_Method($this, 'formatArgs', array('is_safe' => array('html'))), + 'format_args_as_text' => new \Twig_Filter_Method($this, 'formatArgsAsText'), + 'file_excerpt' => new \Twig_Filter_Method($this, 'fileExcerpt', array('is_safe' => array('html'))), + 'format_file' => new \Twig_Filter_Method($this, 'formatFile', array('is_safe' => array('html'))), + 'format_file_from_text' => new \Twig_Filter_Method($this, 'formatFileFromText', array('is_safe' => array('html'))), + 'file_link' => new \Twig_Filter_Method($this, 'getFileLink', array('is_safe' => array('html'))), + ); + } + + 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)) { + $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 integer $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) + { + if (null === $text) { + $file = trim($file); + $text = $file; + if (0 === strpos($text, $this->rootDir)) { + $text = str_replace($this->rootDir, '', str_replace('\\', '/', $text)); + $text = sprintf('kernel.root_dir/%s', $this->rootDir, $text); + } + } + + $text = "$text at line $line"; + + if (false !== $link = $this->getFileLink($file, $line)) { + return sprintf('%s', htmlspecialchars($link, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset), $text); + } + + return $text; + } + + /** + * Returns the link for a given file/line pair. + * + * @param string $file An absolute file path + * @param integer $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) + { + $that = $this; + + return preg_replace_callback('/in ("|")?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', function ($match) use ($that) { + return 'in '.$that->formatFile($match[2], $match[3]); + }, $text); + } + + 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/FormExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/FormExtension.php new file mode 100644 index 0000000..fbfa524 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/FormExtension.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\Twig\Extension; + +use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; +use Symfony\Bridge\Twig\Form\TwigRendererInterface; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +/** + * FormExtension extends Twig with form capabilities. + * + * @author Fabien Potencier + * @author Bernhard Schussek + */ +class FormExtension extends \Twig_Extension +{ + /** + * 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( + 'form_enctype' => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\FormEnctypeNode', array('is_safe' => array('html'))), + 'form_widget' => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', array('is_safe' => array('html'))), + 'form_errors' => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', array('is_safe' => array('html'))), + 'form_label' => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', array('is_safe' => array('html'))), + 'form_row' => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', array('is_safe' => array('html'))), + 'form_rest' => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', array('is_safe' => array('html'))), + 'form' => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\RenderBlockNode', array('is_safe' => array('html'))), + 'form_start' => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\RenderBlockNode', array('is_safe' => array('html'))), + 'form_end' => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\RenderBlockNode', array('is_safe' => array('html'))), + 'csrf_token' => new \Twig_Function_Method($this, 'renderer->renderCsrfToken'), + ); + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return array( + 'humanize' => new \Twig_Filter_Method($this, 'renderer->humanize'), + ); + } + + /** + * {@inheritdoc} + */ + public function getTests() + { + return array( + 'selectedchoice' => new \Twig_Test_Method($this, 'isSelectedChoice'), + ); + } + + /** + * 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 Boolean Whether the choice is selected. + * + * @see ChoiceView::isSelected() + */ + public function isSelectedChoice(ChoiceView $choice, $selectedValue) + { + if (is_array($selectedValue)) { + return false !== array_search($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/HttpKernelExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php new file mode 100644 index 0000000..a837723 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.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\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( + 'render' => new \Twig_Function_Method($this, 'renderFragment', array('is_safe' => array('html'))), + 'render_*' => new \Twig_Function_Method($this, 'renderFragmentStrategy', array('is_safe' => array('html'))), + 'controller' => new \Twig_Function_Method($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 Symfony\Component\HttpKernel\Fragment\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 Symfony\Component\HttpKernel\Fragment\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); + } + + public function getName() + { + return 'http_kernel'; + } +} 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..8274abf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/RoutingExtension.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\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( + 'url' => new \Twig_Function_Method($this, 'getUrl', array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))), + 'path' => new \Twig_Function_Method($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(); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + 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..c9f4466 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/SecurityExtension.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\Bridge\Twig\Extension; + +use Symfony\Component\Security\Acl\Voter\FieldVote; +use Symfony\Component\Security\Core\SecurityContextInterface; + +/** + * SecurityExtension exposes security context features. + * + * @author Fabien Potencier + */ +class SecurityExtension extends \Twig_Extension +{ + private $context; + + public function __construct(SecurityContextInterface $context = null) + { + $this->context = $context; + } + + public function isGranted($role, $object = null, $field = null) + { + if (null === $this->context) { + return false; + } + + if (null !== $field) { + $object = new FieldVote($object, $field); + } + + return $this->context->isGranted($role, $object); + } + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return array( + 'is_granted' => new \Twig_Function_Method($this, 'isGranted'), + ); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'security'; + } +} 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..2ab3832 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/TranslationExtension.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\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( + 'trans' => new \Twig_Filter_Method($this, 'trans'), + 'transchoice' => new \Twig_Filter_Method($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) + { + if (null === $domain) { + $domain = 'messages'; + } + + return $this->translator->trans($message, $arguments, $domain, $locale); + } + + public function transchoice($message, $count, array $arguments = array(), $domain = null, $locale = null) + { + if (null === $domain) { + $domain = 'messages'; + } + + return $this->translator->transChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + 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..f88829e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/YamlExtension.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\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( + 'yaml_encode' => new \Twig_Filter_Method($this, 'encode'), + 'yaml_dump' => new \Twig_Filter_Method($this, 'dump'), + ); + } + + public function encode($input, $inline = 0, $dumpObjects = false) + { + static $dumper; + + if (null === $dumper) { + $dumper = new YamlDumper(); + } + + return $dumper->dump($input, $inline, 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); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + 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..72798d1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRenderer.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\Twig\Form; + +use Symfony\Component\Form\FormRenderer; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; + +/** + * @author Bernhard Schussek + */ +class TwigRenderer extends FormRenderer implements TwigRendererInterface +{ + /** + * @var TwigRendererEngineInterface + */ + private $engine; + + public function __construct(TwigRendererEngineInterface $engine, CsrfProviderInterface $csrfProvider = null) + { + parent::__construct($engine, $csrfProvider); + + $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..05beb31 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.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\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 Boolean 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; + + // 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(array())); + } +} 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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Bridge/Twig/Node/FormEnctypeNode.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/FormEnctypeNode.php new file mode 100644 index 0000000..93bce1b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/FormEnctypeNode.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\Twig\Node; + +/** + * @author Bernhard Schussek + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * the helper "form_start()" instead. + */ +class FormEnctypeNode extends SearchAndRenderBlockNode +{ + public function compile(\Twig_Compiler $compiler) + { + parent::compile($compiler); + + $compiler->raw(";\n"); + + // Uncomment this as soon as the deprecation note should be shown + // $compiler->write('trigger_error(\'The helper form_enctype(form) is deprecated since version 2.3 and will be removed in 3.0. Use form_start(form) instead.\', E_USER_DEPRECATED)'); + } +} 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..329ab86 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/FormThemeNode.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\Node; + +/** + * @author Fabien Potencier + */ +class FormThemeNode extends \Twig_Node +{ + public function __construct(\Twig_NodeInterface $form, \Twig_NodeInterface $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..822a272 --- /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 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..630e263 --- /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/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..a68c101 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/TransNode.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\Bridge\Twig\Node; + +/** + * @author Fabien Potencier + */ +class TransNode extends \Twig_Node +{ + public function __construct(\Twig_NodeInterface $body, \Twig_NodeInterface $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); + + $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_NodeInterface $body, \Twig_Node_Expression_Array $vars) + { + 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('/(?=')) { + foreach ($matches[1] as $var) { + $key = new \Twig_Node_Expression_Constant('%'.$var.'%', $body->getLine()); + if (!$vars->hasElement($key)) { + $vars->addElement(new \Twig_Node_Expression_Name($var, $body->getLine()), $key); + } + } + } else { + $current = array(); + foreach ($vars as $name => $var) { + $current[$name] = true; + } + foreach ($matches[1] as $var) { + if (!isset($current['%'.$var.'%'])) { + $vars->setNode('%'.$var.'%', new \Twig_Node_Expression_Name($var, $body->getLine())); + } + } + } + + 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..ce27b6a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/NodeVisitor/Scope.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\Twig\NodeVisitor; + +/** + * @author Jean-François Simon + */ +class Scope +{ + /** + * @var Scope|null + */ + private $parent; + + /** + * @var Scope[] + */ + private $children; + + /** + * @var array + */ + private $data; + + /** + * @var boolean + */ + private $left; + + /** + * @param Scope $parent + */ + public function __construct(Scope $parent = null) + { + $this->parent = $parent; + $this->left = false; + $this->data = array(); + } + + /** + * Opens a new child scope. + * + * @return Scope + */ + public function enter() + { + $child = new self($this); + $this->children[] = $child; + + return $child; + } + + /** + * 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 boolean + */ + 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..8e7e7f4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.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\NodeVisitor; + +use Symfony\Bridge\Twig\Node\TransNode; +use Symfony\Bridge\Twig\Node\TransDefaultDomainNode; + +/** + * TranslationDefaultDomainNodeVisitor. + * + * @author Fabien Potencier + */ +class TranslationDefaultDomainNodeVisitor implements \Twig_NodeVisitorInterface +{ + /** + * @var Scope + */ + private $scope; + + /** + * Constructor. + */ + public function __construct() + { + $this->scope = new Scope(); + } + + /** + * {@inheritdoc} + */ + public function enterNode(\Twig_NodeInterface $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'))) { + $ind = 'trans' === $node->getNode('filter')->getAttribute('value') ? 1 : 2; + $arguments = $node->getNode('arguments'); + 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} + */ + public function leaveNode(\Twig_NodeInterface $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; + } +} 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..592c250 --- /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 implements \Twig_NodeVisitorInterface +{ + 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} + */ + public function enterNode(\Twig_NodeInterface $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} + */ + public function leaveNode(\Twig_NodeInterface $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 null; + } + + return $this->getReadDomainFromNode($argument); + } + + /** + * @param \Twig_Node $node + * + * @return string|null + */ + private function getReadDomainFromNode(\Twig_Node $node = null) + { + if (null === $node) { + return null; + } + + 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..e566323 --- /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 +Symfony2 components. + +Resources +--------- + +If you want to run the unit tests, install dev dependencies before +running PHPUnit: + + $ cd path/to/Symfony/Bridge/Twig/ + $ composer.phar install --dev + $ phpunit 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..453c29c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig @@ -0,0 +1,390 @@ +{# Widgets #} + +{% block form_widget %} +{% spaceless %} + {% if compound %} + {{ block('form_widget_compound') }} + {% else %} + {{ block('form_widget_simple') }} + {% endif %} +{% endspaceless %} +{% endblock form_widget %} + +{% block form_widget_simple %} +{% spaceless %} + {% set type = type|default('text') %} + +{% endspaceless %} +{% endblock form_widget_simple %} + +{% block form_widget_compound %} +{% spaceless %} +
    + {% if form.parent is empty %} + {{ form_errors(form) }} + {% endif %} + {{ block('form_rows') }} + {{ form_rest(form) }} +
    +{% endspaceless %} +{% endblock form_widget_compound %} + +{% block collection_widget %} +{% spaceless %} + {% if prototype is defined %} + {% set attr = attr|merge({'data-prototype': form_row(prototype) }) %} + {% endif %} + {{ block('form_widget') }} +{% endspaceless %} +{% endblock collection_widget %} + +{% block textarea_widget %} +{% spaceless %} + +{% endspaceless %} +{% endblock textarea_widget %} + +{% block choice_widget %} +{% spaceless %} + {% if expanded %} + {{ block('choice_widget_expanded') }} + {% else %} + {{ block('choice_widget_collapsed') }} + {% endif %} +{% endspaceless %} +{% endblock choice_widget %} + +{% block choice_widget_expanded %} +{% spaceless %} +
    + {% for child in form %} + {{ form_widget(child) }} + {{ form_label(child) }} + {% endfor %} +
    +{% endspaceless %} +{% endblock choice_widget_expanded %} + +{% block choice_widget_collapsed %} +{% spaceless %} + +{% endspaceless %} +{% endblock choice_widget_collapsed %} + +{% block choice_widget_options %} +{% spaceless %} + {% for group_label, choice in options %} + {% if choice is iterable %} + + {% set options = choice %} + {{ block('choice_widget_options') }} + + {% else %} + + {% endif %} + {% endfor %} +{% endspaceless %} +{% endblock choice_widget_options %} + +{% block checkbox_widget %} +{% spaceless %} + +{% endspaceless %} +{% endblock checkbox_widget %} + +{% block radio_widget %} +{% spaceless %} + +{% endspaceless %} +{% endblock radio_widget %} + +{% block datetime_widget %} +{% spaceless %} + {% 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 %} +{% endspaceless %} +{% endblock datetime_widget %} + +{% block date_widget %} +{% spaceless %} + {% 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 %} +{% endspaceless %} +{% endblock date_widget %} + +{% block time_widget %} +{% spaceless %} + {% 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 %} +{% endspaceless %} +{% endblock time_widget %} + +{% block number_widget %} +{% spaceless %} + {# type="number" doesn't work with floats #} + {% set type = type|default('text') %} + {{ block('form_widget_simple') }} +{% endspaceless %} +{% endblock number_widget %} + +{% block integer_widget %} +{% spaceless %} + {% set type = type|default('number') %} + {{ block('form_widget_simple') }} +{% endspaceless %} +{% endblock integer_widget %} + +{% block money_widget %} +{% spaceless %} + {{ money_pattern|replace({ '{{ widget }}': block('form_widget_simple') })|raw }} +{% endspaceless %} +{% endblock money_widget %} + +{% block url_widget %} +{% spaceless %} + {% set type = type|default('url') %} + {{ block('form_widget_simple') }} +{% endspaceless %} +{% endblock url_widget %} + +{% block search_widget %} +{% spaceless %} + {% set type = type|default('search') %} + {{ block('form_widget_simple') }} +{% endspaceless %} +{% endblock search_widget %} + +{% block percent_widget %} +{% spaceless %} + {% set type = type|default('text') %} + {{ block('form_widget_simple') }} % +{% endspaceless %} +{% endblock percent_widget %} + +{% block password_widget %} +{% spaceless %} + {% set type = type|default('password') %} + {{ block('form_widget_simple') }} +{% endspaceless %} +{% endblock password_widget %} + +{% block hidden_widget %} +{% spaceless %} + {% set type = type|default('hidden') %} + {{ block('form_widget_simple') }} +{% endspaceless %} +{% endblock hidden_widget %} + +{% block email_widget %} +{% spaceless %} + {% set type = type|default('email') %} + {{ block('form_widget_simple') }} +{% endspaceless %} +{% endblock email_widget %} + +{% block button_widget %} +{% spaceless %} + {% if label is empty %} + {% set label = name|humanize %} + {% endif %} + +{% endspaceless %} +{% endblock button_widget %} + +{% block submit_widget %} +{% spaceless %} + {% set type = type|default('submit') %} + {{ block('button_widget') }} +{% endspaceless %} +{% endblock submit_widget %} + +{% block reset_widget %} +{% spaceless %} + {% set type = type|default('reset') %} + {{ block('button_widget') }} +{% endspaceless %} +{% endblock reset_widget %} + +{# Labels #} + +{% block form_label %} +{% spaceless %} + {% if label is not sameas(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 %} + {% set label = name|humanize %} + {% endif %} + {{ label|trans({}, translation_domain) }} + {% endif %} +{% endspaceless %} +{% endblock form_label %} + +{% block button_label %}{% endblock %} + +{# Rows #} + +{% block repeated_row %} +{% spaceless %} + {# + No need to render the errors here, as all errors are mapped + to the first child (see RepeatedTypeValidatorExtension). + #} + {{ block('form_rows') }} +{% endspaceless %} +{% endblock repeated_row %} + +{% block form_row %} +{% spaceless %} +
    + {{ form_label(form) }} + {{ form_errors(form) }} + {{ form_widget(form) }} +
    +{% endspaceless %} +{% endblock form_row %} + +{% block button_row %} +{% spaceless %} +
    + {{ form_widget(form) }} +
    +{% endspaceless %} +{% endblock button_row %} + +{% block hidden_row %} + {{ form_widget(form) }} +{% endblock hidden_row %} + +{# Misc #} + +{% block form %} +{% spaceless %} + {{ form_start(form) }} + {{ form_widget(form) }} + {{ form_end(form) }} +{% endspaceless %} +{% endblock form %} + +{% block form_start %} +{% spaceless %} + {% set method = method|upper %} + {% if method in ["GET", "POST"] %} + {% set form_method = method %} + {% else %} + {% set form_method = "POST" %} + {% endif %} +
    + {% if form_method != method %} + + {% endif %} +{% endspaceless %} +{% endblock form_start %} + +{% block form_end %} +{% spaceless %} + {% if not render_rest is defined or render_rest %} + {{ form_rest(form) }} + {% endif %} +
    +{% endspaceless %} +{% endblock form_end %} + +{% block form_enctype %} +{% spaceless %} + {% if multipart %}enctype="multipart/form-data"{% endif %} +{% endspaceless %} +{% endblock form_enctype %} + +{% block form_errors %} +{% spaceless %} + {% if errors|length > 0 %} +
      + {% for error in errors %} +
    • {{ error.message }}
    • + {% endfor %} +
    + {% endif %} +{% endspaceless %} +{% endblock form_errors %} + +{% block form_rest %} +{% spaceless %} + {% for child in form %} + {% if not child.rendered %} + {{ form_row(child) }} + {% endif %} + {% endfor %} +{% endspaceless %} +{% endblock form_rest %} + +{# Support #} + +{% block form_rows %} +{% spaceless %} + {% for child in form %} + {{ form_row(child) }} + {% endfor %} +{% endspaceless %} +{% endblock form_rows %} + +{% block widget_attributes %} +{% spaceless %} + id="{{ id }}" name="{{ full_name }}"{% if read_only %} readonly="readonly"{% endif %}{% if disabled %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %} + {% for attrname, attrvalue in attr %}{% if attrname in ['placeholder', 'title'] %}{{ attrname }}="{{ attrvalue|trans({}, translation_domain) }}" {% else %}{{ attrname }}="{{ attrvalue }}" {% endif %}{% endfor %} +{% endspaceless %} +{% endblock widget_attributes %} + +{% block widget_container_attributes %} +{% spaceless %} + {% if id is not empty %}id="{{ id }}" {% endif %} + {% for attrname, attrvalue in attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %} +{% endspaceless %} +{% endblock widget_container_attributes %} + +{% block button_attributes %} +{% spaceless %} + id="{{ id }}" name="{{ full_name }}"{% if disabled %} disabled="disabled"{% endif %} + {% for attrname, attrvalue in attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %} +{% endspaceless %} +{% endblock button_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..aed4f8d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig @@ -0,0 +1,52 @@ +{% use "form_div_layout.html.twig" %} + +{% block form_row %} +{% spaceless %} + + + {{ form_label(form) }} + + + {{ form_errors(form) }} + {{ form_widget(form) }} + + +{% endspaceless %} +{% endblock form_row %} + +{% block button_row %} +{% spaceless %} + + + + {{ form_widget(form) }} + + +{% endspaceless %} +{% endblock button_row %} + +{% block hidden_row %} +{% spaceless %} + + + {{ form_widget(form) }} + + +{% endspaceless %} +{% endblock hidden_row %} + +{% block form_widget_compound %} +{% spaceless %} + + {% if form.parent is empty and errors|length > 0 %} + + + + {% endif %} + {{ block('form_rows') }} + {{ form_rest(form) }} +
    + {{ form_errors(form) }} +
    +{% endspaceless %} +{% endblock form_widget_compound %} 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..d935651 --- /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/Fixtures/StubFilesystemLoader.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php new file mode 100644 index 0000000..36c61cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.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\Fixtures; + +// Preventing autoloader throwing E_FATAL when Twig is now available +if (!class_exists('Twig_Environment')) { + class StubFilesystemLoader + { + } +} else { + class StubFilesystemLoader extends \Twig_Loader_Filesystem + { + protected function findTemplate($name) + { + // strip away bundle name + $parts = explode(':', $name); + + return parent::findTemplate(end($parts)); + } + } +} 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/FormExtensionDivLayoutTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php new file mode 100644 index 0000000..c5c134b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php @@ -0,0 +1,209 @@ + + * + * 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\Extension\Core\View\ChoiceView; +use Symfony\Component\Form\Tests\AbstractDivLayoutTest; + +class FormExtensionDivLayoutTest extends AbstractDivLayoutTest +{ + /** + * @var FormExtension + */ + protected $extension; + + protected function setUp() + { + if (!class_exists('Symfony\Component\Locale\Locale')) { + $this->markTestSkipped('The "Locale" component is not available'); + } + + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\Form\Form')) { + $this->markTestSkipped('The "Form" component is not available'); + } + + if (!class_exists('Twig_Environment')) { + $this->markTestSkipped('Twig is not available.'); + } + + parent::setUp(); + + $rendererEngine = new TwigRendererEngine(array( + 'form_div_layout.html.twig', + 'custom_widgets.html.twig', + )); + $renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface')); + + $this->extension = new FormExtension($renderer); + + $loader = new StubFilesystemLoader(array( + __DIR__.'/../../Resources/views/Form', + __DIR__, + )); + + $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 testThemeBlockInheritanceUsingUse() + { + $view = $this->factory + ->createNamed('name', 'email') + ->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', 'email') + ->createView() + ; + + $this->setTheme($view, array('theme_extends.html.twig')); + + $this->assertMatchesXpath( + $this->renderWidget($view), + '/input[@type="email"][@rel="theme"]' + ); + } + + public function isSelectedChoiceProvider() + { + // The commented cases should not be necessary anymore, because the + // choice lists should assure that both values passed here are always + // strings + return array( +// array(true, 0, 0), + array(true, '0', '0'), + array(true, '1', '1'), +// array(true, false, 0), +// array(true, true, 1), + array(true, '', ''), +// array(true, null, ''), + 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)); + } + + protected function renderForm(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form', $vars); + } + + protected function renderEnctype(FormView $view) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'enctype'); + } + + 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..99a7821 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.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\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() + { + if (!class_exists('Symfony\Component\Locale\Locale')) { + $this->markTestSkipped('The "Locale" component is not available'); + } + + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\Form\Form')) { + $this->markTestSkipped('The "Form" component is not available'); + } + + if (!class_exists('Twig_Environment')) { + $this->markTestSkipped('Twig is not available.'); + } + + parent::setUp(); + + $rendererEngine = new TwigRendererEngine(array( + 'form_table_layout.html.twig', + 'custom_widgets.html.twig', + )); + $renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface')); + + $this->extension = new FormExtension($renderer); + + $loader = new StubFilesystemLoader(array( + __DIR__.'/../../Resources/views/Form', + __DIR__, + )); + + $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; + } + + protected function renderForm(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form', $vars); + } + + protected function renderEnctype(FormView $view) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'enctype'); + } + + 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/HttpKernelExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php new file mode 100644 index 0000000..077927c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.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\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\HttpKernelExtension; +use Symfony\Bridge\Twig\Tests\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; + +class HttpKernelExtensionTest extends TestCase +{ + protected function setUp() + { + parent::setUp(); + + if (!class_exists('Symfony\Component\HttpKernel\HttpKernel')) { + $this->markTestSkipped('The "HttpKernel" component is not available'); + } + + if (!class_exists('Twig_Environment')) { + $this->markTestSkipped('Twig is not available.'); + } + } + + /** + * @expectedException \Twig_Error_Runtime + */ + public function testFragmentWithError() + { + $kernel = $this->getFragmentHandler($this->throwException(new \Exception('foo'))); + + $loader = new \Twig_Loader_Array(array('index' => '{{ fragment("foo") }}')); + $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false)); + $twig->addExtension(new HttpKernelExtension($kernel)); + + $this->renderTemplate($kernel); + } + + 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); + + $renderer = new FragmentHandler(array($strategy)); + $renderer->setRequest(Request::create('/')); + + 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..3c5d762 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.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\Extension; + +use Symfony\Bridge\Twig\Extension\RoutingExtension; +use Symfony\Bridge\Twig\Tests\TestCase; + +class RoutingExtensionTest extends TestCase +{ + protected function setUp() + { + parent::setUp(); + + if (!class_exists('Symfony\Component\Routing\Route')) { + $this->markTestSkipped('The "Routing" component is not available'); + } + } + + /** + * @dataProvider getEscapingTemplates + */ + public function testEscaping($template, $mustBeEscaped) + { + $twig = new \Twig_Environment(null, array('debug' => true, 'cache' => false, 'autoescape' => true, '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/TranslationExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php new file mode 100644 index 0000000..2b9c553 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.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\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; +use Symfony\Bridge\Twig\Tests\TestCase; + +class TranslationExtensionTest extends TestCase +{ + protected function setUp() + { + parent::setUp(); + + if (!class_exists('Symfony\Component\Translation\Translator')) { + $this->markTestSkipped('The "Translation" component is not available'); + } + + if (!class_exists('Twig_Environment')) { + $this->markTestSkipped('Twig is not available.'); + } + } + + 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)) { + print $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)); + } + + public function getTransTests() + { + return array( + // trans tag + array('{% trans %}Hello{% endtrans %}', 'Hello'), + array('{% trans %}%name%{% endtrans %}', 'Symfony2', array('name' => 'Symfony2')), + + array('{% trans from elsewhere %}Hello{% endtrans %}', 'Hello'), + + array('{% trans %}Hello %name%{% endtrans %}', 'Hello Symfony2', array('name' => 'Symfony2')), + array('{% trans with { \'%name%\': \'Symfony2\' } %}Hello %name%{% endtrans %}', 'Hello Symfony2'), + array('{% set vars = { \'%name%\': \'Symfony2\' } %}{% trans with vars %}Hello %name%{% endtrans %}', 'Hello Symfony2'), + + 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 (Symfony2)', array('count' => 5, 'name' => 'Symfony2')), + array('{% transchoice count with { \'%name%\': \'Symfony2\' } %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%){% endtranschoice %}', + 'There is 5 apples (Symfony2)', 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)), + + // trans filter + array('{{ "Hello"|trans }}', 'Hello'), + array('{{ name|trans }}', 'Symfony2', array('name' => 'Symfony2')), + array('{{ hello|trans({ \'%name%\': \'Symfony2\' }) }}', 'Hello Symfony2', array('hello' => 'Hello %name%')), + array('{% set vars = { \'%name%\': \'Symfony2\' } %}{{ hello|trans(vars) }}', 'Hello Symfony2', 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%\': \'Symfony2\'}) }}', 'There is 5 apples (Symfony2)', 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()))); + } + + 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/Extension/child_label.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig new file mode 100644 index 0000000..8c7c248 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/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/custom_widgets.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig new file mode 100644 index 0000000..12fd7c6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig @@ -0,0 +1,16 @@ +{% block _text_id_widget %} +{% spaceless %} +
    + {{ form_widget(form) }} +
    +{% endspaceless %} +{% endblock _text_id_widget %} + +{% block _name_entry_label %} +{% spaceless %} + {% if label is empty %} + {% set label = name|humanize %} + {% endif %} + +{% endspaceless %} +{% endblock _name_entry_label %} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig new file mode 100644 index 0000000..e96278b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/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/theme.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig new file mode 100644 index 0000000..da1c1b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/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/theme_extends.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig new file mode 100644 index 0000000..8c71986 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/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/theme_use.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig new file mode 100644 index 0000000..d485b8d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/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/Node/FormThemeTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php new file mode 100644 index 0000000..90afef1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.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\Twig\Tests\Node; + +use Symfony\Bridge\Twig\Tests\TestCase; +use Symfony\Bridge\Twig\Node\FormThemeNode; + +class FormThemeTest extends TestCase +{ + protected function setUp() + { + parent::setUp(); + + if (version_compare(\Twig_Environment::VERSION, '1.5.0', '<')) { + $this->markTestSkipped('Requires Twig version to be at least 1.5.0.'); + } + } + + 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->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) + { + if (version_compare(phpversion(), '5.4.0RC1', '>=')) { + return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name); + } + + return sprintf('$this->getContext($context, "%s")', $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..c1f247c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php @@ -0,0 +1,282 @@ + + * + * 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\Tests\TestCase; +use Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode; + +class SearchAndRenderBlockNodeTest extends TestCase +{ + protected function setUp() + { + parent::setUp(); + + if (version_compare(\Twig_Environment::VERSION, '1.5.0', '<')) { + $this->markTestSkipped('Requires Twig version to be at least 1.5.0.'); + } + } + + 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->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->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->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()); + + // "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()); + + // "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->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()); + + // "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->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()); + + // "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()); + + // "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) + { + if (version_compare(phpversion(), '5.4.0RC1', '>=')) { + return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name); + } + + return sprintf('$this->getContext($context, "%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..bcae591 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.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\Bridge\Twig\Tests\NodeVisitor; + +use Symfony\Bridge\Twig\NodeVisitor\Scope; +use Symfony\Bridge\Twig\Tests\TestCase; + +class ScopeTest extends 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..24a6215 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.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\Bridge\Twig\Tests\NodeVisitor; + +use Symfony\Bridge\Twig\NodeVisitor\TranslationDefaultDomainNodeVisitor; +use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor; +use Symfony\Bridge\Twig\Tests\TestCase; + +class TranslationDefaultDomainNodeVisitorTest extends TestCase +{ + private static $message = 'message'; + private static $domain = 'domain'; + + /** @dataProvider getDefaultDomainAssignmentTestData */ + public function testDefaultDomainAssignment(\Twig_Node $node) + { + $env = new \Twig_Environment(new \Twig_Loader_String(), 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(new \Twig_Loader_String(), 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)), + ); + } +} 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..4e3ee6f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.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\Tests\NodeVisitor; + +use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor; +use Symfony\Bridge\Twig\Tests\TestCase; + +class TranslationNodeVisitorTest extends TestCase +{ + /** @dataProvider getMessagesExtractionTestData */ + public function testMessagesExtraction(\Twig_Node $node, array $expectedMessages) + { + $env = new \Twig_Environment(new \Twig_Loader_String(), 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..277e777 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.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\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 = $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 = $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/TestCase.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/TestCase.php new file mode 100644 index 0000000..ecfb7da --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/TestCase.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\Bridge\Twig\Tests; + +abstract class TestCase extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Twig_Environment')) { + $this->markTestSkipped('Twig is not available.'); + } + } +} 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..077cd76 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.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\Tests\Node; + +use Symfony\Bridge\Twig\Tests\TestCase; +use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; +use Symfony\Bridge\Twig\Node\FormThemeNode; + +class FormThemeTokenParserTest extends TestCase +{ + protected function setUp() + { + parent::setUp(); + + if (version_compare(\Twig_Environment::VERSION, '1.5.0', '<')) { + $this->markTestSkipped('Requires Twig version to be at least 1.5.0.'); + } + } + + /** + * @dataProvider getTestsForFormTheme + */ + public function testCompile($source, $expected) + { + $env = new \Twig_Environment(new \Twig_Loader_String(), 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..a2c5cd3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.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\Translation; + +use Symfony\Bridge\Twig\Extension\TranslationExtension; +use Symfony\Bridge\Twig\Translation\TwigExtractor; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Bridge\Twig\Tests\TestCase; + +class TwigExtractorTest extends TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\Translation\Translator')) { + $this->markTestSkipped('The "Translation" component is not available'); + } + } + + /** + * @dataProvider getExtractData + */ + public function testExtract($template, $messages) + { + $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); + $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')), + ); + } +} 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..244d676 --- /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_NodeInterface A Twig_NodeInterface 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/TransChoiceTokenParser.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php new file mode 100644 index 0000000..be8ac5c --- /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_NodeInterface A Twig_NodeInterface 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 must be a simple text.'); + } + + $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..0a0ed55 --- /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_NodeInterface A Twig_NodeInterface 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..a11681c --- /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_NodeInterface A Twig_NodeInterface 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" or "from" keyword.'); + } + } + + // {% 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'); + } + + $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..b93193f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Translation/TwigExtractor.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\Bridge\Twig\Translation; + +use Symfony\Component\Finder\Finder; +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 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($directory, MessageCatalogue $catalogue) + { + // load any existing translation files + $finder = new Finder(); + $files = $finder->files()->name('*.twig')->in($directory); + foreach ($files as $file) { + $this->extractTemplate(file_get_contents($file->getPathname()), $catalogue); + } + } + + /** + * {@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] ? $message[1] : $this->defaultDomain); + } + + $visitor->disable(); + } +} 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..955d4e0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TwigEngine.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; + +use Symfony\Component\Templating\EngineInterface; +use Symfony\Component\Templating\StreamingEngineInterface; +use Symfony\Component\Templating\TemplateNameParserInterface; + +/** + * 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; + } + + /** + * Renders a template. + * + * @param mixed $name A template name + * @param array $parameters An array of parameters to pass to the template + * + * @return string The evaluated template as a string + * + * @throws \InvalidArgumentException if the template does not exist + * @throws \RuntimeException if the template cannot be rendered + */ + public function render($name, array $parameters = array()) + { + return $this->load($name)->render($parameters); + } + + /** + * Streams a template. + * + * @param mixed $name A template name or a TemplateReferenceInterface instance + * @param array $parameters An array of parameters to pass to the template + * + * @throws \RuntimeException if the template cannot be rendered + */ + public function stream($name, array $parameters = array()) + { + $this->load($name)->display($parameters); + } + + /** + * Returns true if the template exists. + * + * @param mixed $name A template name + * + * @return Boolean true if the template exists, false otherwise + */ + public function exists($name) + { + try { + $this->load($name); + } catch (\InvalidArgumentException $e) { + return false; + } + + return true; + } + + /** + * Returns true if this class is able to render the given template. + * + * @param string $name A template name + * + * @return Boolean True if this class supports the given resource, false otherwise + */ + 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 mixed $name A template name or an instance of 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($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..9cd57ae --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/composer.json @@ -0,0 +1,50 @@ +{ + "name": "symfony/twig-bridge", + "type": "symfony-bridge", + "description": "Symfony Twig Bridge", + "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.3", + "twig/twig": "~1.11" + }, + "require-dev": { + "symfony/form": "2.2.*", + "symfony/http-kernel": "~2.2", + "symfony/routing": "~2.2", + "symfony/templating": "~2.1", + "symfony/translation": "~2.2", + "symfony/yaml": "~2.0", + "symfony/security": "~2.0" + }, + "suggest": { + "symfony/form": "", + "symfony/http-kernel": "", + "symfony/routing": "", + "symfony/templating": "", + "symfony/translation": "", + "symfony/yaml": "", + "symfony/security": "" + }, + "autoload": { + "psr-0": { "Symfony\\Bridge\\Twig\\": "" } + }, + "target-dir": "Symfony/Bridge/Twig", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..cc9e0e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./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..0e9393c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -0,0 +1,83 @@ +CHANGELOG +========= + +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/RouterCacheWarmer.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php new file mode 100644 index 0000000..9fa48da --- /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 Boolean 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..7c441a8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.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\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 array An array of templates of type TemplateReferenceInterface + */ + public function findAllTemplates() + { + if (null !== $this->templates) { + return $this->templates; + } + + $templates = array(); + + foreach ($this->kernel->getBundles() as $name => $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 array An array of templates of type 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 array An array of templates of type TemplateReferenceInterface + */ + private function findTemplatesInBundle(BundleInterface $bundle) + { + $templates = $this->findTemplatesInFolder($bundle->getPath().'/Resources/views'); + $name = $bundle->getName(); + + 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..0936290 --- /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; + +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; + + /** + * @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; + } + } + + /** + * {@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->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();'; + } + + return <<boot(); +$profilerCode +echo serialize(\$kernel->handle(unserialize('$request'))); +EOF; + } +} 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..863e14b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.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\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\Finder\Finder; + +/** + * Command that places bundle web assets into a given directory. + * + * @author Fabien Potencier + */ +class AssetsInstallCommand extends ContainerAwareCommand +{ + /** + * {@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: + +php %command.full_name% web --symlink + +To make symlink relative, add the --relative option: + +php %command.full_name% web --symlink --relative + +EOT + ) + ; + } + + /** + * {@inheritdoc} + * + * @throws \InvalidArgumentException When the target directory does not exist or symlink cannot be used + */ + 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'))); + } + + if (!function_exists('symlink') && $input->getOption('symlink')) { + throw new \InvalidArgumentException('The symlink() function is not available on your system. You need to install the assets without the --symlink option.'); + } + + $filesystem = $this->getContainer()->get('filesystem'); + + // Create the bundles directory otherwise symlink will fail. + $bundlesDir = $targetArg.'/bundles/'; + $filesystem->mkdir($bundlesDir, 0777); + + $output->writeln(sprintf("Installing assets using the %s option", $input->getOption('symlink') ? 'symlink' : 'hard copy')); + + foreach ($this->getContainer()->get('kernel')->getBundles() as $bundle) { + if (is_dir($originDir = $bundle->getPath().'/Resources/public')) { + $targetDir = $bundlesDir.preg_replace('/bundle$/', '', strtolower($bundle->getName())); + + $output->writeln(sprintf('Installing assets for %s into %s', $bundle->getNamespace(), $targetDir)); + + $filesystem->remove($targetDir); + + if ($input->getOption('symlink')) { + if ($input->getOption('relative')) { + $relativeOriginDir = $filesystem->makePathRelative($originDir, realpath($bundlesDir)); + } else { + $relativeOriginDir = $originDir; + } + $filesystem->symlink($relativeOriginDir, $targetDir); + } else { + $filesystem->mkdir($targetDir, 0777); + // We use a custom iterator to ignore VCS files + $filesystem->mirror($originDir, $targetDir, Finder::create()->in($originDir)); + } + } + } + } +} 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..f84b466 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.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\Command; + +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\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) + { + $realCacheDir = $this->getContainer()->getParameter('kernel.cache_dir'); + $oldCacheDir = $realCacheDir.'_old'; + $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'); + $output->writeln(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 + $warmupDir = substr($realCacheDir, 0, -1).'_'; + + if ($filesystem->exists($warmupDir)) { + $filesystem->remove($warmupDir); + } + + $this->warmup($warmupDir, $realCacheDir, !$input->getOption('no-optional-warmers')); + + $filesystem->rename($realCacheDir, $oldCacheDir); + $filesystem->rename($warmupDir, $realCacheDir); + } + + $filesystem->remove($oldCacheDir); + } + + /** + * @param string $warmupDir + * @param string $realCacheDir + * @param bool $enableOptionalWarmers + */ + protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = true) + { + $this->getContainer()->get('filesystem')->remove($warmupDir); + + // 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(); + + // warmup temporary dir + $warmer = $tempKernel->getContainer()->get('cache_warmer'); + if ($enableOptionalWarmers) { + $warmer->enableOptionalWarmers(); + } + $warmer->warmUp($warmupDir); + + // fix references to the Kernel in .meta files + foreach (Finder::create()->files()->name('*.meta')->in($warmupDir) as $file) { + file_put_contents($file, preg_replace( + '/(C\:\d+\:)"'.get_class($tempKernel).'"/', + sprintf('$1"%s"', $realKernelClass), + file_get_contents($file) + )); + } + + // fix references to cached files with the real cache directory name + foreach (Finder::create()->files()->in($warmupDir) as $file) { + $content = str_replace($warmupDir, $realCacheDir, 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); + } + } + + /** + * @param KernelInterface $parent + * @param string $namespace + * @param string $parentClass + * @param string $warmupDir + * + * @return KernelInterface + */ + protected function getTempKernel(KernelInterface $parent, $namespace, $parentClass, $warmupDir) + { + $rootDir = $parent->getRootDir(); + // 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 = substr($parent->getName(), 0, -1).'_'; + $code = <<getContainer()->get('filesystem')->mkdir($warmupDir); + file_put_contents($file = $warmupDir.'/kernel.tmp', $code); + require_once $file; + @unlink($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..71f0711 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.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\FrameworkBundle\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * 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) + { + $kernel = $this->getContainer()->get('kernel'); + $output->writeln(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')); + } +} 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..f9e2d40 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Config\Definition\ReferenceDumper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * A console command for dumping available configuration reference + * + * @author Kevin Bond + */ +class ConfigDumpReferenceCommand extends ContainerDebugCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('config:dump-reference') + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'The Bundle or extension alias') + )) + ->setDescription('Dumps default configuration for an extension') + ->setHelp(<<%command.name% command dumps the default configuration for an extension/bundle. + +The extension alias or bundle name can be used: + +Example: + + php %command.full_name% framework + +or + + php %command.full_name% FrameworkBundle +EOF + ) + ; + } + + /** + * {@inheritdoc} + * + * @throws \LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $bundles = $this->getContainer()->get('kernel')->getBundles(); + $containerBuilder = $this->getContainerBuilder(); + + $name = $input->getArgument('name'); + + if (empty($name)) { + $output->writeln('Available registered bundles with their extension alias if available:'); + foreach ($bundles as $bundle) { + $extension = $bundle->getContainerExtension(); + $output->writeln($bundle->getName().($extension ? ': '.$extension->getAlias() : '')); + } + + return; + } + + $extension = null; + + if (preg_match('/Bundle$/', $name)) { + // input is bundle name + + if (isset($bundles[$name])) { + $extension = $bundles[$name]->getContainerExtension(); + } + + if (!$extension) { + throw new \LogicException(sprintf('No extensions with configuration available for "%s"', $name)); + } + + $message = 'Default configuration for "'.$name.'"'; + } else { + foreach ($bundles as $bundle) { + $extension = $bundle->getContainerExtension(); + + if ($extension && $extension->getAlias() === $name) { + break; + } + + $extension = null; + } + + if (!$extension) { + throw new \LogicException(sprintf('No extension with alias "%s" is enabled', $name)); + } + + $message = 'Default configuration for extension with alias: "'.$name.'"'; + } + + $configuration = $extension->getConfiguration(array(), $containerBuilder); + + if (!$configuration) { + throw new \LogicException(sprintf('The extension with alias "%s" does not have it\'s 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))); + } + + $output->writeln($message); + + $dumper = new ReferenceDumper(); + $output->writeln($dumper->dump($configuration)); + } +} 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..035f553 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.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\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 + */ + protected function getContainer() + { + if (null === $this->container) { + $this->container = $this->getApplication()->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..c2f17fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -0,0 +1,460 @@ + + * + * 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\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Definition; +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('container:debug') + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'A service name (foo)'), + new InputOption('show-private', null, InputOption::VALUE_NONE, 'Use to show public *and* private services'), + new InputOption('tag', null, InputOption::VALUE_REQUIRED, 'Show 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') + )) + ->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} + * + * @throws \LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->validateInput($input); + + $this->containerBuilder = $this->getContainerBuilder(); + + if ($input->getOption('parameters')) { + $parameters = $this->getContainerBuilder()->getParameterBag()->all(); + + // Sort parameters alphabetically + ksort($parameters); + + $this->outputParameters($output, $parameters); + + return; + } + + $parameter = $input->getOption('parameter'); + if (null !== $parameter) { + $output->write($this->formatParameter($this->getContainerBuilder()->getParameter($parameter))); + + return; + } + + if ($input->getOption('tags')) { + $this->outputTags($output, $input->getOption('show-private')); + + return; + } + + $tag = $input->getOption('tag'); + if (null !== $tag) { + $serviceIds = array_keys($this->containerBuilder->findTaggedServiceIds($tag)); + } else { + $serviceIds = $this->containerBuilder->getServiceIds(); + } + + // sort so that it reads like an index of services + asort($serviceIds); + + $name = $input->getArgument('name'); + if ($name) { + $this->outputService($output, $name); + } else { + $this->outputServices($output, $serviceIds, $input->getOption('show-private'), $tag); + } + } + + 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.'); + } + } + + protected function outputServices(OutputInterface $output, $serviceIds, $showPrivate = false, $showTagAttributes = null) + { + // set the label to specify public or public+private + if ($showPrivate) { + $label = 'Public and private services'; + } else { + $label = 'Public services'; + } + if ($showTagAttributes) { + $label .= ' with tag '.$showTagAttributes.''; + } + + $output->writeln($this->getHelper('formatter')->formatSection('container', $label)); + + // loop through to get space needed and filter private services + $maxName = 4; + $maxScope = 6; + $maxTags = array(); + foreach ($serviceIds as $key => $serviceId) { + $definition = $this->resolveServiceDefinition($serviceId); + + if ($definition instanceof Definition) { + // filter out private services unless shown explicitly + if (!$showPrivate && !$definition->isPublic()) { + unset($serviceIds[$key]); + continue; + } + + if (strlen($definition->getScope()) > $maxScope) { + $maxScope = strlen($definition->getScope()); + } + + if (null !== $showTagAttributes) { + $tags = $definition->getTag($showTagAttributes); + 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); + } + } + } + } + } + + if (strlen($serviceId) > $maxName) { + $maxName = strlen($serviceId); + } + } + $format = '%-'.$maxName.'s '; + $format .= implode("", array_map(function($length) { return "%-{$length}s "; }, $maxTags)); + $format .= '%-'.$maxScope.'s %s'; + + // the title field needs extra space to make up for comment tags + $format1 = '%-'.($maxName + 19).'s '; + $format1 .= implode("", array_map(function($length) { return '%-'.($length + 19).'s '; }, $maxTags)); + $format1 .= '%-'.($maxScope + 19).'s %s'; + + $tags = array(); + foreach ($maxTags as $tagName => $length) { + $tags[] = ''.$tagName.''; + } + $output->writeln(vsprintf($format1, $this->buildArgumentsArray('Service Id', 'Scope', 'Class Name', $tags))); + + foreach ($serviceIds as $serviceId) { + $definition = $this->resolveServiceDefinition($serviceId); + + if ($definition instanceof Definition) { + $lines = array(); + if (null !== $showTagAttributes) { + foreach ($definition->getTag($showTagAttributes) as $key => $tag) { + $tagValues = array(); + foreach (array_keys($maxTags) as $tagName) { + $tagValues[] = isset($tag[$tagName]) ? $tag[$tagName] : ""; + } + if (0 === $key) { + $lines[] = $this->buildArgumentsArray($serviceId, $definition->getScope(), $definition->getClass(), $tagValues); + } else { + $lines[] = $this->buildArgumentsArray(' "', '', '', $tagValues); + } + } + } else { + $lines[] = $this->buildArgumentsArray($serviceId, $definition->getScope(), $definition->getClass()); + } + + foreach ($lines as $arguments) { + $output->writeln(vsprintf($format, $arguments)); + } + } elseif ($definition instanceof Alias) { + $alias = $definition; + $output->writeln(vsprintf($format, $this->buildArgumentsArray($serviceId, 'n/a', sprintf('alias for %s', (string) $alias), count($maxTags) ? array_fill(0, count($maxTags), "") : array()))); + } else { + // we have no information (happens with "service_container") + $service = $definition; + $output->writeln(vsprintf($format, $this->buildArgumentsArray($serviceId, '', get_class($service), count($maxTags) ? array_fill(0, count($maxTags), "") : array()))); + } + } + } + + protected function buildArgumentsArray($serviceId, $scope, $className, array $tagAttributes = array()) + { + $arguments = array($serviceId); + foreach ($tagAttributes as $tagAttribute) { + $arguments[] = $tagAttribute; + } + $arguments[] = $scope; + $arguments[] = $className; + + return $arguments; + } + + /** + * Renders detailed service information about one service + */ + protected function outputService(OutputInterface $output, $serviceId) + { + $definition = $this->resolveServiceDefinition($serviceId); + + $label = sprintf('Information for service %s', $serviceId); + $output->writeln($this->getHelper('formatter')->formatSection('container', $label)); + $output->writeln(''); + + if ($definition instanceof Definition) { + $output->writeln(sprintf('Service Id %s', $serviceId)); + $output->writeln(sprintf('Class %s', $definition->getClass() ?: "-")); + + $tags = $definition->getTags(); + if (count($tags)) { + $output->writeln('Tags'); + foreach ($tags as $tagName => $tagData) { + foreach ($tagData as $singleTagData) { + $output->writeln(sprintf(' - %-30s (%s)', $tagName, implode(', ', array_map(function($key, $value) { + return sprintf('%s: %s', $key, $value); + }, array_keys($singleTagData), array_values($singleTagData))))); + } + } + } else { + $output->writeln('Tags -'); + } + + $output->writeln(sprintf('Scope %s', $definition->getScope())); + + $public = $definition->isPublic() ? 'yes' : 'no'; + $output->writeln(sprintf('Public %s', $public)); + + $synthetic = $definition->isSynthetic() ? 'yes' : 'no'; + $output->writeln(sprintf('Synthetic %s', $synthetic)); + + $file = $definition->getFile() ? $definition->getFile() : '-'; + $output->writeln(sprintf('Required File %s', $file)); + } elseif ($definition instanceof Alias) { + $alias = $definition; + $output->writeln(sprintf('This service is an alias for the service %s', (string) $alias)); + } else { + // edge case (but true for "service_container", all we have is the service itself + $service = $definition; + $output->writeln(sprintf('Service Id %s', $serviceId)); + $output->writeln(sprintf('Class %s', get_class($service))); + } + } + + protected function outputParameters(OutputInterface $output, $parameters) + { + $output->writeln($this->getHelper('formatter')->formatSection('container', 'List of parameters')); + + $terminalDimensions = $this->getApplication()->getTerminalDimensions(); + $maxTerminalWidth = $terminalDimensions[0]; + $maxParameterWidth = 0; + $maxValueWidth = 0; + + // Determine max parameter & value length + foreach ($parameters as $parameter => $value) { + $parameterWidth = strlen($parameter); + if ($parameterWidth > $maxParameterWidth) { + $maxParameterWidth = $parameterWidth; + } + + $valueWith = strlen($this->formatParameter($value)); + if ($valueWith > $maxValueWidth) { + $maxValueWidth = $valueWith; + } + } + + $maxValueWidth = min($maxValueWidth, $maxTerminalWidth - $maxParameterWidth - 1); + + $formatTitle = '%-'.($maxParameterWidth + 19).'s %-'.($maxValueWidth + 19).'s'; + $format = '%-'.$maxParameterWidth.'s %-'.$maxValueWidth.'s'; + + $output->writeln(sprintf($formatTitle, 'Parameter', 'Value')); + + foreach ($parameters as $parameter => $value) { + $splits = str_split($this->formatParameter($value), $maxValueWidth); + + foreach ($splits as $index => $split) { + if (0 === $index) { + $output->writeln(sprintf($format, $parameter, $split)); + } else { + $output->writeln(sprintf($format, ' ', $split)); + } + } + } + } + + /** + * Loads the ContainerBuilder from the cache. + * + * @return ContainerBuilder + * + * @throws \LogicException + */ + protected function getContainerBuilder() + { + 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 $container; + } + + /** + * Given an array of service IDs, this returns the array of corresponding + * Definition and Alias objects that those ids represent. + * + * @param string $serviceId The service id to resolve + * + * @return Definition|Alias + */ + protected function resolveServiceDefinition($serviceId) + { + if ($this->containerBuilder->hasDefinition($serviceId)) { + return $this->containerBuilder->getDefinition($serviceId); + } + + // Some service IDs don't have a Definition, they're simply an Alias + if ($this->containerBuilder->hasAlias($serviceId)) { + return $this->containerBuilder->getAlias($serviceId); + } + + // the service has been injected in some special way, just return the service + return $this->containerBuilder->get($serviceId); + } + + /** + * Renders list of tagged services grouped by tag + * + * @param OutputInterface $output + * @param Boolean $showPrivate + */ + protected function outputTags(OutputInterface $output, $showPrivate = false) + { + $tags = $this->containerBuilder->findTags(); + asort($tags); + + $label = 'Tagged services'; + $output->writeln($this->getHelper('formatter')->formatSection('container', $label)); + + foreach ($tags as $tag) { + $serviceIds = $this->containerBuilder->findTaggedServiceIds($tag); + + foreach ($serviceIds as $serviceId => $attributes) { + $definition = $this->resolveServiceDefinition($serviceId); + if ($definition instanceof Definition) { + if (!$showPrivate && !$definition->isPublic()) { + unset($serviceIds[$serviceId]); + continue; + } + } + } + + if (count($serviceIds) === 0) { + continue; + } + + $output->writeln($this->getHelper('formatter')->formatSection('tag', $tag)); + + foreach ($serviceIds as $serviceId => $attributes) { + $output->writeln($serviceId); + } + + $output->writeln(''); + } + } + + protected function formatParameter($value) + { + if (is_bool($value) || is_array($value) || (null === $value)) { + return json_encode($value); + } + + return $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/RouterApacheDumperCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/RouterApacheDumperCommand.php new file mode 100644 index 0000000..c8a17e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/RouterApacheDumperCommand.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\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\Routing\Matcher\Dumper\ApacheMatcherDumper; +use Symfony\Component\Routing\RouterInterface; + +/** + * RouterApacheDumperCommand. + * + * @author Fabien Potencier + */ +class RouterApacheDumperCommand 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:dump-apache') + ->setDefinition(array( + new InputArgument('script_name', InputArgument::OPTIONAL, 'The script name of the application\'s front controller.'), + new InputOption('base-uri', null, InputOption::VALUE_REQUIRED, 'The base URI'), + )) + ->setDescription('Dumps all routes as Apache rewrite rules') + ->setHelp(<<%command.name% dumps all routes as Apache rewrite rules. +These can then be used with the ApacheUrlMatcher to use Apache for route +matching. + + php %command.full_name% +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $router = $this->getContainer()->get('router'); + + $dumpOptions = array(); + if ($input->getArgument('script_name')) { + $dumpOptions['script_name'] = $input->getArgument('script_name'); + } + if ($input->getOption('base-uri')) { + $dumpOptions['base_uri'] = $input->getOption('base-uri'); + } + + $dumper = new ApacheMatcherDumper($router->getRouteCollection()); + + $output->writeln($dumper->dump($dumpOptions), OutputInterface::OUTPUT_RAW); + } +} 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..d581af5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.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\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Routing\RouterInterface; + +/** + * 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('router:debug') + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'A route name'), + )) + ->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) + { + $name = $input->getArgument('name'); + + if ($name) { + $this->outputRoute($output, $name); + } else { + $this->outputRoutes($output); + } + } + + protected function outputRoutes(OutputInterface $output, $routes = null) + { + if (null === $routes) { + $routes = $this->getContainer()->get('router')->getRouteCollection()->all(); + } + + $output->writeln($this->getHelper('formatter')->formatSection('router', 'Current routes')); + + $maxName = strlen('name'); + $maxMethod = strlen('method'); + $maxScheme = strlen('scheme'); + $maxHost = strlen('host'); + + foreach ($routes as $name => $route) { + $method = $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY'; + $scheme = $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY'; + $host = '' !== $route->getHost() ? $route->getHost() : 'ANY'; + $maxName = max($maxName, strlen($name)); + $maxMethod = max($maxMethod, strlen($method)); + $maxScheme = max($maxScheme, strlen($scheme)); + $maxHost = max($maxHost, strlen($host)); + } + + $format = '%-'.$maxName.'s %-'.$maxMethod.'s %-'.$maxScheme.'s %-'.$maxHost.'s %s'; + $formatHeader = '%-'.($maxName + 19).'s %-'.($maxMethod + 19).'s %-'.($maxScheme + 19).'s %-'.($maxHost + 19).'s %s'; + $output->writeln(sprintf($formatHeader, 'Name', 'Method', 'Scheme', 'Host', 'Path')); + + foreach ($routes as $name => $route) { + $method = $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY'; + $scheme = $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY'; + $host = '' !== $route->getHost() ? $route->getHost() : 'ANY'; + $output->writeln(sprintf($format, $name, $method, $scheme, $host, $route->getPath()), OutputInterface::OUTPUT_RAW); + } + } + + /** + * @throws \InvalidArgumentException When route does not exist + */ + protected function outputRoute(OutputInterface $output, $name) + { + $route = $this->getContainer()->get('router')->getRouteCollection()->get($name); + if (!$route) { + throw new \InvalidArgumentException(sprintf('The route "%s" does not exist.', $name)); + } + + $output->writeln($this->getHelper('formatter')->formatSection('router', sprintf('Route "%s"', $name))); + + $method = $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY'; + $scheme = $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY'; + $host = '' !== $route->getHost() ? $route->getHost() : 'ANY'; + + $output->write('Name '); + $output->writeln($name, OutputInterface::OUTPUT_RAW); + + $output->write('Path '); + $output->writeln($route->getPath(), OutputInterface::OUTPUT_RAW); + + $output->write('Host '); + $output->writeln($host, OutputInterface::OUTPUT_RAW); + + $output->write('Scheme '); + $output->writeln($scheme, OutputInterface::OUTPUT_RAW); + + $output->write('Method '); + $output->writeln($method, OutputInterface::OUTPUT_RAW); + + $output->write('Class '); + $output->writeln(get_class($route), OutputInterface::OUTPUT_RAW); + + $output->write('Defaults '); + $output->writeln($this->formatConfigs($route->getDefaults()), OutputInterface::OUTPUT_RAW); + + $output->write('Requirements '); + // we do not want to show the schemes and methods again that are also in the requirements for BC + $requirements = $route->getRequirements(); + unset($requirements['_scheme'], $requirements['_method']); + $output->writeln($this->formatConfigs($requirements) ?: 'NO CUSTOM', OutputInterface::OUTPUT_RAW); + + $output->write('Options '); + $output->writeln($this->formatConfigs($route->getOptions()), OutputInterface::OUTPUT_RAW); + + $output->write('Path-Regex '); + $output->writeln($route->compile()->getRegex(), OutputInterface::OUTPUT_RAW); + + if (null !== $route->compile()->getHostRegex()) { + $output->write('Host-Regex '); + $output->writeln($route->compile()->getHostRegex(), OutputInterface::OUTPUT_RAW); + } + } + + 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)); + } + + private function formatConfigs(array $array) + { + $string = ''; + ksort($array); + foreach ($array as $name => $value) { + $string .= ($string ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value); + } + + return $string; + } +} 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..4059969 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.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\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\ArrayInput; +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'), + )) + ->setDescription('Helps debug routes by simulating a path info match') + ->setHelp(<<%command.name% simulates a path info match: + + php %command.full_name% /foo +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $router = $this->getContainer()->get('router'); + $matcher = new TraceableUrlMatcher($router->getRouteCollection(), $router->getContext()); + + $traces = $matcher->getTraces($input->getArgument('path_info')); + + $matches = false; + foreach ($traces as $i => $trace) { + if (TraceableUrlMatcher::ROUTE_ALMOST_MATCHES == $trace['level']) { + $output->writeln(sprintf('Route "%s" almost matches but %s', $trace['name'], lcfirst($trace['log']))); + } elseif (TraceableUrlMatcher::ROUTE_MATCHES == $trace['level']) { + $output->writeln(sprintf('Route "%s" matches', $trace['name'])); + + $routerDebugcommand = $this->getApplication()->find('router:debug'); + $output->writeln(''); + $routerDebugcommand->run(new ArrayInput(array('name' => $trace['name'])), $output); + + $matches = true; + } elseif ($input->getOption('verbose')) { + $output->writeln(sprintf('Route "%s" does not match: %s', $trace['name'], $trace['log'])); + } + } + + if (!$matches) { + $output->writeln('None of the routes matches'); + + return 1; + } + } +} 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..1d01e04 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.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\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\Process\ProcessBuilder; + +/** + * Runs Symfony2 application using PHP built-in web server + * + * @author Michał Pipa + */ +class ServerRunCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + public function isEnabled() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + return false; + } + + return parent::isEnabled(); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', 'localhost:8000'), + new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', 'web/'), + 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 + +See also: http://www.php.net/manual/en/features.commandline.webserver.php +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $env = $this->getContainer()->getParameter('kernel.environment'); + + if ('prod' === $env) { + $output->writeln('Running PHP built-in server in production environment is NOT recommended!'); + } + + $router = $input->getOption('router') ?: $this + ->getContainer() + ->get('kernel') + ->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env)) + ; + + $output->writeln(sprintf("Server running on %s\n", $input->getArgument('address'))); + + $builder = new ProcessBuilder(array(PHP_BINARY, '-S', $input->getArgument('address'), $router)); + $builder->setWorkingDirectory($input->getOption('docroot')); + $builder->setTimeout(null); + $builder->getProcess()->run(function ($type, $buffer) use ($output) { + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $output->write($buffer); + } + }); + } +} 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..cc3aabf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Translation\Catalogue\DiffOperation; +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; +use Symfony\Component\Yaml\Yaml; + +/** + * A command that parse templates to extract translation messages and add 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::REQUIRED, 'The bundle where to load the messages'), + 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( + '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. 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. + +php %command.full_name% --dump-messages en AcmeBundle +php %command.full_name% --force --prefix="new_" fr AcmeBundle +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // check presence of force or dump-message + if ($input->getOption('force') !== true && $input->getOption('dump-messages') !== true) { + $output->writeln('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)) { + $output->writeln('Wrong output format'); + $output->writeln('Supported formats are '.implode(', ', $supportedFormats).'.'); + + return 1; + } + + // get bundle directory + $foundBundle = $this->getApplication()->getKernel()->getBundle($input->getArgument('bundle')); + $bundleTransPath = $foundBundle->getPath().'/Resources/translations'; + $output->writeln(sprintf('Generating "%s" translation files for "%s"', $input->getArgument('locale'), $foundBundle->getName())); + + // load any messages from templates + $extractedCatalogue = new MessageCatalogue($input->getArgument('locale')); + $output->writeln('Parsing templates'); + $extractor = $this->getContainer()->get('translation.extractor'); + $extractor->setPrefix($input->getOption('prefix')); + $extractor->extract($foundBundle->getPath().'/Resources/views/', $extractedCatalogue); + + // load any existing messages from the translation files + $currentCatalogue = new MessageCatalogue($input->getArgument('locale')); + $output->writeln('Loading translation files'); + $loader = $this->getContainer()->get('translation.loader'); + $loader->loadMessages($bundleTransPath, $currentCatalogue); + + // process catalogues + $operation = $input->getOption('clean') + ? new DiffOperation($currentCatalogue, $extractedCatalogue) + : new MergeOperation($currentCatalogue, $extractedCatalogue); + + // show compiled list of messages + if ($input->getOption('dump-messages') === true) { + foreach ($operation->getDomains() as $domain) { + $output->writeln(sprintf("\nDisplaying messages for domain %s:\n", $domain)); + $newKeys = array_keys($operation->getNewMessages($domain)); + $allKeys = array_keys($operation->getMessages($domain)); + foreach (array_diff($allKeys, $newKeys) as $id) { + $output->writeln($id); + } + foreach ($newKeys as $id) { + $output->writeln(sprintf('%s', $id)); + } + foreach (array_keys($operation->getObsoleteMessages($domain)) as $id) { + $output->writeln(sprintf('%s', $id)); + } + } + + if ($input->getOption('output-format') == 'xliff') { + $output->writeln('Xliff output version is 1.2'); + } + } + + // save the files + if ($input->getOption('force') === true) { + $output->writeln('Writing files'); + $writer->writeTranslations($operation->getResult(), $input->getOption('output-format'), array('path' => $bundleTransPath)); + } + } +} 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..06a8698 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.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\FrameworkBundle\Console; + +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; + + /** + * 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('--shell', '-s', InputOption::VALUE_NONE, 'Launch the shell.')); + $this->getDefinition()->addOption(new InputOption('--process-isolation', null, InputOption::VALUE_NONE, 'Launch commands from shell as a separate process.')); + $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 integer 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + $this->registerCommands(); + + $this->setDispatcher($this->kernel->getContainer()->get('event_dispatcher')); + + if (true === $input->hasParameterOption(array('--shell', '-s'))) { + $shell = new Shell($this); + $shell->setProcessIsolation($input->hasParameterOption(array('--process-isolation'))); + $shell->run(); + + return 0; + } + + return parent::doRun($input, $output); + } + + protected function registerCommands() + { + $this->kernel->boot(); + + foreach ($this->kernel->getBundles() as $bundle) { + if ($bundle instanceof Bundle) { + $bundle->registerCommands($this); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Shell.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Shell.php new file mode 100644 index 0000000..33100bd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Shell.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\Console; + +use Symfony\Component\Console\Shell as BaseShell; + +/** + * Shell. + * + * @author Fabien Potencier + */ +class Shell extends BaseShell +{ + /** + * Returns the shell header. + * + * @return string The header string + */ + protected function getHeader() + { + return << + _____ __ ___ + / ____| / _| |__ \ + | (___ _ _ _ __ ___ | |_ ___ _ __ _ _ ) | + \___ \| | | | '_ ` _ \| _/ _ \| '_ \| | | | / / + ____) | |_| | | | | | | || (_) | | | | |_| |/ /_ + |_____/ \__, |_| |_| |_|_| \___/|_| |_|\__, |____| + __/ | __/ | + |___/ |___/ + + +EOF + .parent::getHeader(); + } +} 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..5655595 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.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\FrameworkBundle\Controller; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Form\FormTypeInterface; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +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 + */ +class Controller extends ContainerAware +{ + /** + * Generates a URL from the given parameters. + * + * @param string $route The name of the route + * @param mixed $parameters An array of parameters + * @param Boolean|string $referenceType The type of reference (one of the constants in UrlGeneratorInterface) + * + * @return string The generated URL + * + * @see UrlGeneratorInterface + */ + public 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 + */ + public function forward($controller, array $path = array(), array $query = array()) + { + $path['_controller'] = $controller; + $subRequest = $this->container->get('request')->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 integer $status The status code to use for the Response + * + * @return RedirectResponse + */ + public function redirect($url, $status = 302) + { + return new RedirectResponse($url, $status); + } + + /** + * 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 + */ + public function renderView($view, array $parameters = array()) + { + return $this->container->get('templating')->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 + */ + public function render($view, array $parameters = array(), Response $response = null) + { + return $this->container->get('templating')->renderResponse($view, $parameters, $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 + */ + public function stream($view, array $parameters = array(), StreamedResponse $response = null) + { + $templating = $this->container->get('templating'); + + $callback = function () use ($templating, $view, $parameters) { + $templating->stream($view, $parameters); + }; + + 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 $previous The previous exception + * + * @return NotFoundHttpException + */ + public function createNotFoundException($message = 'Not Found', \Exception $previous = null) + { + return new NotFoundHttpException($message, $previous); + } + + /** + * Creates and returns a Form instance from the type of the form. + * + * @param string|FormTypeInterface $type The built type of the form + * @param mixed $data The initial data for the form + * @param array $options Options for the form + * + * @return Form + */ + public 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 + */ + public function createFormBuilder($data = null, array $options = array()) + { + return $this->container->get('form.factory')->createBuilder('form', $data, $options); + } + + /** + * Shortcut to return the request service. + * + * @return Request + */ + public function getRequest() + { + return $this->container->get('request'); + } + + /** + * Shortcut to return the Doctrine Registry service. + * + * @return Registry + * + * @throws \LogicException If DoctrineBundle is not available + */ + public 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 Context + * + * @return mixed + * + * @throws \LogicException If SecurityBundle is not available + * + * @see Symfony\Component\Security\Core\Authentication\Token\TokenInterface::getUser() + */ + public function getUser() + { + if (!$this->container->has('security.context')) { + throw new \LogicException('The SecurityBundle is not registered in your application.'); + } + + if (null === $token = $this->container->get('security.context')->getToken()) { + return null; + } + + if (!is_object($user = $token->getUser())) { + return null; + } + + return $user; + } + + /** + * Returns true if the service id is defined. + * + * @param string $id The service id + * + * @return Boolean true if the service id is defined, false otherwise + */ + public function has($id) + { + return $this->container->has($id); + } + + /** + * Gets a service by id. + * + * @param string $id The service id + * + * @return object The service + */ + public function get($id) + { + return $this->container->get($id); + } +} 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..4b1c665 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.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\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) + { + 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(); + + // this throws an exception if there is no such bundle + foreach ($this->kernel->getBundle($bundle, false) as $b) { + $try = $b->getNamespace().'\\Controller\\'.$controller.'Controller'; + if (class_exists($try)) { + return $try.'::'.$action.'Action'; + } + + $bundles[] = $b->getName(); + $msg = sprintf('Unable to find controller "%s:%s" - class "%s" does not exist.', $bundle, $controller, $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)); + } +} 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..a620546 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.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\Controller; + +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Controller\ControllerResolver as BaseControllerResolver; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +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); + } else { + throw new \LogicException(sprintf('Unable to parse the controller name "%s".', $controller)); + } + } + + list($class, $method) = explode('::', $controller, 2); + + if (!class_exists($class)) { + throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); + } + + $controller = new $class(); + if ($controller instanceof ContainerAwareInterface) { + $controller->setContainer($this->container); + } + + return array($controller, $method); + } +} 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..59df4ae --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.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\Bundle\FrameworkBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * Redirects a request to another URL. + * + * @author Fabien Potencier + */ +class RedirectController extends ContainerAware +{ + /** + * 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 Boolean $permanent Whether the redirection is permanent + * @param Boolean|array $ignoreAttributes Whether to ignore attributes or an array of attributes to ignore + * + * @return Response A Response instance + */ + public function redirectAction(Request $request, $route, $permanent = false, $ignoreAttributes = false) + { + if ('' == $route) { + return new Response(null, $permanent ? 410 : 404); + } + + $attributes = array(); + if (false === $ignoreAttributes || is_array($ignoreAttributes)) { + $attributes = $request->attributes->get('_route_params'); + unset($attributes['route'], $attributes['permanent']); + 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 Boolean $permanent Whether the redirect is permanent or not + * @param string|null $scheme The URL scheme (null to keep the current one) + * @param integer|null $httpPort The HTTP port (null to keep the current one for the same scheme or the configured port in the container) + * @param integer|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 + */ + public function urlRedirectAction(Request $request, $path, $permanent = false, $scheme = null, $httpPort = null, $httpsPort = null) + { + if ('' == $path) { + return new Response(null, $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) { + $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..105d14a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.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\FrameworkBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\HttpFoundation\Response; + +/** + * TemplateController. + * + * @author Fabien Potencier + */ +class TemplateController extends ContainerAware +{ + /** + * 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 Boolean|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($private); + } + + 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..c98e4e6 --- /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..cf77dd5 --- /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/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/AddValidatorInitializersPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php new file mode 100644 index 0000000..bf9f338 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.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\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')) { + return; + } + + $initializers = array(); + foreach ($container->findTaggedServiceIds('validator.initializer') as $id => $attributes) { + $initializers[] = new Reference($id); + } + + $container->getDefinition('validator')->replaceArgument(4, $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..20591c8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.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\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\Filesystem\Filesystem; + +class CompilerDebugDumpPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $filesystem = new Filesystem(); + $filesystem->dumpFile( + $this->getCompilerLogFilename($container), + implode("\n", $container->getCompiler()->getLog()), + 0666 & ~umask() + ); + } + + 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/ContainerBuilderDebugDumpPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php new file mode 100644 index 0000000..a9916cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\XmlDumper; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\Filesystem\Filesystem; + +/** + * Dumps the ContainerBuilder to a cache file so that it can be used by + * debugging tools such as the container:debug console command. + * + * @author Ryan Weaver + * @author Fabien Potencier + */ +class ContainerBuilderDebugDumpPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $dumper = new XmlDumper($container); + $filesystem = new Filesystem(); + $filesystem->dumpFile( + $container->getParameter('debug.container.dump'), + $dumper->dump(), + 0666 & ~umask() + ); + } +} 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..7fd96e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.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\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 service IDs as keys and tag aliases as values + $types = array(); + + foreach ($container->findTaggedServiceIds('form.type') as $serviceId => $tag) { + $alias = isset($tag[0]['alias']) + ? $tag[0]['alias'] + : $serviceId; + + // Flip, because we want tag aliases (= type identifiers) as keys + $types[$alias] = $serviceId; + } + + $definition->replaceArgument(1, $types); + + $typeExtensions = array(); + + foreach ($container->findTaggedServiceIds('form.type_extension') as $serviceId => $tag) { + $alias = isset($tag[0]['alias']) + ? $tag[0]['alias'] + : $serviceId; + + $typeExtensions[$alias][] = $serviceId; + } + + $definition->replaceArgument(2, $typeExtensions); + + // Find all services annotated with "form.type_guesser" + $guessers = array_keys($container->findTaggedServiceIds('form.type_guesser')); + + $definition->replaceArgument(3, $guessers); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FragmentRendererPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FragmentRendererPass.php new file mode 100644 index 0000000..e3284ab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FragmentRendererPass.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; + +/** + * Adds services tagged kernel.fragment_renderer as HTTP content rendering strategies. + * + * @author Fabien Potencier + */ +class FragmentRendererPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition('fragment.handler')) { + return; + } + + $definition = $container->getDefinition('fragment.handler'); + foreach (array_keys($container->findTaggedServiceIds('kernel.fragment_renderer')) as $id) { + // We must assume that the class value has been correctly filled, even if the service is created by a factory + $class = $container->getDefinition($id)->getClass(); + + $refClass = new \ReflectionClass($class); + $interface = 'Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface'; + if (!$refClass->implementsInterface($interface)) { + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + + $definition->addMethodCall('addRenderer', array(new Reference($id))); + } + } +} 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..d7dce5a --- /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/RoutingResolverPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php new file mode 100644 index 0000000..322f7ba --- /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..1a697c4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.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\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); + } + + 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 $tag) { + $priority = isset($tag['priority']) ? $tag['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..b7ba860 --- /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..d982ced --- /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/Configuration.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..8a73917 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -0,0 +1,413 @@ + + * + * 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 +{ + /** + * 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.") + ->defaultTrue() + ->end() + ->arrayNode('trusted_proxies') + ->beforeNormalization() + ->ifTrue(function($v) { return !is_array($v) && !is_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() + ->end() + ; + + $this->addFormSection($rootNode); + $this->addEsiSection($rootNode); + $this->addFragmentsSection($rootNode); + $this->addProfilerSection($rootNode); + $this->addRouterSection($rootNode); + $this->addSessionSection($rootNode); + $this->addTemplatingSection($rootNode); + $this->addTranslatorSection($rootNode); + $this->addValidationSection($rootNode); + $this->addAnnotationsSection($rootNode); + $this->addSerializerSection($rootNode); + + return $treeBuilder; + } + + private function addFormSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('form') + ->info('form configuration') + ->canBeEnabled() + ->end() + ->arrayNode('csrf_protection') + ->canBeDisabled() + ->children() + ->scalarNode('field_name')->defaultValue('_token')->end() + ->end() + ->end() + ->end() + ; + } + + private function addEsiSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('esi') + ->info('esi 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() + ->scalarNode('username')->defaultValue('')->end() + ->scalarNode('password')->defaultValue('')->end() + ->scalarNode('lifetime')->defaultValue(86400)->end() + ->arrayNode('matcher') + ->canBeUnset() + ->performNoDeepMerging() + ->children() + ->scalarNode('ip')->end() + ->scalarNode('path') + ->info('use the urldecoded format') + ->example('^/path to resource/') + ->end() + ->scalarNode('service')->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')->end() + ->scalarNode('gc_divisor')->end() + ->scalarNode('gc_probability')->end() + ->scalarNode('gc_maxlifetime')->end() + ->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end() + ->end() + ->end() + ->end() + ; + } + + private function addTemplatingSection(ArrayNodeDefinition $rootNode) + { + $organizeUrls = function($urls) { + $urls += array( + 'http' => array(), + 'ssl' => array(), + ); + + foreach ($urls as $i => $url) { + if (is_integer($i)) { + if (0 === strpos($url, 'https://') || 0 === strpos($url, '//')) { + $urls['http'][] = $urls['ssl'][] = $url; + } else { + $urls['http'][] = $url; + } + unset($urls[$i]); + } + } + + return $urls; + }; + + $rootNode + ->children() + ->arrayNode('templating') + ->info('templating configuration') + ->canBeUnset() + ->children() + ->scalarNode('assets_version')->defaultValue(null)->end() + ->scalarNode('assets_version_format')->defaultValue('%%s?%%s')->end() + ->scalarNode('hinclude_default_template')->defaultNull()->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('assets_base_url') + ->children() + ->arrayNode('assets_base_urls') + ->performNoDeepMerging() + ->addDefaultsIfNotSet() + ->beforeNormalization() + ->ifTrue(function($v) { return !is_array($v); }) + ->then(function($v) { return array($v); }) + ->end() + ->beforeNormalization() + ->always() + ->then($organizeUrls) + ->end() + ->children() + ->arrayNode('http') + ->prototype('scalar')->end() + ->end() + ->arrayNode('ssl') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->scalarNode('cache')->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() + ->fixXmlConfig('package') + ->children() + ->arrayNode('packages') + ->useAttributeAsKey('name') + ->prototype('array') + ->fixXmlConfig('base_url') + ->children() + ->scalarNode('version')->defaultNull()->end() + ->scalarNode('version_format')->defaultValue('%%s?%%s')->end() + ->arrayNode('base_urls') + ->performNoDeepMerging() + ->addDefaultsIfNotSet() + ->beforeNormalization() + ->ifTrue(function($v) { return !is_array($v); }) + ->then(function($v) { return array($v); }) + ->end() + ->beforeNormalization() + ->always() + ->then($organizeUrls) + ->end() + ->children() + ->arrayNode('http') + ->prototype('scalar')->end() + ->end() + ->arrayNode('ssl') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addTranslatorSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('translator') + ->info('translator configuration') + ->canBeEnabled() + ->children() + ->scalarNode('fallback')->defaultValue('en')->end() + ->end() + ->end() + ->end() + ; + } + + private function addValidationSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('validation') + ->info('validation configuration') + ->canBeEnabled() + ->children() + ->scalarNode('cache')->end() + ->booleanNode('enable_annotations')->defaultFalse()->end() + ->scalarNode('translation_domain')->defaultValue('validators')->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() + ->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..ded7ac4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -0,0 +1,700 @@ + + * + * 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\DefinitionDecorator; +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; + +/** + * FrameworkExtension. + * + * @author Fabien Potencier + * @author Jeremy Mikola + */ +class FrameworkExtension extends Extension +{ + /** + * Responds to the app.config 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('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'); + + $loader->load('debug_prod.xml'); + + if ($container->getParameter('kernel.debug')) { + $loader->load('debug.xml'); + + // only HttpKernel needs the debug event dispatcher + $definition = $container->findDefinition('http_kernel'); + $arguments = $definition->getArguments(); + $arguments[0] = new Reference('debug.event_dispatcher'); + $arguments[2] = new Reference('debug.controller_resolver'); + $definition->setArguments($arguments); + } + + $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_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->registerSessionConfiguration($config['session'], $container, $loader); + } + + if ($this->isConfigEnabled($container, $config['form'])) { + $this->registerFormConfiguration($config, $container, $loader); + $config['validation']['enabled'] = true; + } + + 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->registerFragmentsConfiguration($config['fragments'], $container, $loader); + $this->registerProfilerConfiguration($config['profiler'], $container, $loader); + $this->registerTranslatorConfiguration($config['translator'], $container); + + if (isset($config['router'])) { + $this->registerRouterConfiguration($config['router'], $container, $loader); + } + + $this->registerAnnotationsConfiguration($config['annotations'], $container, $loader); + + if (isset($config['serializer']) && $config['serializer']['enabled']) { + $loader->load('serializer.xml'); + } + + $this->addClassesToCompile(array( + 'Symfony\\Component\\Config\\FileLocator', + + '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', + )); + } + + /** + * 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 ($this->isConfigEnabled($container, $config['csrf_protection'])) { + if (!isset($config['session'])) { + throw new \LogicException('CSRF protection needs that sessions are enabled.'); + } + if (!isset($config['secret'])) { + throw new \LogicException('CSRF protection needs a secret to be set.'); + } + $loader->load('form_csrf.xml'); + + $container->setParameter('form.type_extension.csrf.enabled', true); + $container->setParameter('form.type_extension.csrf.field_name', $config['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 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'); + + $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 + $supported = array( + 'sqlite' => 'Symfony\Component\HttpKernel\Profiler\SqliteProfilerStorage', + 'mysql' => 'Symfony\Component\HttpKernel\Profiler\MysqlProfilerStorage', + 'file' => 'Symfony\Component\HttpKernel\Profiler\FileProfilerStorage', + 'mongodb' => 'Symfony\Component\HttpKernel\Profiler\MongoDbProfilerStorage', + 'memcache' => 'Symfony\Component\HttpKernel\Profiler\MemcacheProfilerStorage', + 'memcached' => 'Symfony\Component\HttpKernel\Profiler\MemcachedProfilerStorage', + 'redis' => 'Symfony\Component\HttpKernel\Profiler\RedisProfilerStorage', + ); + list($class, ) = explode(':', $config['dsn'], 2); + if (!isset($supported[$class])) { + throw new \LogicException(sprintf('Driver "%s" is not supported for the profiler.', $class)); + } + + $container->setParameter('profiler.storage.dsn', $config['dsn']); + $container->setParameter('profiler.storage.username', $config['username']); + $container->setParameter('profiler.storage.password', $config['password']); + $container->setParameter('profiler.storage.lifetime', $config['lifetime']); + + $container->getDefinition('profiler.storage')->setClass($supported[$class]); + + 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'])) { + $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']['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', '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 { + $container->setAlias('session.handler', $config['handler_id']); + } + + $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(), + )); + } + } + + /** + * 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'); + $loader->load('templating_php.xml'); + + $links = array( + 'textmate' => 'txmt://open?url=file://%%f&line=%%l', + 'macvim' => 'mvim://open?url=file://%%f&line=%%l', + ); + + $container->setParameter('templating.helper.code.file_link_format', isset($links[$ide]) ? $links[$ide] : $ide); + $container->setParameter('templating.helper.form.resources', $config['form']['resources']); + $container->setParameter('fragment.renderer.hinclude.global_template', $config['hinclude_default_template']); + + 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'); + } + + // create package definitions and add them to the assets helper + $defaultPackage = $this->createPackageDefinition($container, $config['assets_base_urls']['http'], $config['assets_base_urls']['ssl'], $config['assets_version'], $config['assets_version_format']); + $container->setDefinition('templating.asset.default_package', $defaultPackage); + $namedPackages = array(); + foreach ($config['packages'] as $name => $package) { + $namedPackage = $this->createPackageDefinition($container, $package['base_urls']['http'], $package['base_urls']['ssl'], $package['version'], $package['version_format'], $name); + $container->setDefinition('templating.asset.package.'.$name, $namedPackage); + $namedPackages[$name] = new Reference('templating.asset.package.'.$name); + } + $container->getDefinition('templating.helper.assets')->setArguments(array( + new Reference('templating.asset.default_package'), + $namedPackages, + )); + + // Apply request scope to assets helper if one or more packages are request-scoped + $requireRequestScope = array_reduce( + $namedPackages, + function($v, Reference $ref) use ($container) { + return $v || 'request' === $container->getDefinition($ref)->getScope(); + }, + 'request' === $defaultPackage->getScope() + ); + + if ($requireRequestScope) { + $container->getDefinition('templating.helper.assets')->setScope('request'); + } + + 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(), + )); + + if (in_array('php', $config['engines'], true)) { + $this->addClassesToCompile(array( + 'Symfony\\Component\\Templating\\Storage\\FileStorage', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader', + )); + } + + $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'); + } + } + + /** + * Returns a definition for an asset package. + */ + private function createPackageDefinition(ContainerBuilder $container, array $httpUrls, array $sslUrls, $version, $format, $name = null) + { + if (!$httpUrls) { + $package = new DefinitionDecorator('templating.asset.path_package'); + $package + ->setPublic(false) + ->setScope('request') + ->replaceArgument(1, $version) + ->replaceArgument(2, $format) + ; + + return $package; + } + + if ($httpUrls == $sslUrls) { + $package = new DefinitionDecorator('templating.asset.url_package'); + $package + ->setPublic(false) + ->replaceArgument(0, $sslUrls) + ->replaceArgument(1, $version) + ->replaceArgument(2, $format) + ; + + return $package; + } + + $prefix = $name ? 'templating.asset.package.'.$name : 'templating.asset.default_package'; + + $httpPackage = new DefinitionDecorator('templating.asset.url_package'); + $httpPackage + ->replaceArgument(0, $httpUrls) + ->replaceArgument(1, $version) + ->replaceArgument(2, $format) + ; + $container->setDefinition($prefix.'.http', $httpPackage); + + if ($sslUrls) { + $sslPackage = new DefinitionDecorator('templating.asset.url_package'); + $sslPackage + ->replaceArgument(0, $sslUrls) + ->replaceArgument(1, $version) + ->replaceArgument(2, $format) + ; + } else { + $sslPackage = new DefinitionDecorator('templating.asset.path_package'); + $sslPackage + ->setScope('request') + ->replaceArgument(1, $version) + ->replaceArgument(2, $format) + ; + } + $container->setDefinition($prefix.'.ssl', $sslPackage); + + $package = new DefinitionDecorator('templating.asset.request_aware_package'); + $package + ->setPublic(false) + ->setScope('request') + ->replaceArgument(1, $prefix.'.http') + ->replaceArgument(2, $prefix.'.ssl') + ; + + return $package; + } + + /** + * 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; + } + + // Use the "real" translator instead of the identity default + $container->setAlias('translator', 'translator.default'); + $translator = $container->findDefinition('translator.default'); + if (!is_array($config['fallback'])) { + $config['fallback'] = array($config['fallback']); + } + $translator->addMethodCall('setFallbackLocales', array($config['fallback'])); + + // Discover translation directories + $dirs = array(); + if (class_exists('Symfony\Component\Validator\Validator')) { + $r = new \ReflectionClass('Symfony\Component\Validator\Validator'); + + $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; + } + } + 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)); + } + $finder = Finder::create() + ->files() + ->filter(function (\SplFileInfo $file) { + return 2 === substr_count($file->getBasename(), '.') && preg_match('/\.\w+$/', $file->getBasename()); + }) + ->in($dirs) + ; + + foreach ($finder as $file) { + // filename is domain.locale.format + list($domain, $locale, $format) = explode('.', $file->getBasename(), 3); + $translator->addMethodCall('addResource', array($format, (string) $file, $locale, $domain)); + } + } + } + + /** + * 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'); + + $container->setParameter('validator.translation_domain', $config['translation_domain']); + $container->setParameter('validator.mapping.loader.xml_files_loader.mapping_files', $this->getValidatorXmlMappingFiles($container)); + $container->setParameter('validator.mapping.loader.yaml_files_loader.mapping_files', $this->getValidatorYamlMappingFiles($container)); + + if (array_key_exists('enable_annotations', $config) && $config['enable_annotations']) { + $loaderChain = $container->getDefinition('validator.mapping.loader.loader_chain'); + $arguments = $loaderChain->getArguments(); + array_unshift($arguments[0], new Reference('validator.mapping.loader.annotation_loader')); + $loaderChain->setArguments($arguments); + } + + if (isset($config['cache'])) { + $container->getDefinition('validator.mapping.class_metadata_factory') + ->replaceArgument(1, new Reference('validator.mapping.cache.'.$config['cache'])); + $container->setParameter( + 'validator.mapping.cache.prefix', + 'validator_'.md5($container->getParameter('kernel.root_dir')) + ); + } + } + + private function getValidatorXmlMappingFiles(ContainerBuilder $container) + { + $reflClass = new \ReflectionClass('Symfony\Component\Form\FormInterface'); + $files = array(dirname($reflClass->getFileName()).'/Resources/config/validation.xml'); + $container->addResource(new FileResource($files[0])); + + foreach ($container->getParameter('kernel.bundles') as $bundle) { + $reflection = new \ReflectionClass($bundle); + if (is_file($file = dirname($reflection->getFilename()).'/Resources/config/validation.xml')) { + $files[] = realpath($file); + $container->addResource(new FileResource($file)); + } + } + + return $files; + } + + private function getValidatorYamlMappingFiles(ContainerBuilder $container) + { + $files = array(); + + foreach ($container->getParameter('kernel.bundles') as $bundle) { + $reflection = new \ReflectionClass($bundle); + if (is_file($file = dirname($reflection->getFilename()).'/Resources/config/validation.yml')) { + $files[] = realpath($file); + $container->addResource(new FileResource($file)); + } + } + + return $files; + } + + private function registerAnnotationsConfiguration(array $config, ContainerBuilder $container,$loader) + { + $loader->load('annotations.xml'); + + if ('file' === $config['cache']) { + $cacheDir = $container->getParameterBag()->resolveValue($config['file_cache_dir']); + if (!is_dir($cacheDir) && false === @mkdir($cacheDir, 0777, true)) { + throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir)); + } + + $container + ->getDefinition('annotations.file_cache_reader') + ->replaceArgument(1, $cacheDir) + ->replaceArgument(2, $config['debug']) + ; + $container->setAlias('annotation_reader', 'annotations.file_cache_reader'); + } elseif ('none' !== $config['cache']) { + $container + ->getDefinition('annotations.cached_reader') + ->replaceArgument(1, new Reference($config['cache'])) + ->replaceArgument(2, $config['debug']) + ; + $container->setAlias('annotation_reader', 'annotations.cached_reader'); + } + } + + /** + * 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..7b5ce51 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.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\EventListener; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +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 + */ +class SessionListener implements EventSubscriberInterface +{ + /** + * @var ContainerInterface + */ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function onKernelRequest(GetResponseEvent $event) + { + if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + return; + } + + $request = $event->getRequest(); + if (!$this->container->has('session') || $request->hasSession()) { + return; + } + + $request->setSession($this->container->get('session')); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array('onKernelRequest', 128), + ); + } +} 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..d0360c3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.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\EventListener; + +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * TestSessionListener. + * + * Saves session in test environment. + * + * @author Bulat Shakirzyanov + * @author Fabien Potencier + */ +class TestSessionListener implements EventSubscriberInterface +{ + protected $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function onKernelRequest(GetResponseEvent $event) + { + if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + return; + } + + // bootstrap the session + if (!$this->container->has('session')) { + return; + } + + $session = $this->container->get('session'); + $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 (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + 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), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Fragment/ContainerAwareHIncludeFragmentRenderer.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Fragment/ContainerAwareHIncludeFragmentRenderer.php new file mode 100644 index 0000000..698d979 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Fragment/ContainerAwareHIncludeFragmentRenderer.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\Fragment; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\UriSigner; +use Symfony\Component\HttpKernel\Fragment\HIncludeFragmentRenderer; + +/** + * Implements the Hinclude rendering strategy. + * + * @author Fabien Potencier + */ +class ContainerAwareHIncludeFragmentRenderer extends HIncludeFragmentRenderer +{ + private $container; + + /** + * {@inheritdoc} + */ + public function __construct(ContainerInterface $container, UriSigner $signer = null, $globalDefaultTemplate = null) + { + $this->container = $container; + + parent::__construct(null, $signer, $globalDefaultTemplate, $container->getParameter('kernel.charset')); + } + + /** + * {@inheritdoc} + */ + public function render($uri, Request $request, array $options = array()) + { + // setting the templating cannot be done in the constructor + // as it would lead to an infinite recursion in the service container + if (!$this->hasTemplating()) { + $this->setTemplating($this->container->get('templating')); + } + + return parent::render($uri, $request, $options); + } +} 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..8a04ed9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.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; + +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass; +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\AddCacheWarmerPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheClearerPass; +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\FragmentRendererPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\Scope; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class FrameworkBundle extends Bundle +{ + public function boot() + { + if ($trustedProxies = $this->container->getParameter('kernel.trusted_proxies')) { + Request::setTrustedProxies($trustedProxies); + } + + if ($this->container->getParameter('kernel.http_method_override')) { + Request::enableHttpMethodParameterOverride(); + } + } + + public function build(ContainerBuilder $container) + { + parent::build($container); + + // we need to add the request scope as early as possible so that + // the compilation can find scope widening issues + $container->addScope(new Scope('request')); + + $container->addCompilerPass(new RoutingResolverPass()); + $container->addCompilerPass(new ProfilerPass()); + $container->addCompilerPass(new RegisterListenersPass(), PassConfig::TYPE_AFTER_REMOVING); + $container->addCompilerPass(new TemplatingPass()); + $container->addCompilerPass(new AddConstraintValidatorsPass()); + $container->addCompilerPass(new AddValidatorInitializersPass()); + $container->addCompilerPass(new FormPass()); + $container->addCompilerPass(new TranslatorPass()); + $container->addCompilerPass(new AddCacheWarmerPass()); + $container->addCompilerPass(new AddCacheClearerPass()); + $container->addCompilerPass(new TranslationExtractorPass()); + $container->addCompilerPass(new TranslationDumperPass()); + $container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING); + $container->addCompilerPass(new SerializerPass()); + + if ($container->getParameter('kernel.debug')) { + $container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING); + $container->addCompilerPass(new CompilerDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING); + } + } +} 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..f07c994 --- /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->createEsi(), 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 Boolean $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('esi', $this->getEsi()); + + 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 createEsi() + { + 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/LICENSE b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/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..1c0c312 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml @@ -0,0 +1,30 @@ + + + + + + Doctrine\Common\Annotations\AnnotationReader + Doctrine\Common\Annotations\CachedReader + Doctrine\Common\Annotations\FileCacheReader + + + + + + + + + + + + + + + + + + + + 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..0e07cdb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml @@ -0,0 +1,57 @@ + + + + + + Symfony\Component\HttpKernel\DataCollector\ConfigDataCollector + Symfony\Component\HttpKernel\DataCollector\RequestDataCollector + Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector + Symfony\Component\HttpKernel\DataCollector\EventDataCollector + Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector + Symfony\Component\HttpKernel\DataCollector\TimeDataCollector + Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector + Symfony\Bundle\FrameworkBundle\DataCollector\RouterDataCollector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..e7d1c3c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml @@ -0,0 +1,37 @@ + + + + + + Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher + Symfony\Component\Stopwatch\Stopwatch + %kernel.cache_dir%/%kernel.container_class%.xml + Symfony\Component\HttpKernel\Controller\TraceableControllerResolver + + + + + + + + + + + + + + + + + + + + + + deprecation + + + + 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..36872ad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml @@ -0,0 +1,19 @@ + + + + + + Symfony\Component\HttpKernel\EventListener\ErrorsLoggerListener + + + + + + + emergency + + + + 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..dd8e801 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml @@ -0,0 +1,28 @@ + + + + + + Symfony\Component\HttpKernel\HttpCache\Esi + Symfony\Component\HttpKernel\EventListener\EsiListener + Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer + + + + + + + + + + + + + + + %fragment.path% + + + 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..569100f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -0,0 +1,170 @@ + + + + + + Symfony\Component\Form\ResolvedFormTypeFactory + Symfony\Component\Form\FormRegistry + Symfony\Component\Form\FormFactory + Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension + Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser + Symfony\Component\PropertyAccess\PropertyAccessor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..57cad20 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml @@ -0,0 +1,26 @@ + + + + + + Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider + + + + + + %kernel.secret% + + + + + + %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/fragment_listener.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_listener.xml new file mode 100644 index 0000000..930ca17 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_listener.xml @@ -0,0 +1,18 @@ + + + + + + Symfony\Component\HttpKernel\EventListener\FragmentListener + + + + + + + %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..595db6d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml @@ -0,0 +1,37 @@ + + + + + + Symfony\Component\HttpKernel\Fragment\FragmentHandler + Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer + Symfony\Bundle\FrameworkBundle\Fragment\ContainerAwareHIncludeFragmentRenderer + + /_fragment + + + + + + %kernel.debug% + + + + + + + + %fragment.path% + + + + + + + %fragment.renderer.hinclude.global_template% + %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..a866660 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml @@ -0,0 +1,34 @@ + + + + + + Symfony\Component\HttpKernel\Profiler\Profiler + Symfony\Component\HttpKernel\EventListener\ProfilerListener + + + + + + + + + + + %profiler.storage.dsn% + %profiler.storage.username% + %profiler.storage.password% + %profiler.storage.lifetime% + + + + + + + %profiler_listener.only_exceptions% + %profiler_listener.only_master_requests% + + + 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..64c90b4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php @@ -0,0 +1,30 @@ + + * + * 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['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'app_dev.php'; + +require 'app_dev.php'; 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..4278b4b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_prod.php @@ -0,0 +1,30 @@ + + * + * 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['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'app.php'; + +require 'app.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..9e21db4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -0,0 +1,100 @@ + + + + + + Symfony\Bundle\FrameworkBundle\Routing\Router + Symfony\Component\Routing\RequestContext + Symfony\Bundle\FrameworkBundle\Routing\DelegatingLoader + Symfony\Component\Config\Loader\LoaderResolver + Symfony\Component\Routing\Loader\XmlFileLoader + Symfony\Component\Routing\Loader\YamlFileLoader + Symfony\Component\Routing\Loader\PhpFileLoader + 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 + Symfony\Bundle\FrameworkBundle\CacheWarmer\RouterCacheWarmer + %router.cache_class_prefix%UrlMatcher + %router.cache_class_prefix%UrlGenerator + Symfony\Component\HttpKernel\EventListener\RouterListener + 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..4b23505 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..491ccbc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml @@ -0,0 +1,26 @@ + + + + + + Symfony\Component\Serializer\Serializer + Symfony\Component\Serializer\Encoder\XmlEncoder + Symfony\Component\Serializer\Encoder\JsonEncoder + + + + + + + + + + + + + + + + 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..674e28f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml @@ -0,0 +1,60 @@ + + + + + + Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher + Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel + Symfony\Component\Filesystem\Filesystem + Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate + Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer + Symfony\Component\HttpKernel\Config\FileLocator + Symfony\Component\HttpKernel\UriSigner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %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..de45365 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml @@ -0,0 +1,54 @@ + + + + + + Symfony\Component\HttpFoundation\Session\Session + Symfony\Component\HttpFoundation\Session\Flash\FlashBag + Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag + Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage + Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage + Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage + Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler + Symfony\Bundle\FrameworkBundle\EventListener\SessionListener + + + + + + + + + + + %session.storage.options% + + + + + + + + + + + + + %kernel.cache_dir%/sessions + + + + %session.save_path% + + + + + + + + + + + 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..b06c9af --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml @@ -0,0 +1,64 @@ + + + + + + Symfony\Bundle\FrameworkBundle\Templating\DelegatingEngine + Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser + Symfony\Bundle\FrameworkBundle\Templating\TemplateFilenameParser + Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplatePathsCacheWarmer + Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator + Symfony\Bundle\FrameworkBundle\Templating\Loader\FilesystemLoader + Symfony\Component\Templating\Loader\CacheLoader + Symfony\Component\Templating\Loader\ChainLoader + Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinder + + + + + + + + + + + + + + + + + %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..23da774 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_debug.xml @@ -0,0 +1,25 @@ + + + + + + Symfony\Bundle\FrameworkBundle\Templating\Debugger + Symfony\Bundle\FrameworkBundle\Templating\TimedPhpEngine + + + + + + + + + + + + + + + + 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..9aadc81 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_php.xml @@ -0,0 +1,118 @@ + + + + + + Symfony\Bundle\FrameworkBundle\Templating\PhpEngine + Symfony\Component\Templating\Helper\SlotsHelper + Symfony\Component\Templating\Helper\CoreAssetsHelper + Symfony\Bundle\FrameworkBundle\Templating\Helper\ActionsHelper + Symfony\Bundle\FrameworkBundle\Templating\Helper\RouterHelper + Symfony\Bundle\FrameworkBundle\Templating\Helper\RequestHelper + Symfony\Bundle\FrameworkBundle\Templating\Helper\SessionHelper + Symfony\Bundle\FrameworkBundle\Templating\Helper\CodeHelper + Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper + Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper + Symfony\Component\Form\Extension\Templating\TemplatingRendererEngine + Symfony\Component\Form\FormRenderer + Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables + Symfony\Bundle\FrameworkBundle\Templating\Asset\PathPackage + Symfony\Component\Templating\Asset\UrlPackage + Symfony\Bundle\FrameworkBundle\Templating\Asset\PackageFactory + + + + + + + + + %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..4e609a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml @@ -0,0 +1,32 @@ + + + + + + Symfony\Bundle\FrameworkBundle\Client + + Symfony\Component\BrowserKit\History + Symfony\Component\BrowserKit\CookieJar + Symfony\Bundle\FrameworkBundle\EventListener\TestSessionListener + + + + + + %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..48df235 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml @@ -0,0 +1,139 @@ + + + + + + Symfony\Bundle\FrameworkBundle\Translation\Translator + Symfony\Component\Translation\IdentityTranslator + Symfony\Component\Translation\MessageSelector + Symfony\Component\Translation\Loader\PhpFileLoader + Symfony\Component\Translation\Loader\YamlFileLoader + Symfony\Component\Translation\Loader\XliffFileLoader + Symfony\Component\Translation\Loader\PoFileLoader + Symfony\Component\Translation\Loader\MoFileLoader + Symfony\Component\Translation\Loader\QtFileLoader + Symfony\Component\Translation\Loader\CsvFileLoader + Symfony\Component\Translation\Loader\IcuResFileLoader + Symfony\Component\Translation\Loader\IcuDatFileLoader + Symfony\Component\Translation\Loader\IniFileLoader + Symfony\Component\Translation\Dumper\PhpFileDumper + Symfony\Component\Translation\Dumper\XliffFileDumper + Symfony\Component\Translation\Dumper\PoFileDumper + Symfony\Component\Translation\Dumper\MoFileDumper + Symfony\Component\Translation\Dumper\YamlFileDumper + Symfony\Component\Translation\Dumper\QtFileDumper + Symfony\Component\Translation\Dumper\CsvFileDumper + Symfony\Component\Translation\Dumper\IniFileDumper + Symfony\Component\Translation\Dumper\IcuResFileDumper + Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor + Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader + Symfony\Component\Translation\Extractor\ChainExtractor + Symfony\Component\Translation\Writer\TranslationWriter + + + + + + + + + %kernel.cache_dir%/translations + %kernel.debug% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..0bc7004 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml @@ -0,0 +1,67 @@ + + + + + + Symfony\Component\Validator\Validator + Symfony\Component\Validator\Mapping\ClassMetadataFactory + Symfony\Component\Validator\Mapping\Cache\ApcCache + + Symfony\Component\Validator\Mapping\Loader\LoaderChain + Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader + Symfony\Component\Validator\Mapping\Loader\AnnotationLoader + Symfony\Component\Validator\Mapping\Loader\XmlFilesLoader + Symfony\Component\Validator\Mapping\Loader\YamlFilesLoader + Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory + + + + + + + + + + %validator.translation_domain% + + + + + + + %validator.mapping.cache.prefix% + + + + + + + + + + + + + + + + + + + + + + + %validator.mapping.loader.xml_files_loader.mapping_files% + + + + %validator.mapping.loader.yaml_files_loader.mapping_files% + + + 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..177821a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml @@ -0,0 +1,44 @@ + + + + + + Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver + Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser + Symfony\Component\HttpKernel\EventListener\ResponseListener + Symfony\Component\HttpKernel\EventListener\StreamedResponseListener + Symfony\Component\HttpKernel\EventListener\LocaleListener + + + + + + + + + + + + + + + + + + %kernel.charset% + + + + + + + + + %kernel.default_locale% + + + + + 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..c37fdce --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/body.css @@ -0,0 +1,150 @@ +/* +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 html{color:#000;background:#FFF;}.sf-reset body,.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 html, +.sf-reset body { + width: 100%; + min-height: 100%; + _height: 100%; + margin: 0; + padding: 0; +} +.sf-reset body { + font: 1em "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + text-align: left; + background-color: #efefef; +} +.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; +} +.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..00b948f --- /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; +} +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..63d16bd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php @@ -0,0 +1,6 @@ +id="escape($id) ?>" +name="escape($full_name) ?>" +disabled="disabled" + $v): ?> + 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..64d4466 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php @@ -0,0 +1,2 @@ +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..d3cf747 --- /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') ?> + 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..73234b4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_collapsed.html.php @@ -0,0 +1,13 @@ + 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..64ae7db --- /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) ?> + +
    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..a7a9311 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php @@ -0,0 +1,11 @@ + + + $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..da9bec4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php @@ -0,0 +1,7 @@ + +
      + +
    • 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..4ed04cc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php @@ -0,0 +1,6 @@ + + + +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..9c3af35 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_start.html.php @@ -0,0 +1,6 @@ + + +
    $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..45a8210 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_compound.html.php @@ -0,0 +1,11 @@ +
    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..11c2283 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_simple.html.php @@ -0,0 +1,5 @@ +value="escape($value) ?>" + block($form, 'widget_attributes') ?> +/> 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..68eec54 --- /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..3775a71 --- /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..854952f --- /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..845fcb2 --- /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..35b9b04 --- /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/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..d91267f --- /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..c2ab28a --- /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..210b84c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php @@ -0,0 +1,10 @@ +id="escape($id) ?>" +name="escape($full_name) ?>" +readonly="readonly" +disabled="disabled" +required="required" +maxlength="escape($max_length) ?>" +pattern="escape($pattern) ?>" + $v): ?> + escape($k), $view->escape(in_array($v, array('placeholder', 'title')) ? $view['translator']->trans($v, array(), $translation_domain) : $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..2a8e979 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php @@ -0,0 +1,2 @@ +id="escape($id) ?>" + $v) { printf('%s="%s" ', $view->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..a4878e5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget_compound.html.php @@ -0,0 +1,7 @@ +block($form, 'widget_container_attributes') ?>> + parent): ?> + errors($form) ?> + + block($form, 'form_rows') ?> + rest($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..e0ec390 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.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\Routing; + +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Component\Config\Loader\DelegatingLoader as BaseDelegatingLoader; +use Symfony\Component\Config\Loader\LoaderResolverInterface; +use Psr\Log\LoggerInterface; + +/** + * 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; + protected $logger; + + /** + * Constructor. + * + * @param ControllerNameParser $parser A ControllerNameParser instance + * @param LoggerInterface $logger A LoggerInterface instance + * @param LoaderResolverInterface $resolver A LoaderResolverInterface instance + */ + public function __construct(ControllerNameParser $parser, LoggerInterface $logger = null, LoaderResolverInterface $resolver) + { + $this->parser = $parser; + $this->logger = $logger; + + parent::__construct($resolver); + } + + /** + * Loads a resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return RouteCollection A RouteCollection instance + */ + public function load($resource, $type = null) + { + $collection = parent::load($resource, $type); + + 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..ffbb648 --- /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..5777479 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Routing/Router.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\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 = null === $context ? new RequestContext() : $context; + $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 pattern. + * - the route host. + * + * @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())); + } + } + + /** + * 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. Array 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 '%%'; + } + + $key = strtolower($match[1]); + + if (!$container->hasParameter($key)) { + throw new ParameterNotFoundException($key); + } + + $resolved = $container->getParameter($key); + + if (is_string($resolved) || is_numeric($resolved)) { + return (string) $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) + ); + + }, $value); + + return str_replace('%%', '%', $escapedValue); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Asset/PackageFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Asset/PackageFactory.php new file mode 100644 index 0000000..de36a32 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Asset/PackageFactory.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\FrameworkBundle\Templating\Asset; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Creates packages based on whether the current request is secure. + * + * @author Kris Wallsmith + */ +class PackageFactory +{ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Returns either the HTTP or SSL version of an asset package. + * + * @param Request $request The current request + * @param string $httpId The id for the package to use when the current request is HTTP + * @param string $sslId The id for the package to use when the current request is SSL + * + * @return PackageInterface The package + */ + public function getPackage(Request $request, $httpId, $sslId) + { + return $this->container->get($request->isSecure() ? $sslId : $httpId); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Asset/PathPackage.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Asset/PathPackage.php new file mode 100644 index 0000000..6aa8c58 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Asset/PathPackage.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\Templating\Asset; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Templating\Asset\PathPackage as BasePathPackage; + +/** + * The path packages adds a version and a base path to asset URLs. + * + * @author Kris Wallsmith + */ +class PathPackage extends BasePathPackage +{ + /** + * Constructor. + * + * @param Request $request The current request + * @param string $version The version + * @param string $format The version format + */ + public function __construct(Request $request, $version = null, $format = null) + { + parent::__construct($request->getBasePath(), $version, $format); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Debugger.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Debugger.php new file mode 100644 index 0000000..19a5938 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Debugger.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\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\Templating\DebuggerInterface; +use Psr\Log\LoggerInterface; + +/** + * Binds the Symfony templating loader debugger to the Symfony logger. + * + * @author Fabien Potencier + */ +class Debugger implements DebuggerInterface +{ + protected $logger; + + /** + * Constructor. + * + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + } + + /** + * Logs a message. + * + * @param string $message A message to log + */ + public function log($message) + { + if (null !== $this->logger) { + $this->logger->debug($message); + } + } +} 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..c5f1a3e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.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\Templating; + +use Symfony\Component\Templating\DelegatingEngine as BaseDelegatingEngine; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; +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 supports($name) + { + foreach ($this->engines as $i => $engine) { + if (is_string($engine)) { + $engine = $this->engines[$i] = $this->container->get($engine); + } + + if ($engine->supports($name)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function getEngine($name) + { + foreach ($this->engines as $i => $engine) { + if (is_string($engine)) { + $engine = $this->engines[$i] = $this->container->get($engine); + } + + if ($engine->supports($name)) { + return $engine; + } + } + + throw new \RuntimeException(sprintf('No engine is able to work with the template "%s".', $name)); + } + + /** + * 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 + */ + public function renderResponse($view, array $parameters = array(), Response $response = null) + { + return $this->getEngine($view)->renderResponse($view, $parameters, $response); + } +} 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..edc087e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/EngineInterface.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\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 + */ + 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..af3b34a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.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\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\Security\Core\SecurityContext; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * GlobalVariables is the entry point for Symfony global variables in Twig templates. + * + * @author Fabien Potencier + */ +class GlobalVariables +{ + protected $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Returns the security context service. + * + * @return SecurityContext|null The security context + */ + public function getSecurity() + { + if ($this->container->has('security.context')) { + return $this->container->get('security.context'); + } + } + + /** + * Returns the current user. + * + * @return mixed|void + * + * @see TokenInterface::getUser() + */ + public function getUser() + { + if (!$security = $this->getSecurity()) { + return; + } + + if (!$token = $security->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') && $request = $this->container->get('request')) { + return $request; + } + } + + /** + * 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 Boolean The current debug mode + */ + public function getDebug() + { + return (Boolean) $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..253d348 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/ActionsHelper.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\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 Symfony\Component\HttpKernel\Fragment\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); + } + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + 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..3c031d2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php @@ -0,0 +1,231 @@ + + * + * 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; + +if (!defined('ENT_SUBSTITUTE')) { + define('ENT_SUBSTITUTE', 8); +} + +/** + * 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 = empty($fileLinkFormat) ? ini_get('xdebug.file_link_format') : $fileLinkFormat; + $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 hightlight_file cannot parse these files + if ('application/octet-stream' === $finfo->file($file, FILEINFO_MIME_TYPE)) { + return; + } + } + + $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 integer $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) + { + if (null === $text) { + $file = trim($file); + $fileStr = $file; + if (0 === strpos($fileStr, $this->rootDir)) { + $fileStr = str_replace($this->rootDir, '', str_replace('\\', '/', $fileStr)); + $fileStr = sprintf('kernel.root_dir/%s', $this->rootDir, $fileStr); + } + + $text = "$fileStr at line $line"; + } + + if (false !== $link = $this->getFileLink($file, $line)) { + return sprintf('%s', htmlspecialchars($link, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset), $text); + } + + return $text; + } + + /** + * Returns the link for a given file/line pair. + * + * @param string $file An absolute file path + * @param integer $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) + { + $that = $this; + + return preg_replace_callback('/in ("|")?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', function ($match) use ($that) { + return 'in '.$that->formatFile($match[2], $match[3]); + }, $text); + } + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + 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..29220af --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.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\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 enctype in the form tag, if necessary. + * + * Example usage templates: + * + * enctype($form) ?>> + * + * @param FormView $view The view for which to render the encoding type + * + * @return string The HTML markup + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link start} instead. + */ + public function enctype(FormView $view) + { + // Uncomment this as soon as the deprecation note should be shown + // trigger_error('The form helper $view[\'form\']->enctype() is deprecated since version 2.3 and will be removed in 3.0. Use $view[\'form\']->start() instead.', E_USER_DEPRECATED); + + return $this->renderer->searchAndRenderBlock($view, 'enctype'); + } + + /** + * 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 intention. + * + * + * $csrfProvider = $this->get('form.csrf_provider'); + * if (!$csrfProvider->isCsrfTokenValid('rm_user_'.$user->getId(), $token)) { + * throw new \RuntimeException('CSRF attack detected.'); + * } + * + * + * @param string $intention The intention of the protected action + * + * @return string A CSRF token + * + * @throws \BadMethodCallException When no CSRF provider was injected in the constructor. + */ + public function csrfToken($intention) + { + return $this->renderer->renderCsrfToken($intention); + } + + 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..4533bf1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RequestHelper.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\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\HttpFoundation\Request; + +/** + * RequestHelper provides access to the current request parameters. + * + * @author Fabien Potencier + */ +class RequestHelper extends Helper +{ + protected $request; + + /** + * Constructor. + * + * @param Request $request A Request instance + */ + public function __construct(Request $request) + { + $this->request = $request; + } + + /** + * 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 Symfony\Component\HttpFoundation\Request::get() + */ + public function getParameter($key, $default = null) + { + return $this->request->get($key, $default); + } + + /** + * Returns the locale + * + * @return string + */ + public function getLocale() + { + return $this->request->getLocale(); + } + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + 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..845b75d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RouterHelper.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\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 from the given parameters. + * + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param Boolean|string $referenceType The type of reference (one of the constants in UrlGeneratorInterface) + * + * @return string The generated URL + * + * @see UrlGeneratorInterface + */ + public function generate($name, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) + { + return $this->generator->generate($name, $parameters, $referenceType); + } + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + 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..aac3f6d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.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\Templating\Helper; + +use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\HttpFoundation\Request; + +/** + * SessionHelper provides read-only access to the session attributes. + * + * @author Fabien Potencier + */ +class SessionHelper extends Helper +{ + protected $session; + + /** + * Constructor. + * + * @param Request $request A Request instance + */ + public function __construct(Request $request) + { + $this->session = $request->getSession(); + } + + /** + * 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->session->get($name, $default); + } + + public function getFlash($name, array $default = array()) + { + return $this->session->getFlashBag()->get($name, $default); + } + + public function getFlashes() + { + return $this->session->getFlashBag()->all(); + } + + public function hasFlash($name) + { + return $this->session->getFlashBag()->has($name); + } + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + public function getName() + { + return 'session'; + } +} 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..30816d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/TranslatorHelper.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\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); + } + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + 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..ac917e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/FilesystemLoader.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\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; + } + + /** + * Loads a template. + * + * @param TemplateReferenceInterface $template A template + * + * @return FileStorage|Boolean false if the template cannot be loaded, a Storage instance otherwise + */ + public function load(TemplateReferenceInterface $template) + { + try { + $file = $this->locator->locate($template); + } catch (\InvalidArgumentException $e) { + return false; + } + + return new FileStorage($file); + } + + /** + * Returns true if the template is still fresh. + * + * @param TemplateReferenceInterface $template The template name as an array + * @param integer $time The last modification time of the cached template (timestamp) + * + * @return Boolean + */ + 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..0ff6b8c --- /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 Boolean $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..a93098e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/PhpEngine.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\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); + } + } + + /** + * @throws \InvalidArgumentException When the helper is not defined + */ + 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; + } + + /** + * 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 + */ + 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..befb3e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateFilenameParser.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\Templating; + +use Symfony\Component\Templating\TemplateNameParserInterface; + +/** + * TemplateFilenameParser converts template filenames to + * TemplateReferenceInterface instances. + * + * @author Fabien Potencier + */ +class TemplateFilenameParser implements TemplateNameParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($file) + { + $parts = explode('/', strtr($file, '\\', '/')); + + $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..ef2ae08 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.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; + +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Templating\TemplateReferenceInterface; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * TemplateNameParser converts template names from the short notation + * "bundle:section:template.format.engine" to TemplateReferenceInterface + * instances. + * + * @author Fabien Potencier + */ +class TemplateNameParser implements TemplateNameParserInterface +{ + protected $kernel; + protected $cache; + + /** + * Constructor. + * + * @param KernelInterface $kernel A KernelInterface instance + */ + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + $this->cache = array(); + } + + /** + * {@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,}#', '/', strtr($name, '\\', '/'))); + + if (false !== strpos($name, '..')) { + throw new \RuntimeException(sprintf('Template name "%s" contains invalid characters.', $name)); + } + + if (!preg_match('/^([^:]*):([^:]*):(.+)\.([^\.]+)\.([^\.]+)$/', $name, $matches)) { + throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid (format is "bundle:section:template.format.engine").', $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..65a5779 --- /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..cdbdc6f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TimedPhpEngine.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; + +use Symfony\Bundle\FrameworkBundle\Templating\PhpEngine; +use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables; +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/WebTestCase.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php new file mode 100644 index 0000000..37cf41c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.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\Test; + +use Symfony\Bundle\FrameworkBundle\Client; +use Symfony\Component\Finder\Finder; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * WebTestCase is the base class for functional tests. + * + * @author Fabien Potencier + */ +abstract class WebTestCase extends \PHPUnit_Framework_TestCase +{ + protected static $class; + protected static $kernel; + + /** + * 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()) + { + if (null !== static::$kernel) { + static::$kernel->shutdown(); + } + + static::$kernel = static::createKernel($options); + static::$kernel->boot(); + + $client = static::$kernel->getContainer()->get('test.client'); + $client->setServerParameters($server); + + return $client; + } + + /** + * 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 WebTestCase::createKernel() method.'); + } + + $dir = static::getPhpUnitCliConfigArgument(); + if ($dir === null && + (is_file(getcwd().DIRECTORY_SEPARATOR.'phpunit.xml') || + is_file(getcwd().DIRECTORY_SEPARATOR.'phpunit.xml.dist'))) { + $dir = getcwd(); + } + + // Can't continue + if ($dir === null) { + 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() + { + $dir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_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 http://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; + } + + /** + * Creates a Kernel. + * + * Available options: + * + * * environment + * * debug + * + * @param array $options An array of options + * + * @return HttpKernelInterface A HttpKernelInterface 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 function tearDown() + { + if (null !== static::$kernel) { + static::$kernel->shutdown(); + } + } +} 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..f2e756a --- /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($kernel); + + $finder = new TemplateFinder($kernel, $parser, __DIR__.'/../Fixtures/Resources'); + + $templates = array_map( + function ($template) { return $template->getLogicalName(); }, + $finder->findAllTemplates() + ); + + $this->assertEquals(6, count($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('::this.is.a.template.format.engine', $templates); + $this->assertContains('::resource.format.engine', $templates); + } + +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TestCaseMethod.tpl b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TestCaseMethod.tpl new file mode 100644 index 0000000..5279001 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TestCaseMethod.tpl @@ -0,0 +1,47 @@ +collectRawCodeCoverageInformation({collectCodeCoverageInformation}); + + $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(TRUE); + + ob_end_clean(); + ob_start(); + $test->run($result); + $output = ob_get_clean(); + + print serialize( + array( + 'testResult' => $test->getResult(), + 'numAssertions' => $test->getNumAssertions(), + 'result' => $result, + 'output' => $output + ) + ); + + ob_start(); +} + +{globals} + +if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; + unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); +} + +{constants} + +__phpunit_run_isolated_test(); +ob_end_clean(); +?> 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..6ded6dc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.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\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; + +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()); + } + + 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->once()) + ->method('get') + ->with($this->equalTo('event_dispatcher')) + ->will($this->returnValue($dispatcher)) + ; + + $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/Controller/ControllerNameParserTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php new file mode 100644 index 0000000..b18ade8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.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\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(); + } + + public 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 + ); + } + + 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) { + 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/RedirectControllerTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php new file mode 100644 index 0000000..6bd636a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.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\Tests\Controller; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +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(); + + $returnResponse = $controller->redirectAction($request, '', true); + $this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $returnResponse); + $this->assertEquals(410, $returnResponse->getStatusCode()); + + $returnResponse = $controller->redirectAction($request, '', false); + $this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $returnResponse); + $this->assertEquals(404, $returnResponse->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', + ), + ); + + $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(); + + $returnResponse = $controller->urlRedirectAction($request, '', true); + $this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $returnResponse); + $this->assertEquals(410, $returnResponse->getStatusCode()); + + $returnResponse = $controller->urlRedirectAction($request, '', false); + $this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $returnResponse); + $this->assertEquals(404, $returnResponse->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); + } + + private function createRequestObject($scheme, $host, $port, $baseUrl) + { + $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)); + + 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..03eacc3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.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\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +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'); + + $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'); + + $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'); + + $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/FragmentRendererPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FragmentRendererPassTest.php new file mode 100644 index 0000000..ecc334d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FragmentRendererPassTest.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\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FragmentRendererPass; + +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(), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('stdClass')); + + $builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $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(), + ); + + $renderer = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $renderer + ->expects($this->once()) + ->method('addMethodCall') + ->with('addRenderer', array(new Reference('my_content_renderer'))) + ; + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\RendererService')); + + $builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $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 \Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface +{ + public function render($uri, Request $request = null, array $options = array()) + { + } + + public function getName() + { + return 'test'; + } +} 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..05c3ec1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.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\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass; + +class ProfilerPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * 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->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($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->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $container->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($services)); + + // fake the getDefinition() to return a Profiler definition + $definition = new Definition('ProfilerClass'); + $container->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + // 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 = $definition->getMethodCalls(); + $this->assertCount(1, $methodCalls); + $this->assertEquals('add', $methodCalls[0][0]); // grab the method part of the first call + } +} 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..a65d069 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerPassTest.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\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +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'); + + $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'); + + $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'); + + $container->expects($this->atLeastOnce()) + ->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..871acb2 --- /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\ContainerBuilder; +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'); + $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/ConfigurationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php new file mode 100644 index 0000000..55c9b55 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.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\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(), array(array('secret' => 's3cr3t'))); + + $this->assertEquals( + array_merge(array('secret' => 's3cr3t'), self::getBundleDefaultConfig()), + $config + ); + } + + /** + * @dataProvider getTestValidTrustedProxiesData + */ + public function testValidTrustedProxies($trustedProxies, $processedProxies) + { + $processor = new Processor(); + $configuration = new Configuration(array()); + $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(array()); + $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(array()); + $processor->processConfiguration($configuration, array( + array( + 'secret' => 's3cr3t', + 'trusted_proxies' => array('Not an IP address') + ) + )); + } + + protected static function getBundleDefaultConfig() + { + return array( + 'http_method_override' => true, + 'trusted_proxies' => array(), + 'ide' => null, + 'default_locale' => 'en', + 'form' => array('enabled' => false), + 'csrf_protection' => array( + 'enabled' => true, + 'field_name' => '_token', + ), + 'esi' => 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', + 'username' => '', + 'password' => '', + 'lifetime' => 86400, + 'collect' => true, + ), + 'translator' => array( + 'enabled' => false, + 'fallback' => 'en', + ), + 'validation' => array( + 'enabled' => false, + 'enable_annotations' => false, + 'translation_domain' => 'validators', + ), + 'annotations' => array( + 'cache' => 'file', + 'file_cache_dir' => '%kernel.cache_dir%/annotations', + 'debug' => '%kernel.debug%', + ), + 'serializer' => 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..e936bfd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.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 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/full.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php new file mode 100644 index 0000000..c4eff93 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php @@ -0,0 +1,74 @@ +loadFromExtension('framework', array( + 'secret' => 's3cr3t', + 'default_locale' => 'fr', + 'form' => null, + 'http_method_override' => false, + 'trusted_proxies' => array('127.0.0.1', '10.0.0.1'), + 'csrf_protection' => array( + 'enabled' => true, + 'field_name' => '_csrf', + ), + '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' => true, + 'gc_maxlifetime' => 90000, + 'gc_divisor' => 108, + 'gc_probability' => 1, + 'save_path' => '/path/to/sessions', + ), + 'templating' => array( + 'assets_version' => 'SomeVersionScheme', + 'assets_base_urls' => 'http://cdn.example.com', + 'cache' => '/path/to/cache', + 'engines' => array('php', 'twig'), + 'loader' => array('loader.foo', 'loader.bar'), + 'packages' => array( + 'images' => array( + 'version' => '1.0.0', + 'base_urls' => array('http://images1.example.com', 'http://images2.example.com'), + ), + 'foo' => array( + 'version' => '1.0.0', + ), + 'bar' => array( + 'base_urls' => array('http://bar1.example.com', 'http://bar2.example.com'), + ), + ), + 'form' => array( + 'resources' => array('theme1', 'theme2') + ), + ), + 'translator' => array( + 'enabled' => true, + 'fallback' => 'fr', + ), + 'validation' => array( + 'enabled' => true, + 'cache' => 'apc', + ), + 'annotations' => array( + 'cache' => 'file', + 'debug' => true, + 'file_cache_dir' => '%kernel.cache_dir%/annotations', + ), + 'ide' => 'file%%link%%format' +)); 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/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/templating_url_package.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/templating_url_package.php new file mode 100644 index 0000000..a5dda77 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/templating_url_package.php @@ -0,0 +1,14 @@ +loadFromExtension('framework', array( + 'secret' => 's3cr3t', + 'templating' => array( + 'assets_base_urls' => 'https://cdn.example.com', + 'engines' => array('php', 'twig'), + 'packages' => array( + 'images' => array( + 'base_urls' => 'https://images.example.com', + ), + ), + ), +)); 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..d0c1fda --- /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/xml/full.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml new file mode 100644 index 0000000..8fd3e9b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + loader.foo + loader.bar + php + twig + http://cdn.example.com + + http://images1.example.com + http://images2.example.com + + + + http://bar1.example.com + http://bar2.example.com + + + theme1 + theme2 + + + + + + + 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/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/templating_url_package.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/templating_url_package.xml new file mode 100644 index 0000000..0fd2039 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/templating_url_package.xml @@ -0,0 +1,19 @@ + + + + + + + php + twig + https://cdn.example.com + + https://images.example.com + + + + 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/yml/csrf.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/csrf.yml new file mode 100644 index 0000000..ce5fc59 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/csrf.yml @@ -0,0 +1,6 @@ +framework: + secret: s3cr3t + form: ~ + session: ~ + # CSRF should be enabled by default + # csrf_protection: ~ 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..d013063 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -0,0 +1,57 @@ +framework: + secret: s3cr3t + default_locale: fr + form: ~ + http_method_override: false + trusted_proxies: ['127.0.0.1', '10.0.0.1'] + csrf_protection: + enabled: true + field_name: _csrf + 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: true + gc_probability: 1 + gc_divisor: 108 + gc_maxlifetime: 90000 + save_path: /path/to/sessions + templating: + assets_version: SomeVersionScheme + assets_base_urls: http://cdn.example.com + engines: [php, twig] + loader: [loader.foo, loader.bar] + cache: /path/to/cache + packages: + images: + version: 1.0.0 + base_urls: ["http://images1.example.com", "http://images2.example.com"] + foo: + version: 1.0.0 + bar: + base_urls: ["http://images1.example.com", "http://images2.example.com"] + form: + resources: [theme1, theme2] + translator: + enabled: true + fallback: fr + validation: + enabled: true + cache: apc + annotations: + cache: file + debug: true + file_cache_dir: %kernel.cache_dir%/annotations + ide: file%%link%%format 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/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/templating_url_package.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/templating_url_package.yml new file mode 100644 index 0000000..bfec7a1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/templating_url_package.yml @@ -0,0 +1,8 @@ +framework: + secret: s3cr3t + templating: + assets_base_urls: https://cdn.example.com + engines: [php, twig] + packages: + images: + base_urls: https://images.example.com 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/FrameworkExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php new file mode 100644 index 0000000..123f8e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -0,0 +1,312 @@ + + * + * 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\ParameterBag\ParameterBag; + +abstract class FrameworkExtensionTest extends TestCase +{ + abstract protected function loadFromFile(ContainerBuilder $container, $file); + + public function testCsrfProtection() + { + $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)); + $this->assertEquals('s3cr3t', $container->getParameterBag()->resolveValue($container->findDefinition('form.csrf_provider')->getArgument(1))); + } + + 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->assertTrue($options['cookie_httponly']); + $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 testTemplating() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('templating.name_parser'), '->registerTemplatingConfiguration() loads templating.xml'); + + $this->assertEquals('request', $container->getDefinition('templating.helper.assets')->getScope(), '->registerTemplatingConfiguration() sets request scope on assets helper if one or more packages are request-scoped'); + + // default package should have one http base url and path package ssl url + $this->assertTrue($container->hasDefinition('templating.asset.default_package.http')); + $package = $container->getDefinition('templating.asset.default_package.http'); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\DefinitionDecorator', $package); + $this->assertEquals('templating.asset.url_package', $package->getParent()); + $arguments = array_values($package->getArguments()); + $this->assertEquals(array('http://cdn.example.com'), $arguments[0]); + $this->assertEquals('SomeVersionScheme', $arguments[1]); + $this->assertEquals('%%s?%%s', $arguments[2]); + + $this->assertTrue($container->hasDefinition('templating.asset.default_package.ssl')); + $package = $container->getDefinition('templating.asset.default_package.ssl'); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\DefinitionDecorator', $package); + $this->assertEquals('templating.asset.path_package', $package->getParent()); + + $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'); + } + + public function testTemplatingAssetsHelperScopeDependsOnPackageArgumentScopes() + { + $container = $this->createContainerFromFile('templating_url_package'); + + $this->assertNotEquals('request', $container->getDefinition('templating.helper.assets')->getScope(), '->registerTemplatingConfiguration() does not set request scope on assets helper if no packages are request-scoped'); + } + + 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'); + + $resources = array(); + foreach ($container->getDefinition('translator.default')->getMethodCalls() as $call) { + if ('addResource' == $call[0]) { + $resources[] = $call[1]; + } + } + + $files = array_map(function($resource) { return realpath($resource[1]); }, $resources); + $ref = new \ReflectionClass('Symfony\Component\Validator\Validator'); + $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\SecurityContext'); + $this->assertContains( + strtr(dirname(dirname($ref->getFileName())) . '/Resources/translations/security.en.xlf', '/', DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds Security translation resources' + ); + + $calls = $container->getDefinition('translator.default')->getMethodCalls(); + $this->assertEquals(array('fr'), $calls[0][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'); + + $this->assertTrue($container->hasDefinition('validator'), '->registerValidationConfiguration() loads validator.xml'); + $this->assertTrue($container->hasDefinition('validator.mapping.loader.xml_files_loader'), '->registerValidationConfiguration() defines the XML loader'); + $this->assertTrue($container->hasDefinition('validator.mapping.loader.yaml_files_loader'), '->registerValidationConfiguration() defines the YAML loader'); + + $xmlFiles = $container->getParameter('validator.mapping.loader.xml_files_loader.mapping_files'); + $ref = new \ReflectionClass('Symfony\Component\Form\Form'); + $this->assertContains( + realpath(dirname($ref->getFileName()).'/Resources/config/validation.xml'), + array_map('realpath', $xmlFiles), + '->registerValidationConfiguration() adds Form validation.xml to XML loader' + ); + } + + public function testAnnotations() + { + if (!class_exists('Doctrine\\Common\\Version')) { + $this->markTestSkipped('Doctrine is not available.'); + } + + $container = $this->createContainerFromFile('full'); + + $this->assertEquals($container->getParameter('kernel.cache_dir').'/annotations', $container->getDefinition('annotations.file_cache_reader')->getArgument(1)); + $this->assertInstanceOf('Doctrine\Common\Annotations\FileCacheReader', $container->get('annotation_reader')); + } + + 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'); + + $this->assertTrue($container->hasDefinition('validator.mapping.loader.annotation_loader'), '->registerValidationConfiguration() defines the annotation loader'); + $loaders = $container->getDefinition('validator.mapping.loader.loader_chain')->getArgument(0); + $found = false; + foreach ($loaders as $loader) { + if ('validator.mapping.loader.annotation_loader' === (string) $loader) { + $found = true; + } + } + $this->assertTrue($found, 'validator.mapping.loader.annotation_loader is added to the loader chain.'); + } + + 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'), + )); + + $yamlArgs = $container->getParameter('validator.mapping.loader.yaml_files_loader.mapping_files'); + $this->assertCount(1, $yamlArgs); + $this->assertStringEndsWith('TestBundle'.DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'validation.yml', $yamlArgs[0]); + + $xmlArgs = $container->getParameter('validator.mapping.loader.xml_files_loader.mapping_files'); + $this->assertCount(2, $xmlArgs); + $this->assertStringEndsWith('Component'.DIRECTORY_SEPARATOR.'Form/Resources/config/validation.xml', $xmlArgs[0]); + $this->assertStringEndsWith('TestBundle'.DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'validation.xml', $xmlArgs[1]); + } + + 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.compiled_classes' => array(), + '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; + } +} 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..fad373e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.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\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'); + } +} 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..b8dcefc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.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\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'); + } + + public function testCsrfProtectionShouldBeEnabledByDefault() + { + $container = $this->createContainerFromFile('csrf'); + + $this->assertTrue($container->getParameter('form.type_extension.csrf.enabled')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/TestSessionListenerTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/TestSessionListenerTest.php new file mode 100644 index 0000000..2c91254 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/TestSessionListenerTest.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\FrameworkBundle\Tests\EventListener; + +use Symfony\Bundle\FrameworkBundle\EventListener\TestSessionListener; +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 = new TestSessionListener($this->getMock('Symfony\Component\DependencyInjection\ContainerInterface')); + $this->session = $this->getSession(); + } + + protected function tearDown() + { + $this->listener = null; + $this->session = null; + } + + 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/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/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..48ea9fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php @@ -0,0 +1,2 @@ +This template is used for translation message extraction tests +trans('new key') ?> 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/Fragments/ContainerAwareHIncludeFragmentRendererTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fragments/ContainerAwareHIncludeFragmentRendererTest.php new file mode 100644 index 0000000..546d48b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fragments/ContainerAwareHIncludeFragmentRendererTest.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\Fragment; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Fragment\ContainerAwareHIncludeFragmentRenderer; +use Symfony\Component\HttpFoundation\Request; + +class ContainerAwareHIncludeFragmentRendererTest extends TestCase +{ + public function testRender() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container->expects($this->once()) + ->method('get') + ->will($this->returnValue($this->getMock('\Twig_Environment'))) + ; + $renderer = new ContainerAwareHIncludeFragmentRenderer($container); + $renderer->render('/', Request::create('/')); + } +} 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..0452e25 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/ProfilerController.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\Bundle\TestBundle\Controller; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\DependencyInjection\ContainerAware; + +class ProfilerController extends ContainerAware +{ + 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..8d39576 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.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\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\DependencyInjection\ContainerAware; + +class SessionController extends ContainerAware +{ + public function welcomeAction($name=null) + { + $request = $this->container->get('request'); + $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 = $this->container->get('request')->getSession('session')->invalidate(); + + return new Response('Session cleared.'); + } + + public function setFlashAction($message) + { + $request = $this->container->get('request'); + $session = $request->getSession(); + $session->getFlashBag()->set('notice', $message); + + return new RedirectResponse($this->container->get('router')->generate('session_showflash')); + } + + public function showFlashAction() + { + $request = $this->container->get('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..598ecc0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestController.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\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\HttpKernel\Controller\ControllerReference; + +class SubRequestController extends ContainerAware +{ + 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/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..6a9f5a8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml @@ -0,0 +1,38 @@ +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] 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..d17347c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/TestBundle.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\Functional\Bundle\TestBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class TestBundle extends Bundle +{ +} 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..d4eb927 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.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\Tests\Functional; + +/** + * @group 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..3e90e9d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.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\Bundle\FrameworkBundle\Tests\Functional; + +/** + * @group 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..2676653 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SubRequestsTest.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; + +/** + * @group 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..8cf0dfe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.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\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 setUp() + { + if (!class_exists('Twig_Environment')) { + $this->markTestSkipped('Twig is not available.'); + } + + parent::setUp(); + } + + 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\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..caf2fd8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.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\Bundle\FrameworkBundle\Tests\Functional; + +// 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 init() + { + } + + 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) + { + call_user_func_array(array($this, '__construct'), unserialize($str)); + } + + 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/Profiler/bundles.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Profiler/bundles.php new file mode 100644 index 0000000..351cf79 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Profiler/bundles.php @@ -0,0 +1,9 @@ + + * + * 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('_scheme' => '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..fa29765 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.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\Bundle\FrameworkBundle\Tests\Routing; + +use Symfony\Bundle\FrameworkBundle\Routing\Router; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class RoutingTest extends \PHPUnit_Framework_TestCase +{ + public function testDefaultsPlaceholders() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route( + '/foo', + array( + 'foo' => 'before_%parameter.foo%', + 'bar' => '%parameter.bar%_after', + 'baz' => '%%unescaped%%', + 'boo' => array('%parameter%', '%%escaped_parameter%%', array('%bee_parameter%', 'bee')), + 'bee' => array('bee', 'bee'), + ), + array( + ) + )); + + $sc = $this->getServiceContainer($routes); + + $sc->expects($this->at(1))->method('hasParameter')->will($this->returnValue(true)); + $sc->expects($this->at(2))->method('getParameter')->will($this->returnValue('foo')); + $sc->expects($this->at(3))->method('hasParameter')->will($this->returnValue(true)); + $sc->expects($this->at(4))->method('getParameter')->will($this->returnValue('bar')); + + $sc->expects($this->at(5))->method('hasParameter')->will($this->returnValue(true)); + $sc->expects($this->at(6))->method('getParameter')->will($this->returnValue('boo')); + + $sc->expects($this->at(7))->method('hasParameter')->will($this->returnValue(true)); + $sc->expects($this->at(8))->method('getParameter')->will($this->returnValue('foo_bee')); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + array( + 'foo' => 'before_foo', + 'bar' => 'bar_after', + 'baz' => '%unescaped%', + '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' => '%%unescaped%%', + ) + )); + + $sc = $this->getServiceContainer($routes); + + $sc->expects($this->at(1))->method('hasParameter')->with('parameter.foo')->will($this->returnValue(true)); + $sc->expects($this->at(2))->method('getParameter')->with('parameter.foo')->will($this->returnValue('foo')); + $sc->expects($this->at(3))->method('hasParameter')->with('parameter.bar')->will($this->returnValue(true)); + $sc->expects($this->at(4))->method('getParameter')->with('parameter.bar')->will($this->returnValue('bar')); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + array( + 'foo' => 'before_foo', + 'bar' => 'bar_after', + 'baz' => '%unescaped%', + ), + $route->getRequirements() + ); + } + + public function testPatternPlaceholders() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/before/%parameter.foo%/after/%%unescaped%%')); + + $sc = $this->getServiceContainer($routes); + + $sc->expects($this->at(1))->method('hasParameter')->with('parameter.foo')->will($this->returnValue(true)); + $sc->expects($this->at(2))->method('getParameter')->with('parameter.foo')->will($this->returnValue('foo')); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + '/before/foo/after/%unescaped%', + $route->getPath() + ); + } + + public function testHostPlaceholders() + { + $routes = new RouteCollection(); + + $route = new Route('foo'); + $route->setHost('/before/%parameter.foo%/after/%%unescaped%%'); + + $routes->add('foo', $route); + + $sc = $this->getServiceContainer($routes); + + $sc->expects($this->at(1))->method('hasParameter')->with('parameter.foo')->will($this->returnValue(true)); + $sc->expects($this->at(2))->method('getParameter')->with('parameter.foo')->will($this->returnValue('foo')); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + '/before/foo/after/%unescaped%', + $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); + + $sc->expects($this->at(1))->method('hasParameter')->with('nope')->will($this->returnValue(false)); + + $router = new Router($sc, 'foo'); + $router->getRouteCollection()->get('foo'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage A string value must be composed of strings and/or numbers,but found parameter "object" of type object inside string value "/%object%". + */ + public function testExceptionOnNonStringParameter() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/%object%')); + + $sc = $this->getServiceContainer($routes); + + $sc->expects($this->at(1))->method('hasParameter')->with('object')->will($this->returnValue(true)); + $sc->expects($this->at(2))->method('getParameter')->with('object')->will($this->returnValue(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()))); + } + + 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\\ContainerInterface'); + + $sc + ->expects($this->once()) + ->method('get') + ->will($this->returnValue($loader)) + ; + + return $sc; + } +} 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..9a960e3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.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\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; + +// should probably be moved to the Translation component +use Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper; + +class FormHelperDivLayoutTest extends AbstractDivLayoutTest +{ + /** + * @var PhpEngine + */ + protected $engine; + + protected function setUp() + { + if (!class_exists('Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper')) { + $this->markTestSkipped('The "FrameworkBundle" is not available'); + } + + if (!class_exists('Symfony\Component\Templating\PhpEngine')) { + $this->markTestSkipped('The "Templating" component is not available'); + } + + parent::setUp(); + } + + 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->csrfProvider, array( + 'FrameworkBundle:Form', + )), + )); + } + + 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 renderEnctype(FormView $view) + { + return (string) $this->engine->get('form')->enctype($view); + } + + 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..96c56b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.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\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; + +// should probably be moved to the Translation component +use Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper; + +class FormHelperTableLayoutTest extends AbstractTableLayoutTest +{ + /** + * @var PhpEngine + */ + protected $engine; + + protected function setUp() + { + if (!class_exists('Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper')) { + $this->markTestSkipped('The "FrameworkBundle" is not available'); + } + + if (!class_exists('Symfony\Component\Templating\PhpEngine')) { + $this->markTestSkipped('The "Templating" component is not available'); + } + + parent::setUp(); + } + + 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->csrfProvider, 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 renderEnctype(FormView $view) + { + return (string) $this->engine->get('form')->enctype($view); + } + + 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..d3d4f22 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/RequestHelperTest.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\FrameworkBundle\Tests\Templating\Helper; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\RequestHelper; + +class RequestHelperTest extends \PHPUnit_Framework_TestCase +{ + protected $request; + + protected function setUp() + { + $this->request = new Request(); + $this->request->initialize(array('foobar' => 'bar')); + } + + protected function tearDown() + { + $this->request = null; + } + + public function testGetParameter() + { + $helper = new RequestHelper($this->request); + + $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->request); + + $this->assertEquals('en', $helper->getLocale()); + } + + public function testGetName() + { + $helper = new RequestHelper($this->request); + + $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_entry_label.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php new file mode 100644 index 0000000..0efecf0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_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/_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..4bd043e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.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\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use Symfony\Component\HttpFoundation\Request; +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 $request; + + protected function setUp() + { + $this->request = new Request(); + + $session = new Session(new MockArraySessionStorage()); + $session->set('foobar', 'bar'); + $session->getFlashBag()->set('notice', 'bar'); + + $this->request->setSession($session); + } + + protected function tearDown() + { + $this->request = null; + } + + public function testFlash() + { + $helper = new SessionHelper($this->request); + + $this->assertTrue($helper->hasFlash('notice')); + + $this->assertEquals(array('bar'), $helper->getFlash('notice')); + } + + public function testGetFlashes() + { + $helper = new SessionHelper($this->request); + $this->assertEquals(array('notice' => array('bar')), $helper->getFlashes()); + } + + public function testGet() + { + $helper = new SessionHelper($this->request); + + $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->request); + + $this->assertEquals('session', $helper->getName()); + } +} 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..476b398 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/PhpEngineTest.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; + +use Symfony\Bundle\FrameworkBundle\Templating\PhpEngine; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\HttpFoundation\Request; +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', 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(); + $request = new Request(); + $session = new Session(new MockArraySessionStorage()); + + $request->setSession($session); + $container->set('request', $request); + + 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..2a1544c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.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\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; + +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')), + ); + } + + /** + * @dataProvider getInvalidLogicalNameProvider + * @expectedException \InvalidArgumentException + */ + public function testParseInvalidName($name) + { + $this->parser->parse($name); + } + + public function getInvalidLogicalNameProvider() + { + return array( + array('BarBundle:Post:index.html.php'), + array('FooBundle:Post:index'), + array('FooBundle:Post'), + array('FooBundle:Post:foo:bar'), + ); + } +} 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..595be96 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TimedPhpEngineTest.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\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\TimedPhpEngine; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\HttpFoundation\Request; +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 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 \Symfony\Component\DependencyInjection\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 \Symfony\Bundle\FrameworkBundle\Templating\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..4305f0e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.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\Translation; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor; +use Symfony\Component\Translation\MessageCatalogue; + +class PhpExtractorTest extends TestCase +{ + public function testExtraction() + { + // Arrange + $extractor = new PhpExtractor(); + $extractor->setPrefix('prefix'); + $catalogue = new MessageCatalogue('en'); + + // Act + $extractor->extract(__DIR__.'/../Fixtures/Resources/views/', $catalogue); + + // Assert + $this->assertCount(1, $catalogue->all('messages'), '->extract() should find 1 translation'); + $this->assertTrue($catalogue->has('new key'), '->extract() should find at leat "new key" message'); + $this->assertEquals('prefixnew key', $catalogue->get('new key'), '->extract() should apply "prefix" as prefix'); + } +} 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..1988def --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -0,0 +1,189 @@ + + * + * 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(); + } + + public 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')); + + $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')); + } + + public function testTransWithCaching() + { + // prime the cache + $translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir)); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es')); + + $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')); + + // do it another time as the cache is primed now + $loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface'); + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir)); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es')); + + $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')); + } + + public function testGetLocale() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + + $request + ->expects($this->once()) + ->method('getLocale') + ->will($this->returnValue('en')) + ; + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + + $container + ->expects($this->exactly(2)) + ->method('isScopeActive') + ->with('request') + ->will($this->onConsecutiveCalls(false, true)) + ; + + $container + ->expects($this->once()) + ->method('has') + ->with('request') + ->will($this->returnValue(true)) + ; + + $container + ->expects($this->once()) + ->method('get') + ->with('request') + ->will($this->returnValue($request)) + ; + + $translator = new Translator($container, new MessageSelector()); + + $this->assertNull($translator->getLocale()); + $this->assertSame('en', $translator->getLocale()); + } + + protected function getCatalogue($locale, $messages) + { + $catalogue = new MessageCatalogue($locale); + foreach ($messages as $key => $translation) { + $catalogue->set($key, $translation); + } + + 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)', + )))) + ; + + 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()) + { + $translator = new Translator( + $this->getContainer($loader), + new MessageSelector(), + array('loader' => array('loader')), + $options + ); + + $translator->addResource('loader', 'foo', 'fr'); + $translator->addResource('loader', 'foo', 'en'); + $translator->addResource('loader', 'foo', 'es'); + + return $translator; + } +} 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..7dc3389 --- /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 = new \stdClass(); + + // mock ContainerBuilder b/c it implements TaggedContainerInterface + $container = $this->getMock('Symfony\\Component\\DependencyInjection\\ContainerBuilder'); + $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..1eb3605 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.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\Translation; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Extractor\ExtractorInterface; + +/** + * PhpExtractor extracts translation messages from a php template. + * + * @author Michel Salib + */ +class PhpExtractor implements ExtractorInterface +{ + const MESSAGE_TOKEN = 300; + const IGNORE_TOKEN = 400; + + /** + * Prefix for new found message. + * + * @var string + */ + private $prefix = ''; + + /** + * The sequence that captures translation messages. + * + * @var array + */ + protected $sequences = array( + array( + '$view', + '[', + '\'translator\'', + ']', + '->', + 'trans', + '(', + self::MESSAGE_TOKEN, + ')', + ), + ); + + /** + * {@inheritDoc} + */ + public function extract($directory, MessageCatalogue $catalog) + { + // load any existing translation files + $finder = new Finder(); + $files = $finder->files()->name('*.php')->in($directory); + 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; + } + + /** + * Extracts trans message from php tokens. + * + * @param array $tokens + * @param MessageCatalogue $catalog + */ + protected function parseTokens($tokens, MessageCatalogue $catalog) + { + foreach ($tokens as $key => $token) { + foreach ($this->sequences as $sequence) { + $message = ''; + + foreach ($sequence as $id => $item) { + if ($this->normalizeToken($tokens[$key + $id]) == $item) { + continue; + } elseif (self::MESSAGE_TOKEN == $item) { + $message = $this->normalizeToken($tokens[$key + $id]); + } elseif (self::IGNORE_TOKEN == $item) { + continue; + } else { + break; + } + } + + $message = trim($message, '\''); + + if ($message) { + $catalog->set($message, $this->prefix.$message); + break; + } + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/TranslationLoader.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/TranslationLoader.php new file mode 100644 index 0000000..aea1ec4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/TranslationLoader.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\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) + { + 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..efb40aa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.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\Translation; + +use Symfony\Component\Translation\Translator as BaseTranslator; +use Symfony\Component\Translation\MessageSelector; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Config\ConfigCache; + +/** + * Translator. + * + * @author Fabien Potencier + */ +class Translator extends BaseTranslator +{ + protected $container; + protected $options; + protected $loaderIds; + + /** + * Constructor. + * + * Available options: + * + * * cache_dir: The cache directory (or null to disable caching) + * * debug: Whether to enable debugging or not (false by default) + * + * @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; + + $this->options = array( + 'cache_dir' => null, + 'debug' => false, + ); + + // 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); + + parent::__construct(null, $selector); + } + + /** + * {@inheritdoc} + */ + public function getLocale() + { + if (null === $this->locale && $this->container->isScopeActive('request') && $this->container->has('request')) { + $this->locale = $this->container->get('request')->getLocale(); + } + + return $this->locale; + } + + /** + * {@inheritdoc} + */ + protected function loadCatalogue($locale) + { + if (isset($this->catalogues[$locale])) { + return; + } + + if (null === $this->options['cache_dir']) { + $this->initialize(); + + return parent::loadCatalogue($locale); + } + + $cache = new ConfigCache($this->options['cache_dir'].'/catalogue.'.$locale.'.php', $this->options['debug']); + if (!$cache->isFresh()) { + $this->initialize(); + + parent::loadCatalogue($locale); + + $fallbackContent = ''; + $current = ''; + foreach ($this->computeFallbackLocales($locale) as $fallback) { + $fallbackContent .= sprintf(<<addFallbackCatalogue(\$catalogue%s); + + +EOF + , + ucfirst($fallback), + $fallback, + var_export($this->catalogues[$fallback]->all(), true), + ucfirst($current), + ucfirst($fallback) + ); + $current = $fallback; + } + + $content = sprintf(<<catalogues[$locale]->all(), true), + $fallbackContent + ); + + $cache->write($content, $this->catalogues[$locale]->getResources()); + + return; + } + + $this->catalogues[$locale] = include $cache; + } + + protected function initialize() + { + foreach ($this->loaderIds as $id => $aliases) { + foreach ($aliases as $alias) { + $this->addLoader($alias, $this->container->get($id)); + } + } + } +} 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..86e2f99 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.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\Validator; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; +use Symfony\Component\Validator\ConstraintValidator; + +/** + * 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 ConstraintValidator A validator for the supplied constraint + */ + 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]); + } + + 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..daa1eee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -0,0 +1,54 @@ +{ + "name": "symfony/framework-bundle", + "type": "symfony-bundle", + "description": "Symfony FrameworkBundle", + "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.3", + "symfony/dependency-injection" : "~2.2", + "symfony/config" : "~2.2", + "symfony/event-dispatcher": "~2.1", + "symfony/http-kernel": "~2.3", + "symfony/filesystem": "~2.3", + "symfony/routing": "~2.2", + "symfony/stopwatch": "~2.3", + "symfony/templating": "~2.1", + "symfony/translation": "~2.3", + "doctrine/common": "~2.2" + }, + "require-dev": { + "symfony/finder": "~2.0", + "symfony/security": "~2.3", + "symfony/form": "~2.3", + "symfony/class-loader": "~2.1", + "symfony/validator": "~2.1" + }, + "suggest": { + "symfony/console": "", + "symfony/finder": "", + "symfony/form": "", + "symfony/validator": "" + }, + "autoload": { + "psr-0": { "Symfony\\Bundle\\FrameworkBundle\\": "" } + }, + "target-dir": "Symfony/Bundle/FrameworkBundle", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..663faf4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./vendor + ./Resources + ./Tests + + + + 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..724254d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -0,0 +1,88 @@ +CHANGELOG +========= + +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: form.csrf_provider # 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..66a2f0a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Command/InitAclCommand.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\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 +{ + /** + * @see Command + */ + 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 + ) + ; + } + + /** + * @see Command::execute() + */ + 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/DataCollector/SecurityDataCollector.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php new file mode 100644 index 0000000..acefa38 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.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\SecurityBundle\DataCollector; + +use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; + +/** + * SecurityDataCollector. + * + * @author Fabien Potencier + */ +class SecurityDataCollector extends DataCollector +{ + private $context; + + public function __construct(SecurityContextInterface $context = null) + { + $this->context = $context; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + if (null === $this->context) { + $this->data = array( + 'enabled' => false, + 'authenticated' => false, + 'token_class' => null, + 'user' => '', + 'roles' => array(), + ); + } elseif (null === $token = $this->context->getToken()) { + $this->data = array( + 'enabled' => true, + 'authenticated' => false, + 'token_class' => null, + 'user' => '', + 'roles' => array(), + ); + } else { + $this->data = array( + 'enabled' => true, + 'authenticated' => $token->isAuthenticated(), + 'token_class' => get_class($token), + 'user' => $token->getUsername(), + 'roles' => array_map(function ($role){ return $role->getRole();}, $token->getRoles()), + ); + } + } + + /** + * Checks if security is enabled. + * + * @return Boolean 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']; + } + + /** + * Checks if the user is authenticated or not. + * + * @return Boolean 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']; + } + + /** + * {@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..cfc6173 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.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\SecurityBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * 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); + + $container->getDefinition('security.access.decision_manager')->replaceArgument(0, 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..dfd45fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.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\Bundle\SecurityBundle\DependencyInjection; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * This class contains the configuration information 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 \Symfony\Component\Config\Definition\Builder\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() + ->scalarNode('session_fixation_strategy')->cannotBeEmpty()->info('strategy can be: none, migrate, invalidate')->defaultValue('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() + ->scalarNode('strategy')->defaultValue('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') + ->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() + ->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() + ->booleanNode('security')->defaultTrue()->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_provider')->cannotBeEmpty()->end() + ->scalarNode('intention')->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('key')->defaultValue(uniqid())->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')) + )) + ->disallowNewKeysInSubsequentConfigs() + ->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..27f08b0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.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\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 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 Boolean 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) + { + if (isset($config['success_handler'])) { + return $config['success_handler']; + } + + $successHandlerId = 'security.authentication.success_handler.'.$id.'.'.str_replace('-', '_', $this->getKey()); + + $successHandler = $container->setDefinition($successHandlerId, new DefinitionDecorator('security.authentication.success_handler')); + $successHandler->replaceArgument(1, array_intersect_key($config, $this->defaultSuccessHandlerOptions)); + $successHandler->addMethodCall('setProviderKey', array($id)); + + return $successHandlerId; + } + + protected function createAuthenticationFailureHandler($container, $id, $config) + { + if (isset($config['failure_handler'])) { + return $config['failure_handler']; + } + + $id = 'security.authentication.failure_handler.'.$id.'.'.str_replace('-', '_', $this->getKey()); + + $failureHandler = $container->setDefinition($id, new DefinitionDecorator('security.authentication.failure_handler')); + $failureHandler->replaceArgument(2, array_intersect_key($config, $this->defaultFailureHandlerOptions)); + + return $id; + } +} 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..ce06bba --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.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\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('intention', '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_provider')->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(2, $id) + ; + + return $provider; + } + + protected function createListener($container, $id, $config, $userProvider) + { + $listenerId = parent::createListener($container, $id, $config, $userProvider); + + if (isset($config['csrf_provider'])) { + $container + ->getDefinition($listenerId) + ->addArgument(new Reference($config['csrf_provider'])) + ; + } + + 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/HttpBasicFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php new file mode 100644 index 0000000..c87f68c --- /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(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/HttpDigestFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php new file mode 100644 index 0000000..3a49b5d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.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\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('key')->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['key']) + ; + + 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..08c4a33 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.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\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')) + ->addArgument($config['key']) + ->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['key']); + $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)); + + return array($authProviderId, $listenerId, $defaultEntryPoint); + } + + public function getPosition() + { + return 'remember_me'; + } + + public function getKey() + { + return 'remember-me'; + } + + public function addConfiguration(NodeDefinition $node) + { + $node->fixXmlConfig('user_provider'); + $builder = $node->children(); + + $builder + ->scalarNode('key')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('token_provider')->end() + ->arrayNode('user_providers') + ->beforeNormalization() + ->ifString()->then(function($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->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/SecurityFactoryInterface.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php new file mode 100644 index 0000000..5e8773b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.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\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); + + public function getPosition(); + + public function getKey(); + + public function addConfiguration(NodeDefinition $builder); +} 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..19446d4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.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\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)) + ->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..bab1bb4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/InMemoryFactory.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\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())->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/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..936552c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -0,0 +1,655 @@ + + * + * 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\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; + +/** + * SecurityExtension. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class SecurityExtension extends Extension +{ + private $requestMatchers = array(); + private $contextListeners = array(); + private $listenerPositions = array('pre_auth', 'form', 'http', 'remember_me'); + private $factories = array(); + private $userProviderFactories = array(); + + 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'); + + // 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\\SecurityContext', + 'Symfony\\Component\\Security\\Core\\User\\UserProviderInterface', + 'Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager', + 'Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager', + '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) + { + $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'])) { + $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'] + ); + + $container->getDefinition('security.access_map') + ->addMethodCall('add', array($matcher, $access['roles'], $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 + $i = 0; + $matcher = null; + if (isset($firewall['request_matcher'])) { + $matcher = new Reference($firewall['request_matcher']); + } elseif (isset($firewall['pattern'])) { + $matcher = $this->createRequestMatcher($container, $firewall['pattern']); + } + + // 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'], + 'intention' => $firewall['logout']['intention'], + '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_provider'])) { + $listener->addArgument(new Reference($firewall['logout']['csrf_provider'])); + } + + // 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 LogoutUrlHelper + $container + ->getDefinition('templating.helper.logout_url') + ->addMethodCall('registerListener', array( + $id, + $firewall['logout']['path'], + $firewall['logout']['intention'], + $firewall['logout']['csrf_parameter'], + isset($firewall['logout']['csrf_provider']) ? new Reference($firewall['logout']['csrf_provider']) : null, + )) + ; + } + + // Authentication listeners + list($authListeners, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $authenticationProviders, $defaultProvider); + + $listeners = array_merge($listeners, $authListeners); + + // Access listener + $listeners[] = new Reference('security.access_listener'); + + // Switch user listener + if (isset($firewall['switch_user'])) { + $listeners[] = new Reference($this->createSwitchUserListener($container, $id, $firewall['switch_user'], $defaultProvider)); + } + + // Determine default entry point + if (isset($firewall['entry_point'])) { + $defaultEntryPoint = $firewall['entry_point']; + } + + // Exception listener + $exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $defaultEntryPoint)); + + 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) + { + $listeners = array(); + $hasListeners = false; + $defaultEntryPoint = null; + + 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']['key']) + ; + + $listeners[] = new Reference($listenerId); + + $providerId = 'security.authentication.provider.anonymous.'.$id; + $container + ->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.anonymous')) + ->replaceArgument(0, $firewall['anonymous']['key']) + ; + + $authenticationProviders[] = $providerId; + $hasListeners = true; + } + + if (false === $hasListeners) { + throw new \LogicException(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' => new Parameter('security.encoder.plain.class'), + 'arguments' => $arguments, + ); + } + + // pbkdf2 encoder + if ('pbkdf2' === $config['algorithm']) { + return array( + 'class' => new Parameter('security.encoder.pbkdf2.class'), + 'arguments' => array( + $config['hash_algorithm'], + $config['encode_as_base64'], + $config['iterations'], + $config['key_length'], + ), + ); + } + + // bcrypt encoder + if ('bcrypt' === $config['algorithm']) { + return array( + 'class' => new Parameter('security.encoder.bcrypt.class'), + 'arguments' => array($config['cost']), + ); + } + + // message digest encoder + return array( + 'class' => new Parameter('security.encoder.digest.class'), + '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, $master = true) + { + $name = $this->getUserProviderId(strtolower($name)); + + 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; + } + + // Doctrine Entity DAO provider + if (isset($provider['entity'])) { + $container + ->setDefinition($name, new DefinitionDecorator('security.user.provider.entity')) + ->addArgument($provider['entity']['class']) + ->addArgument($provider['entity']['property']) + ; + + return $name; + } + + // In-memory DAO provider + $definition = $container->setDefinition($name, new DefinitionDecorator('security.user.provider.in_memory')); + foreach ($provider['users'] as $username => $user) { + $userId = $name.'_'.$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))); + } + + return $name; + } + + private function getUserProviderId($name) + { + return 'security.user.provider.concrete.'.$name; + } + + private function createExceptionListener($container, $config, $id, $defaultEntryPoint) + { + $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)); + + // 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(3, $id); + $listener->replaceArgument(6, $config['parameter']); + $listener->replaceArgument(7, $config['role']); + + return $switchUserListenerId; + } + + private function createRequestMatcher($container, $path = null, $host = null, $methods = array(), $ip = null, array $attributes = array()) + { + $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]; + } + + if ($methods) { + $methods = array_map('strtoupper', (array) $methods); + } + + // 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, '%security.matcher.class%') + ->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); + } +} 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..53758ab --- /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\DependencyInjection\ContainerInterface; +use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; + +/** + * Merges ACL schema into the given schema. + * + * @author Johannes M. Schmitt + */ +class AclSchemaListener +{ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function postGenerateSchema(GenerateSchemaEventArgs $args) + { + $schema = $args->getSchema(); + $this->container->get('security.acl.dbal.schema')->addToSchema($schema); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/LICENSE b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/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..f6106f7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/collectors.xml @@ -0,0 +1,17 @@ + + + + + + Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector + + + + + + + + + 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..dd2a7fc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -0,0 +1,150 @@ + + + + + + Symfony\Component\Security\Core\SecurityContext + + Symfony\Component\Security\Core\User\UserChecker + + Symfony\Component\Security\Core\Encoder\EncoderFactory + Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder + Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder + Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder + Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder + + Symfony\Component\Security\Core\User\InMemoryUserProvider + Symfony\Component\Security\Core\User\User + Symfony\Component\Security\Core\User\ChainUserProvider + + Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver + Symfony\Component\Security\Core\Authentication\Token\AnonymousToken + Symfony\Component\Security\Core\Authentication\Token\RememberMeToken + + Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager + + Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy + + Symfony\Component\Security\Core\Authorization\AccessDecisionManager + + Symfony\Component\Security\Core\Authorization\Voter\RoleVoter + Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter + Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter + + Symfony\Component\Security\Http\Firewall + Symfony\Bundle\SecurityBundle\Security\FirewallMap + Symfony\Bundle\SecurityBundle\Security\FirewallContext + Symfony\Component\HttpFoundation\RequestMatcher + + Symfony\Component\Security\Core\Role\RoleHierarchy + + Symfony\Component\Security\Http\HttpUtils + + Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator + + + + + + + %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% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %kernel.cache_dir%/secure_random.seed + + + + 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..624b6b1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl.xml @@ -0,0 +1,47 @@ + + + + + + Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy + + Symfony\Component\Security\Acl\Voter\AclVoter + Symfony\Component\Security\Acl\Permission\BasicPermissionMap + + Symfony\Component\Security\Acl\Domain\ObjectIdentityRetrievalStrategy + Symfony\Component\Security\Acl\Domain\SecurityIdentityRetrievalStrategy + + Symfony\Component\Security\Acl\Domain\DoctrineAclCache + + Symfony\Component\Security\Acl\Domain\AclCollectionCache + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..aac84a3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl_dbal.xml @@ -0,0 +1,54 @@ + + + + + + Symfony\Component\Security\Acl\Dbal\MutableAclProvider + Symfony\Component\Security\Acl\Dbal\Schema + Symfony\Bundle\SecurityBundle\EventListener\AclSchemaListener + + + + + + + + + + %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..6148337 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml @@ -0,0 +1,212 @@ + + + + + + Symfony\Component\Security\Http\EntryPoint\RetryAuthenticationEntryPoint + + Symfony\Component\Security\Http\Firewall\ChannelListener + + Symfony\Component\Security\Http\EntryPoint\FormAuthenticationEntryPoint + Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener + + Symfony\Component\Security\Http\Firewall\BasicAuthenticationListener + Symfony\Component\Security\Http\EntryPoint\BasicAuthenticationEntryPoint + + Symfony\Component\Security\Http\Firewall\DigestAuthenticationListener + Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint + + Symfony\Component\Security\Http\Firewall\X509AuthenticationListener + + Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener + + Symfony\Component\Security\Http\Firewall\SwitchUserListener + + Symfony\Component\Security\Http\Firewall\LogoutListener + Symfony\Component\Security\Http\Logout\SessionLogoutHandler + Symfony\Component\Security\Http\Logout\CookieClearingLogoutHandler + Symfony\Component\Security\Http\Logout\DefaultLogoutSuccessHandler + + Symfony\Component\Security\Http\Firewall\AccessListener + Symfony\Component\Security\Http\AccessMap + Symfony\Component\Security\Http\Firewall\ExceptionListener + Symfony\Component\Security\Http\Firewall\ContextListener + + Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider + Symfony\Component\Security\Core\Authentication\Provider\PreAuthenticatedAuthenticationProvider + + Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider + + Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler + Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler + + + + + + + + + + + + + + + + %request_listener.http_port% + %request_listener.https_port% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + / + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %security.authentication.hide_user_not_found% + + + + + + + + + + + + + + + %security.access.denied_url% + + + + + + + + + + + + + _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..eec67aa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml @@ -0,0 +1,62 @@ + + + + + + Symfony\Component\Security\Core\Authentication\Provider\RememberMeAuthenticationProvider + + Symfony\Component\Security\Http\Firewall\RememberMeListener + Symfony\Component\Security\Core\Authentication\RememberMe\InMemoryTokenProvider + + Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices + Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices + + Symfony\Component\Security\Http\RememberMe\ResponseListener + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..c91bf79 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_php.xml @@ -0,0 +1,24 @@ + + + + + + Symfony\Bundle\SecurityBundle\Templating\Helper\LogoutUrlHelper + Symfony\Bundle\SecurityBundle\Templating\Helper\SecurityHelper + + + + + + + + + + + + + + + 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..d457cf1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_twig.xml @@ -0,0 +1,23 @@ + + + + + + Symfony\Bundle\SecurityBundle\Twig\Extension\LogoutUrlExtension + Symfony\Bridge\Twig\Extension\SecurityExtension + + + + + + + + + + + + + + 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..b6795e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig @@ -0,0 +1,86 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {% if collector.user %} + {% set color_code = (collector.enabled and collector.authenticated) ? 'green' : 'yellow' %} + {% set authentication_color_code = (collector.enabled and collector.authenticated) ? 'green' : 'red' %} + {% set authentication_color_text = (collector.enabled and collector.authenticated) ? 'Yes' : 'No' %} + {% else %} + {% set color_code = collector.enabled ? 'red' : 'black' %} + {% endif %} + {% set text %} + {% if collector.user %} +
    + Logged in as + {{ collector.user }} +
    +
    + Authenticated + {{ authentication_color_text }} +
    + {% if collector.tokenClass != null %} +
    + Token class + {{ collector.tokenClass|abbr_class }} +
    + {% endif %} + {% elseif collector.enabled %} + You are not authenticated. + {% else %} + The security is disabled. + {% endif %} + {% endset %} + {% set icon %} + Security + + {% if collector.user %}
    {{ collector.user }}
    {% endif %} + {% endset %} + {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %} +{% endblock %} + +{% block menu %} + + + Security + +{% endblock %} + +{% block panel %} +

    Security

    + {% if collector.user %} + + + + + + + + + + + + + + {% if collector.tokenClass != null %} + + + + + {% endif %} +
    Username{{ collector.user }}
    Authenticated? + {% if collector.authenticated %} + yes + {% else %} + no {% if not collector.roles|length %}(probably because the user has no roles){% endif %} + {% endif %} +
    Roles{{ collector.roles|yaml_encode }}
    Token class{{ collector.tokenClass }}
    + {% elseif collector.enabled %} +

    + No 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..b06234b --- /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..7d810fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/SecurityBundle.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\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\HttpBasicFactory; +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\UserProvider\InMemoryFactory; + +/** + * 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 HttpBasicFactory()); + $extension->addSecurityListenerFactory(new HttpDigestFactory()); + $extension->addSecurityListenerFactory(new RememberMeFactory()); + $extension->addSecurityListenerFactory(new X509Factory()); + + $extension->addUserProviderFactory(new InMemoryFactory()); + $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..c7135f5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.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\Bundle\SecurityBundle\Templating\Helper; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Templating\Helper\Helper; + +/** + * LogoutUrlHelper provides generator functions for the logout URL. + * + * @author Jeremy Mikola + */ +class LogoutUrlHelper extends Helper +{ + private $container; + private $listeners; + private $router; + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + * @param UrlGeneratorInterface $router A Router instance + */ + public function __construct(ContainerInterface $container, UrlGeneratorInterface $router) + { + $this->container = $container; + $this->router = $router; + $this->listeners = array(); + } + + /** + * Registers a firewall's LogoutListener, allowing its URL to be generated. + * + * @param string $key The firewall key + * @param string $logoutPath The path that starts the logout process + * @param string $intention The intention for CSRF token generation + * @param string $csrfParameter The CSRF token parameter name + * @param CsrfProviderInterface $csrfProvider A CsrfProviderInterface instance + */ + public function registerListener($key, $logoutPath, $intention, $csrfParameter, CsrfProviderInterface $csrfProvider = null) + { + $this->listeners[$key] = array($logoutPath, $intention, $csrfParameter, $csrfProvider); + } + + /** + * Generates the absolute logout path for the firewall. + * + * @param string $key The firewall key + * + * @return string The logout path + */ + public function getLogoutPath($key) + { + return $this->generateLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_PATH); + } + + /** + * Generates the absolute logout URL for the firewall. + * + * @param string $key The firewall key + * + * @return string The logout URL + */ + public function getLogoutUrl($key) + { + return $this->generateLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * Generates the logout URL for the firewall. + * + * @param string $key The firewall key + * @param Boolean|string $referenceType The type of reference (one of the constants in UrlGeneratorInterface) + * + * @return string The logout URL + * + * @throws \InvalidArgumentException if no LogoutListener is registered for the key + */ + private function generateLogoutUrl($key, $referenceType) + { + if (!array_key_exists($key, $this->listeners)) { + throw new \InvalidArgumentException(sprintf('No LogoutListener found for firewall key "%s".', $key)); + } + + list($logoutPath, $intention, $csrfParameter, $csrfProvider) = $this->listeners[$key]; + + $parameters = null !== $csrfProvider ? array($csrfParameter => $csrfProvider->generateCsrfToken($intention)) : array(); + + if ('/' === $logoutPath[0]) { + $request = $this->container->get('request'); + + $url = UrlGeneratorInterface::ABSOLUTE_URL === $referenceType ? $request->getUriForPath($logoutPath) : $request->getBasePath().$logoutPath; + + if (!empty($parameters)) { + $url .= '?'.http_build_query($parameters); + } + } else { + $url = $this->router->generate($logoutPath, $parameters, $referenceType); + } + + return $url; + } + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + 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..7ca0e7b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Templating/Helper/SecurityHelper.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\Templating\Helper; + +use Symfony\Component\Security\Acl\Voter\FieldVote; +use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\Security\Core\SecurityContextInterface; + +/** + * SecurityHelper provides read-only access to the security context. + * + * @author Fabien Potencier + */ +class SecurityHelper extends Helper +{ + private $context; + + /** + * Constructor. + * + * @param SecurityContextInterface $context A SecurityContext instance + */ + public function __construct(SecurityContextInterface $context = null) + { + $this->context = $context; + } + + public function isGranted($role, $object = null, $field = null) + { + if (null === $this->context) { + return false; + } + + if (null !== $field) { + $object = new FieldVote($object, $field); + } + + return $this->context->isGranted($role, $object); + } + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + public function getName() + { + return 'security'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/ConfigurationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/ConfigurationTest.php new file mode 100644 index 0000000..0925e8b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/ConfigurationTest.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\Bundle\SecurityBundle\Tests\DependencyInjection; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\MainConfiguration; +use Symfony\Component\Config\Definition\Processor; + +class ConfigurationTest 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()); + $config = $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()); + $config = $processor->processConfiguration($configuration, array($config)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php new file mode 100644 index 0000000..bb79da3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php @@ -0,0 +1,86 @@ +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('key' => 'TheKey'), + 'form_login' => true, + 'anonymous' => true, + 'switch_user' => true, + 'x509' => true, + 'logout' => 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'), + ), + + '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..4acf6cc --- /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..82966dc --- /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..1e29d40 --- /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/xml/container1.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml new file mode 100644 index 0000000..cb452e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/yml/container1.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml new file mode 100644 index 0000000..169f7fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml @@ -0,0 +1,66 @@ +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: + key: TheKey + form_login: true + anonymous: true + switch_user: true + x509: true + logout: true + + 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 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/PhpSecurityExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpSecurityExtensionTest.php new file mode 100644 index 0000000..fc54797 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpSecurityExtensionTest.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 PhpSecurityExtensionTest extends SecurityExtensionTest +{ + 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..2bc24bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.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\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' => 'qux', 'failure_handler' => 'bar', '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('qux'), + 'index_6' => new Reference('bar'), + 'index_7' => array( + 'use_forward' => true, + ), + ), $definition->getArguments()); + + // entry point + $this->assertEquals('entry_point', $entryPointId, '->create() does not change the default entry point.'); + } + + public function testDefaultFailureHandler() + { + list($container, + $authProviderId, + $listenerId, + $entryPointId + ) = $this->callFactory('foo', array('remember_me' => true), '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']); + } + + public function testDefaultSuccessHandler() + { + list($container, + $authProviderId, + $listenerId, + $entryPointId + ) = $this->callFactory('foo', array('remember_me' => true), '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']); + } + + 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'); + + 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/SecurityExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php new file mode 100644 index 0000000..b85e850 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.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\SecurityBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\Reference; + +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Bundle\SecurityBundle\SecurityBundle; +use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +abstract class SecurityExtensionTest 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.form.secure', + 'security.authentication.listener.basic.secure', + 'security.authentication.listener.digest.secure', + 'security.authentication.listener.anonymous.secure', + 'security.access_listener', + 'security.authentication.switchuser_listener.secure', + ), + ), $listeners); + } + + 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 $rule) { + list($matcherId, $roles, $channel) = $rule; + $requestMatcher = $container->getDefinition($matcherId); + + $this->assertFalse(isset($matcherIds[$matcherId])); + $matcherIds[$matcherId] = true; + + $i = count($matcherIds); + if (1 === $i) { + $this->assertEquals(array('ROLE_USER'), $roles); + $this->assertEquals('https', $channel); + $this->assertEquals( + array('/blog/524', null, array('GET', 'POST')), + $requestMatcher->getArguments() + ); + } elseif (2 === $i) { + $this->assertEquals(array('IS_AUTHENTICATED_ANONYMOUSLY'), $roles); + $this->assertNull($channel); + $this->assertEquals( + array('/blog/.*'), + $requestMatcher->getArguments() + ); + } + } + } + + 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' => new Parameter('security.encoder.plain.class'), + 'arguments' => array(false), + ), + 'JMS\FooBundle\Entity\User2' => array( + 'class' => new Parameter('security.encoder.digest.class'), + 'arguments' => array('sha1', false, 5), + ), + 'JMS\FooBundle\Entity\User3' => array( + 'class' => new Parameter('security.encoder.digest.class'), + 'arguments' => array('md5', true, 5000), + ), + 'JMS\FooBundle\Entity\User4' => new Reference('security.encoder.foo'), + 'JMS\FooBundle\Entity\User5' => array( + 'class' => new Parameter('security.encoder.pbkdf2.class'), + 'arguments' => array('sha1', false, 5, 30), + ), + 'JMS\FooBundle\Entity\User6' => array( + 'class' => new Parameter('security.encoder.bcrypt.class'), + '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')); + } + + 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/XmlSecurityExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlSecurityExtensionTest.php new file mode 100644 index 0000000..6ce0489 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlSecurityExtensionTest.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 XmlSecurityExtensionTest extends SecurityExtensionTest +{ + 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/YamlSecurityExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlSecurityExtensionTest.php new file mode 100644 index 0000000..f5fabe0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlSecurityExtensionTest.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 YamlSecurityExtensionTest extends SecurityExtensionTest +{ + 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..e94a21e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticationCommencingTest.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\SecurityBundle\Tests\Functional; + +class AuthenticationCommencingTest extends WebTestCase +{ + public function testAuthenticationIsCommencingIfAccessDeniedExceptionIsWrapped() + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'config.yml')); + $client->insulate(); + + $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/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..3442459 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.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\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; + +class LoginController extends ContainerAware +{ + public function loginAction() + { + $form = $this->container->get('form.factory')->create('user_login'); + + 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/UserLoginFormType.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginFormType.php new file mode 100644 index 0000000..21389ef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginFormType.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\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\Request; +use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * Form type for use with the Security component's form-based authentication + * listener. + * + * @author Henrik Bjornskov + * @author Jeremy Mikola + */ +class UserLoginFormType extends AbstractType +{ + private $request; + + /** + * @param Request $request A request instance + */ + public function __construct(Request $request) + { + $this->request = $request; + } + + /** + * @see Symfony\Component\Form\AbstractType::buildForm() + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('username', 'text') + ->add('password', 'password') + ->add('_target_path', 'hidden') + ; + + $request = $this->request; + + /* 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(SecurityContextInterface::AUTHENTICATION_ERROR)) { + $error = $request->attributes->get(SecurityContextInterface::AUTHENTICATION_ERROR); + } else { + $error = $request->getSession()->get(SecurityContextInterface::AUTHENTICATION_ERROR); + } + + if ($error) { + $event->getForm()->addError(new FormError($error->getMessage())); + } + + $event->setData(array_replace((array) $event->getData(), array( + 'username' => $request->getSession()->get(SecurityContextInterface::LAST_USERNAME), + ))); + }); + } + + /** + * @see Symfony\Component\Form\AbstractType::setDefaultOptions() + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + /* Note: the form's intention must correspond to that for the form login + * listener in order for the CSRF token to validate successfully. + */ + + $resolver->setDefaults(array( + 'intention' => 'authenticate', + )); + } + + /** + * @see Symfony\Component\Form\FormTypeInterface::getName() + */ + public function getName() + { + return 'user_login'; + } +} 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/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..381b13d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.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\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Controller; + +use Symfony\Component\Security\Core\SecurityContext; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\DependencyInjection\ContainerAware; + +class LocalizedController extends ContainerAware +{ + public function loginAction() + { + // get the login error if there is one + if ($this->container->get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) { + $error = $this->container->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR); + } else { + $error = $this->container->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR); + } + + return $this->container->get('templating')->renderResponse('FormLoginBundle:Localized:login.html.twig', array( + // last username entered by the user + 'last_username' => $this->container->get('request')->getSession()->get(SecurityContext::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..cec298c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.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\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Controller; + +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\SecurityContext; +use Symfony\Component\DependencyInjection\ContainerAware; + +class LoginController extends ContainerAware +{ + public function loginAction() + { + // get the login error if there is one + if ($this->container->get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) { + $error = $this->container->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR); + } else { + $error = $this->container->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR); + } + + return $this->container->get('templating')->renderResponse('FormLoginBundle:Login:login.html.twig', array( + // last username entered by the user + 'last_username' => $this->container->get('request')->getSession()->get(SecurityContext::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..016bf5a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/DependencyInjection/FormLoginExtension.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\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..535df35 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/routing.yml @@ -0,0 +1,39 @@ +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 } 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..9972b48 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig @@ -0,0 +1,6 @@ +{% extends "::base.html.twig" %} + +{% block body %} + Hello {{ app.user.username }}!

    + You're browsing to path "{{ app.request.pathInfo }}". +{% 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..4c4c1ac --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.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\Bundle\SecurityBundle\Tests\Functional; + +/** + * @group functional + */ +class CsrfFormLoginTest extends WebTestCase +{ + /** + * @dataProvider getConfigs + */ + public function testFormLoginAndLogoutWithCsrfTokens($config) + { + $client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config)); + $client->insulate(); + + $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)); + $client->insulate(); + + $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)); + $client->insulate(); + + $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->insulate(); + + $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'), + ); + } + + protected function setUp() + { + parent::setUp(); + + $this->deleteTmpDir('CsrfFormLogin'); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->deleteTmpDir('CsrfFormLogin'); + } +} 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..0dc038e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.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\SecurityBundle\Tests\Functional; + +/** + * @group functional + */ +class FormLoginTest extends WebTestCase +{ + /** + * @dataProvider getConfigs + */ + public function testFormLogin($config) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + $client->insulate(); + + $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 testFormLoginWithCustomTargetPath($config) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + $client->insulate(); + + $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->insulate(); + + $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'), + ); + } + + protected function setUp() + { + parent::setUp(); + + $this->deleteTmpDir('StandardFormLogin'); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->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..84c7905 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.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\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')); + $client->insulate(); + + $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')); + $client->insulate(); + + $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->insulate(); + + $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')); + $client->insulate(); + + $crawler = $client->request('GET', '/'.$locale.'/secure/'); + $this->assertCount(1, $crawler->selectButton('login'), (string) $client->getResponse()); + } + + public function getLocales() + { + return array(array('en'), array('de')); + } + + protected function setUp() + { + parent::setUp(); + + $this->deleteTmpDir('StandardFormLogin'); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->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..a0a1ca2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.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\SecurityBundle\Tests\Functional; + +class SecurityRoutingIntegrationTest extends WebTestCase +{ + /** + * @dataProvider getConfigs + */ + public function testRoutingErrorIsNotExposedForProtectedResourceWhenAnonymous($config) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + $client->insulate(); + $client->request('GET', '/protected_resource'); + + $this->assertRedirect($client->getResponse(), '/login'); + } + + /** + * @dataProvider getConfigs + */ + public function testRoutingErrorIsExposedWhenNotProtected($config) + { + if (strpos(PHP_OS, "WIN") === 0 && version_compare(phpversion(), "5.3.9", "<")) { + $this->markTestSkipped('Test hangs on Windows & PHP due to https://bugs.php.net/bug.php?id=60120 fixed in http://svn.php.net/viewvc?view=revision&revision=318366'); + } + + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + $client->insulate(); + $client->request('GET', '/unprotected_resource'); + + $this->assertEquals(404, $client->getResponse()->getStatusCode(), (string) $client->getResponse()); + } + + /** + * @dataProvider getConfigs + */ + public function testRoutingErrorIsNotExposedForProtectedResourceWhenLoggedInWithInsufficientRights($config) + { + if (strpos(PHP_OS, "WIN") === 0 && version_compare(phpversion(), "5.3.9", "<")) { + $this->markTestSkipped('Test hangs on Windows & PHP due to https://bugs.php.net/bug.php?id=60120 fixed in http://svn.php.net/viewvc?view=revision&revision=318366'); + } + + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + $client->insulate(); + + $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 + * @group ip_whitelist + */ + 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 + * @group ip_whitelist + */ + 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'); + } + + 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')); + } + + protected function setUp() + { + parent::setUp(); + + $this->deleteTmpDir('StandardFormLogin'); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->deleteTmpDir('StandardFormLogin'); + } +} 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..8c0c339 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.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\SecurityBundle\Tests\Functional; + +/** + * @group 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); + $client->insulate(); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['_username'] = $username; + $form['_password'] = 'test'; + $client->submit($form); + + return $client; + } + + protected function setUp() + { + parent::setUp(); + + $this->deleteTmpDir('StandardFormLogin'); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->deleteTmpDir('StandardFormLogin'); + } +} 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..b4b906f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.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\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 function setUp() + { + if (!class_exists('Twig_Environment')) { + $this->markTestSkipped('Twig is not available.'); + } + + parent::setUp(); + } + + 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\SecurityBundle\Tests\Functional\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/AppKernel.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php new file mode 100644 index 0000000..0979db4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.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\Bundle\SecurityBundle\Tests\Functional; + +// 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); + } + + 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 init() + { + } + + 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) + { + call_user_func_array(array($this, '__construct'), unserialize($str)); + } + + 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..cee883f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/bundles.php @@ -0,0 +1,8 @@ + + + + + {% 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..e4bbc08 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/bundles.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Twig\Extension; + +use Symfony\Bundle\SecurityBundle\Templating\Helper\LogoutUrlHelper; + +/** + * LogoutUrlHelper provides generator functions for the logout URL to Twig. + * + * @author Jeremy Mikola + */ +class LogoutUrlExtension extends \Twig_Extension +{ + private $helper; + + /** + * Constructor. + * + * @param LogoutUrlHelper $helper + */ + public function __construct(LogoutUrlHelper $helper) + { + $this->helper = $helper; + } + + /** + * @see Twig_Extension::getFunctions() + */ + public function getFunctions() + { + return array( + 'logout_url' => new \Twig_Function_Method($this, 'getLogoutUrl'), + 'logout_path' => new \Twig_Function_Method($this, 'getLogoutPath'), + ); + } + + /** + * Generate the relative logout URL for the firewall. + * + * @param string $key The firewall key + * @return string The relative logout URL + */ + public function getLogoutPath($key) + { + return $this->helper->getLogoutPath($key); + } + + /** + * Generate the absolute logout URL for the firewall. + * + * @param string $key The firewall key + * @return string The absolute logout URL + */ + public function getLogoutUrl($key) + { + return $this->helper->getLogoutUrl($key); + } + + /** + * @see Twig_ExtensionInterface::getName() + */ + public function getName() + { + return 'logout_url'; + } +} 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..776f590 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/composer.json @@ -0,0 +1,40 @@ +{ + "name": "symfony/security-bundle", + "type": "symfony-bundle", + "description": "Symfony SecurityBundle", + "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.3", + "symfony/security": "~2.2", + "symfony/http-kernel": "~2.2" + }, + "require-dev": { + "symfony/framework-bundle": "~2.2", + "symfony/twig-bundle": "~2.2", + "symfony/form": "~2.1", + "symfony/validator": "~2.2", + "symfony/yaml": "~2.0" + }, + "autoload": { + "psr-0": { "Symfony\\Bundle\\SecurityBundle\\": "" } + }, + "target-dir": "Symfony/Bundle/SecurityBundle", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..2cffc1e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./Resources + ./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..de36165 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/CHANGELOG.md @@ -0,0 +1,24 @@ +CHANGELOG +========= + +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..dfd9143 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.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\CacheWarmer; + +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinderInterface; + +/** + * 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; + + /** + * Constructor. + * + * @param ContainerInterface $container The dependency injection container + * @param TemplateFinderInterface $finder The template paths cache warmer + */ + public function __construct(ContainerInterface $container, TemplateFinderInterface $finder) + { + // 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 + $this->container = $container; + $this->finder = $finder; + } + + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir) + { + $twig = $this->container->get('twig'); + + foreach ($this->finder->findAllTemplates() 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 Boolean always true + */ + public function isOptional() + { + return true; + } +} 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..aa01ce6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Command/LintCommand.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\TwigBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Finder\Finder; + +/** + * Command that will validate your template syntax and output encountered errors. + * + * @author Marc Weistroff + */ +class LintCommand extends ContainerAwareCommand +{ + protected function configure() + { + $this + ->setName('twig:lint') + ->setDescription('Lints a template and outputs encountered errors') + ->addArgument('filename') + ->setHelp(<<%command.name% command lints a template and outputs to stdout +the first encountered syntax error. + +php %command.full_name% filename + +The command gets the contents of filename and validates its syntax. + +php %command.full_name% dirname + +The command finds all twig templates in dirname and validates the syntax +of each Twig template. + +php %command.full_name% @AcmeMyBundle + +The command finds all twig templates in the AcmeMyBundle bundle and validates +the syntax of each Twig template. + +cat filename | php %command.full_name% + +The command gets the template contents from stdin and validates its syntax. +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $twig = $this->getContainer()->get('twig'); + $template = null; + $filename = $input->getArgument('filename'); + + if (!$filename) { + if (0 !== ftell(STDIN)) { + throw new \RuntimeException("Please provide a filename or pipe template content to stdin."); + } + + while (!feof(STDIN)) { + $template .= fread(STDIN, 1024); + } + + return $this->validateTemplate($twig, $output, $template); + } + + 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('*.twig'); + } else { + $dir = $this->getApplication()->getKernel()->locateResource($filename); + $files = Finder::create()->files()->in($dir)->name('*.twig'); + } + + $errors = 0; + foreach ($files as $file) { + $errors += $this->validateTemplate($twig, $output, file_get_contents($file), $file); + } + + return $errors > 0 ? 1 : 0; + } + + protected function validateTemplate(\Twig_Environment $twig, OutputInterface $output, $template, $file = null) + { + try { + $twig->parse($twig->tokenize($template, $file ? (string) $file : null)); + $output->writeln('OK'.($file ? sprintf(' in %s', $file) : '')); + } catch (\Twig_Error $e) { + $this->renderException($output, $template, $e, $file); + + return 1; + } + + return 0; + } + + protected function renderException(OutputInterface $output, $template, \Twig_Error $exception, $file = null) + { + $line = $exception->getTemplateLine(); + $lines = $this->getContext($template, $line); + + if ($file) { + $output->writeln(sprintf("KO in %s (line %s)", $file, $line)); + } else { + $output->writeln(sprintf("KO (line %s)", $line)); + } + + foreach ($lines as $no => $code) { + $output->writeln(sprintf( + "%s %-6s %s", + $no == $line ? '>>' : ' ', + $no, + $code + )); + if ($no == $line) { + $output->writeln(sprintf('>> %s ', $exception->getRawMessage())); + } + } + } + + protected 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/Bundle/TwigBundle/Controller/ExceptionController.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php new file mode 100644 index 0000000..3c2d14d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.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\TwigBundle\Controller; + +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Component\HttpKernel\Exception\FlattenException; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * ExceptionController. + * + * @author Fabien Potencier + */ +class ExceptionController +{ + protected $twig; + protected $debug; + + public function __construct(\Twig_Environment $twig, $debug) + { + $this->twig = $twig; + $this->debug = $debug; + } + + /** + * Converts an Exception to a Response. + * + * @param Request $request The request + * @param FlattenException $exception A FlattenException instance + * @param DebugLoggerInterface $logger A DebugLoggerInterface instance + * @param string $format The format to use for rendering (html, xml, ...) + * + * @return Response + * + * @throws \InvalidArgumentException When the exception template does not exist + */ + public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null, $format = 'html') + { + $request->setRequestFormat($format); + + $currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1)); + + $code = $exception->getStatusCode(); + + return new Response($this->twig->render( + $this->findTemplate($request, $format, $code, $this->debug), + array( + 'status_code' => $code, + 'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '', + 'exception' => $exception, + 'logger' => $logger, + 'currentContent' => $currentContent, + ) + )); + } + + /** + * @param integer $startObLevel + * + * @return string + */ + protected function getAndCleanOutputBuffering($startObLevel) + { + // ob_get_level() never returns 0 on some Windows configurations, so if + // the level is the same two times in a row, the loop should be stopped. + $previousObLevel = null; + $currentContent = ''; + + while (($obLevel = ob_get_level()) > $startObLevel && $obLevel !== $previousObLevel) { + $previousObLevel = $obLevel; + $currentContent .= ob_get_clean(); + } + + return $currentContent; + } + + /** + * @param Request $request + * @param string $format + * @param integer $code An HTTP response status code + * @param Boolean $debug + * + * @return TemplateReference + */ + protected function findTemplate(Request $request, $format, $code, $debug) + { + $name = $debug ? 'exception' : 'error'; + if ($debug && 'html' == $format) { + $name = 'exception_full'; + } + + // when not in debug, try to find a template for the specific HTTP status code and format + if (!$debug) { + $template = new TemplateReference('TwigBundle', 'Exception', $name.$code, $format, 'twig'); + if ($this->templateExists($template)) { + return $template; + } + } + + // try to find a template for the given format + $template = new TemplateReference('TwigBundle', 'Exception', $name, $format, 'twig'); + if ($this->templateExists($template)) { + return $template; + } + + // default to a generic HTML exception + $request->setRequestFormat('html'); + + return new TemplateReference('TwigBundle', 'Exception', $name, '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/TwigBundle/Debug/TimedTwigEngine.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Debug/TimedTwigEngine.php new file mode 100644 index 0000000..33178e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Debug/TimedTwigEngine.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\TwigBundle\Debug; + +use Symfony\Bundle\TwigBundle\TwigEngine; +use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables; +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\Config\FileLocatorInterface; + +/** + * Times the time spent to render a template. + * + * @author Fabien Potencier + */ +class TimedTwigEngine extends TwigEngine +{ + protected $stopwatch; + + /** + * Constructor. + * + * @param \Twig_Environment $environment A \Twig_Environment instance + * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + * @param FileLocatorInterface $locator A FileLocatorInterface instance + * @param Stopwatch $stopwatch A Stopwatch instance + * @param GlobalVariables $globals A GlobalVariables instance + */ + public function __construct(\Twig_Environment $environment, TemplateNameParserInterface $parser, FileLocatorInterface $locator, Stopwatch $stopwatch, GlobalVariables $globals = null) + { + parent::__construct($environment, $parser, $locator, $globals); + + $this->stopwatch = $stopwatch; + } + + /** + * {@inheritdoc} + */ + public function render($name, array $parameters = array()) + { + $e = $this->stopwatch->start(sprintf('template.twig (%s)', $name), 'template'); + + $ret = parent::render($name, $parameters); + + $e->stop(); + + return $ret; + } +} 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..18d98b3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExceptionListenerPass.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\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 + $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..069083d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * @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'); + } + } +} 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..785c597 --- /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..075177d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigLoaderPass.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\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'); + foreach (array_keys($loaderIds) as $id) { + $chainLoader->addMethodCall('addLoader', array(new Reference($id))); + } + $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..083eb0c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.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\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 \Symfony\Component\Config\Definition\Builder\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->addFormSection($rootNode); + $this->addGlobalsSection($rootNode); + $this->addTwigOptions($rootNode); + + return $treeBuilder; + } + + private function addFormSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('form') + ->addDefaultsIfNotSet() + ->fixXmlConfig('resource') + ->children() + ->arrayNode('resources') + ->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() + ->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){ 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() + ->scalarNode('autoescape')->end() + ->scalarNode('autoescape_service')->defaultNull()->end() + ->scalarNode('autoescape_service_method')->defaultNull()->end() + ->scalarNode('base_template_class')->example('Twig_Template')->end() + ->scalarNode('cache')->defaultValue('%kernel.cache_dir%/twig')->end() + ->scalarNode('charset')->defaultValue('%kernel.charset%')->end() + ->scalarNode('debug')->defaultValue('%kernel.debug%')->end() + ->scalarNode('strict_variables')->end() + ->scalarNode('auto_reload')->end() + ->scalarNode('optimizations')->end() + ->arrayNode('paths') + ->normalizeKeys(false) + ->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() + ; + } +} 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..e8fec8d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.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\TwigBundle\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +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 &$config) { + if (isset($config['globals'])) { + foreach ($config['globals'] as $name => $value) { + if (is_array($value) && isset($value['key'])) { + $config['globals'][$name] = array( + 'key' => $name, + 'value' => $config['globals'][$name] + ); + } + } + } + } + + $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']['resources']); + + $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)); + } + } + + // register bundles as Twig namespaces + foreach ($container->getParameter('kernel.bundles') as $bundle => $class) { + if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/views')) { + $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); + } + + $reflection = new \ReflectionClass($class); + if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/views')) { + $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); + } + } + + if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/views')) { + $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($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 ($container->getParameter('kernel.debug')) { + $loader->load('debug.xml'); + + $container->setDefinition('templating.engine.twig', $container->findDefinition('debug.templating.engine.twig')); + $container->setAlias('debug.templating.engine.twig', 'templating.engine.twig'); + } + + if (isset($config['autoescape_service']) && isset($config['autoescape_service_method'])) { + $container->findDefinition('templating.engine.twig')->addMethodCall('setDefaultEscapingStrategy', array(array(new Reference($config['autoescape_service']), $config['autoescape_service_method']))); + + unset($config['autoescape_service'], $config['autoescape_service_method']); + } elseif (!isset($config['autoescape'])) { + $container->findDefinition('templating.engine.twig')->addMethodCall('setDefaultEscapingStrategy', array(array(new Reference('templating.engine.twig'), 'guessDefaultEscapingStrategy'))); + } + + $container->setParameter('twig.options', $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/Extension/ActionsExtension.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Extension/ActionsExtension.php new file mode 100644 index 0000000..4f88c6c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Extension/ActionsExtension.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\TwigBundle\Extension; + +use Symfony\Bundle\TwigBundle\TokenParser\RenderTokenParser; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Twig extension for Symfony actions helper + * + * @author Fabien Potencier + */ +class ActionsExtension extends \Twig_Extension +{ + private $container; + + /** + * Constructor. + * + * @param ContainerInterface $container The service container + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Returns the Response content for a given URI. + * + * @param string $uri A URI + * @param array $options An array of options + * + * @see Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver::render() + */ + public function renderUri($uri, array $options = array()) + { + return $this->container->get('templating.helper.actions')->render($uri, $options); + } + + /** + * Returns the token parser instance to add to the existing list. + * + * @return array An array of Twig_TokenParser instances + */ + public function getTokenParsers() + { + return array( + // {% render url('post_list', { 'limit': 2 }), { 'alt': 'BlogBundle:Post:error' } %} + new RenderTokenParser(), + ); + } + + public function getName() + { + return 'actions'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Extension/AssetsExtension.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Extension/AssetsExtension.php new file mode 100644 index 0000000..29bb2ec --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Extension/AssetsExtension.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\Extension; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Twig extension for Symfony assets helper + * + * @author Fabien Potencier + */ +class AssetsExtension extends \Twig_Extension +{ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Returns a list of functions to add to the existing list. + * + * @return array An array of functions + */ + public function getFunctions() + { + return array( + 'asset' => new \Twig_Function_Method($this, 'getAssetUrl'), + 'assets_version' => new \Twig_Function_Method($this, 'getAssetsVersion'), + ); + } + + /** + * Returns the public path of an asset. + * + * 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 getAssetUrl($path, $packageName = null) + { + return $this->container->get('templating.helper.assets')->getUrl($path, $packageName); + } + + /** + * Returns the version of the assets in a package. + * + * @param string $packageName + * + * @return int + */ + public function getAssetsVersion($packageName = null) + { + return $this->container->get('templating.helper.assets')->getVersion($packageName); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'assets'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/LICENSE b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/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..64d5616 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.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\TwigBundle\Loader; + +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Config\FileLocatorInterface; + +/** + * FilesystemLoader extends the default Twig filesystem loader + * to work with the Symfony2 paths. + * + * @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; + $this->cache = array(); + } + + /** + * 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) + { + $logicalName = (string) $template; + + if (isset($this->cache[$logicalName])) { + return $this->cache[$logicalName]; + } + + $file = null; + $previous = null; + try { + $file = parent::findTemplate($template); + } catch (\Twig_Error_Loader $e) { + $previous = $e; + + // for BC + try { + $template = $this->parser->parse($template); + try { + $file = $this->locator->locate($template); + } catch (\InvalidArgumentException $e) { + $previous = $e; + } + } 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/Node/RenderNode.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Node/RenderNode.php new file mode 100644 index 0000000..7f4c385 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Node/RenderNode.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\Node; + +/** + * Represents a render node. + * + * @author Fabien Potencier + */ +class RenderNode extends \Twig_Node +{ + public function __construct(\Twig_Node_Expression $expr, \Twig_Node_Expression $options, $lineno, $tag = null) + { + parent::__construct(array('expr' => $expr, 'options' => $options), 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("echo \$this->env->getExtension('actions')->renderUri(") + ->subcompile($this->getNode('expr')) + ->raw(', ') + ->subcompile($this->getNode('options')) + ->raw(");\n") + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml new file mode 100644 index 0000000..2857e18 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml @@ -0,0 +1,24 @@ + + + + + + Symfony\Bundle\TwigBundle\Debug\TimedTwigEngine + + + + + + + + + + + + + + + + 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..a75f645 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..971f4f1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml @@ -0,0 +1,124 @@ + + + + + + Twig_Environment + Symfony\Bundle\TwigBundle\Loader\FilesystemLoader + Twig_Loader_Chain + Symfony\Bundle\TwigBundle\TwigEngine + Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheCacheWarmer + Symfony\Bridge\Twig\Extension\TranslationExtension + Symfony\Bundle\TwigBundle\Extension\AssetsExtension + Symfony\Bundle\TwigBundle\Extension\ActionsExtension + Symfony\Bridge\Twig\Extension\CodeExtension + Symfony\Bridge\Twig\Extension\RoutingExtension + Symfony\Bridge\Twig\Extension\YamlExtension + Symfony\Bridge\Twig\Extension\FormExtension + Symfony\Bridge\Twig\Extension\HttpKernelExtension + Symfony\Bridge\Twig\Form\TwigRendererEngine + Symfony\Bridge\Twig\Form\TwigRenderer + Symfony\Bridge\Twig\Translation\TwigExtractor + Symfony\Component\HttpKernel\EventListener\ExceptionListener + Symfony\Bundle\TwigBundle\Controller\ExceptionController + + + + + + %twig.options% + + app + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %templating.helper.code.file_link_format% + %kernel.root_dir% + %kernel.charset% + + + + + + + + + + + + + + + + + + + + + %twig.form.resources% + + + + + + + + + + + + + + + + %twig.exception_listener.controller% + + + + + + %kernel.debug% + + + 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..9c1ab3e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.atom.twig @@ -0,0 +1 @@ +{% include 'TwigBundle:Exception:error.xml.twig' with { 'exception': exception } %} 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..22d0c3a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.html.twig @@ -0,0 +1,17 @@ + + + + + An Error Occurred: {{ status_text }} + + +

    Oops! An Error Occurred

    +

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

    + +
    + Something is broken. Please e-mail us at [email] and 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..9c1ab3e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.rdf.twig @@ -0,0 +1 @@ +{% include 'TwigBundle:Exception:error.xml.twig' with { 'exception': exception } %} 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..b621b08 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.txt.twig @@ -0,0 +1,8 @@ +Oops! An Error Occurred +======================= + +The server returned a "{{ status_code }} {{ status_text }}". + +Please e-mail us at [email] and 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..989740f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.atom.twig @@ -0,0 +1 @@ +{% include 'TwigBundle: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..870d4a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.css.twig @@ -0,0 +1,3 @@ +/* +{% include 'TwigBundle: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..f09ffb3 --- /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 'TwigBundle: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 'TwigBundle:Exception:logs.html.twig' with { 'logs': logger.logs } only %} +
    +
    +{% endif %} + +{% if currentContent %} +
    + {% spaceless %} +

    + Content of the Output  + + + + + +

    + {% endspaceless %} + + + +
    +
    +{% endif %} + +{% include 'TwigBundle: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..870d4a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.js.twig @@ -0,0 +1,3 @@ +/* +{% include 'TwigBundle: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..042e082 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.json.twig @@ -0,0 +1 @@ +{{ 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..989740f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.rdf.twig @@ -0,0 +1 @@ +{% include 'TwigBundle: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..3c7a912 --- /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 'TwigBundle: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..fa99d44 --- /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 'TwigBundle: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..1920b60 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception_full.html.twig @@ -0,0 +1,13 @@ +{% extends 'TwigBundle::layout.html.twig' %} + +{% block head %} + +{% endblock %} + +{% block title %} + {{ exception.message }} ({{ status_code }} {{ status_text }}) +{% endblock %} + +{% block body %} + {% include 'TwigBundle: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..cf49082 --- /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 'TwigBundle: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..2cb3ba4 --- /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 'TwigBundle: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..133a626 --- /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 'TwigBundle: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..3ea3d7b --- /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..49f997b --- /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/Tests/Controller/ExceptionControllerTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php new file mode 100644 index 0000000..18523ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.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\Tests\Controller; + +use Symfony\Bundle\TwigBundle\Tests\TestCase; +use Symfony\Bundle\TwigBundle\Controller\ExceptionController; +use Symfony\Component\HttpFoundation\Request; + +class ExceptionControllerTest extends TestCase +{ + public function testOnlyClearOwnOutputBuffers() + { + $flatten = $this->getMock('Symfony\Component\HttpKernel\Exception\FlattenException'); + $flatten + ->expects($this->once()) + ->method('getStatusCode') + ->will($this->returnValue(404)); + $twig = $this->getMockBuilder('\Twig_Environment') + ->disableOriginalConstructor() + ->getMock(); + $twig + ->expects($this->any()) + ->method('render') + ->will($this->returnValue($this->getMock('Symfony\Component\HttpFoundation\Response'))); + $twig + ->expects($this->any()) + ->method('getLoader') + ->will($this->returnValue($this->getMock('\Twig_LoaderInterface'))); + $request = Request::create('/'); + $request->headers->set('X-Php-Ob-Level', 1); + + $controller = new ExceptionController($twig, false); + $controller->showAction($request, $flatten); + } +} 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..21486a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigLoaderPassTest.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\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigLoaderPass; + +class TwigLoaderPassTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + $this->builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $this->chainLoader = new Definition('loader'); + $this->pass = new TwigLoaderPass(); + } + + public function testMapperPassWithOneTaggedLoaders() + { + $serviceIds = array( + 'test_loader_1' => 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( + ), + 'test_loader_2' => 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->assertEquals(2, count($calls)); + $this->assertEquals('addLoader', $calls[0][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/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..9e1d405 --- /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/full.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/full.php new file mode 100644 index 0000000..bad71a3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/full.php @@ -0,0 +1,27 @@ +loadFromExtension('twig', array( + 'form' => array( + 'resources' => array( + 'MyBundle::form.html.twig', + ) + ), + 'globals' => array( + 'foo' => '@bar', + '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' => 'namespace', + 'namespaced_path2' => 'namespace', + ), +)); 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/full.xml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/full.xml new file mode 100644 index 0000000..0d3c053 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -0,0 +1,20 @@ + + + + + + + MyBundle::form.html.twig + + + 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/full.yml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/full.yml new file mode 100644 index 0000000..afc1461 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -0,0 +1,20 @@ +twig: + form: + resources: + - MyBundle::form.html.twig + globals: + foo: "@bar" + 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: namespace + namespaced_path2: namespace 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..225ecfd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php @@ -0,0 +1,232 @@ + + * + * 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->getParameter('twig.class'), '->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->getParameter('twig.options'); + $this->assertEquals(__DIR__.'/twig', $options['cache'], '->load() sets default value for cache option'); + $this->assertEquals('UTF-8', $options['charset'], '->load() sets default value for charset option'); + $this->assertFalse($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->getParameter('twig.class'), '->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('templating.globals'), $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('pi', $calls[2][1][0], '->load() registers variables as Twig globals'); + $this->assertEquals(3.14, $calls[2][1][1], '->load() registers variables as Twig globals'); + + // Yaml and Php specific configs + if (in_array($format, array('yml', 'php'))) { + $this->assertEquals('bad', $calls[3][1][0], '->load() registers variables as Twig globals'); + $this->assertEquals(array('key' => 'foo'), $calls[3][1][1], '->load() registers variables as Twig globals'); + } + + // Twig options + $options = $container->getParameter('twig.options'); + $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); + + $this->assertTemplateEscapingGuesserDefinition($container, 'my_project.some_bundle.template_escaping_guesser', 'guess'); + } + + /** + * @dataProvider getFormats + */ + public function testLoadDefaultTemplateEscapingGuesserConfiguration($format) + { + $container = $this->createContainer(); + $container->registerExtension(new TwigExtension()); + $this->loadFromFile($container, 'empty', $format); + $this->compileContainer($container); + + $this->assertTemplateEscapingGuesserDefinition($container, 'templating.engine.twig', 'guessDefaultEscapingStrategy'); + } + + 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->compileContainer($container); + + $def = $container->getDefinition('twig.loader.filesystem'); + $paths = array(); + foreach ($def->getMethodCalls() as $call) { + if ('addPath' === $call[0]) { + if (false === strpos($call[1][0], 'Form')) { + $paths[] = $call[1]; + } + } + } + + $this->assertEquals(array( + array('path1'), + array('path2'), + array('namespaced_path1', 'namespace'), + array('namespaced_path2', 'namespace'), + 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'), + ); + } + + 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); + } + + private function assertTemplateEscapingGuesserDefinition(ContainerBuilder $container, $serviceId, $serviceMethod) + { + $def = $container->getDefinition('templating.engine.twig'); + + $this->assertCount(1, $def->getMethodCalls()); + + foreach ($def->getMethodCalls() as $call) { + if ('setDefaultEscapingStrategy' === $call[0]) { + $this->assertSame($serviceId, (string) $call[1][0][0]); + $this->assertSame($serviceMethod, $call[1][0][1]); + } + } + } +} 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..8bb1c16 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.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\Bundle\TwigBundle\Tests\Loader; + +use Symfony\Bundle\TwigBundle\Tests\TestCase; +use Symfony\Bundle\TwigBundle\Loader\FilesystemLoader; +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Component\Templating\TemplateNameParserInterface; + +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')); + } + + /** + * @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/TestCase.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/TestCase.php new file mode 100644 index 0000000..a3848ef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/TestCase.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\TwigBundle\Tests; + +class TestCase extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Twig_Environment')) { + $this->markTestSkipped('Twig is not available.'); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/TokenParser/RenderTokenParserTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/TokenParser/RenderTokenParserTest.php new file mode 100644 index 0000000..9823a98 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/TokenParser/RenderTokenParserTest.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\TokenParser; + +use Symfony\Bundle\TwigBundle\Tests\TestCase; +use Symfony\Bundle\TwigBundle\TokenParser\RenderTokenParser; +use Symfony\Bundle\TwigBundle\Node\RenderNode; + +class RenderTokenParserTest extends TestCase +{ + /** + * @dataProvider getTestsForRender + */ + public function testCompile($source, $expected) + { + $env = new \Twig_Environment(new \Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); + $env->addTokenParser(new RenderTokenParser()); + $stream = $env->tokenize($source); + $parser = new \Twig_Parser($env); + + $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)); + } + + public function getTestsForRender() + { + return array( + array( + '{% render "foo" %}', + new RenderNode( + new \Twig_Node_Expression_Constant('foo', 1), + new \Twig_Node_Expression_Array(array(), 1), + 1, + 'render' + ) + ), + array( + '{% render "foo", {foo: 1} %}', + new RenderNode( + new \Twig_Node_Expression_Constant('foo', 1), + new \Twig_Node_Expression_Array(array( + new \Twig_Node_Expression_Constant('foo', 1), + new \Twig_Node_Expression_Constant('1', 1), + ), 1), + 1, + 'render' + ) + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TokenParser/RenderTokenParser.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TokenParser/RenderTokenParser.php new file mode 100644 index 0000000..fc53be4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TokenParser/RenderTokenParser.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\TwigBundle\TokenParser; + +use Symfony\Bundle\TwigBundle\Node\RenderNode; + +/** + * Token Parser for the render tag. + * + * @author Fabien Potencier + */ +class RenderTokenParser extends \Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param \Twig_Token $token A \Twig_Token instance + * + * @return \Twig_NodeInterface A \Twig_NodeInterface instance + */ + public function parse(\Twig_Token $token) + { + $expr = $this->parser->getExpressionParser()->parseExpression(); + + // options + if ($this->parser->getStream()->test(\Twig_Token::PUNCTUATION_TYPE, ',')) { + $this->parser->getStream()->next(); + + $options = $this->parser->getExpressionParser()->parseExpression(); + } else { + $options = new \Twig_Node_Expression_Array(array(), $token->getLine()); + } + + $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE); + + return new RenderNode($expr, $options, $token->getLine(), $this->getTag()); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'render'; + } +} 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..fbb8602 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TwigEngine.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\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; + } + + public function setDefaultEscapingStrategy($strategy) + { + $this->environment->getExtension('escaper')->setDefaultStrategy($strategy); + } + + public function guessDefaultEscapingStrategy($filename) + { + // remove .twig + $filename = substr($filename, 0, -5); + + // get the format + $format = substr($filename, strrpos($filename, '.') + 1); + + if ('js' === $format) { + return 'js'; + } + + return 'html'; + } + + /** + * Renders a template. + * + * @param mixed $name A template name + * @param array $parameters An array of parameters to pass to the template + * + * @return string The evaluated template as a string + * + * @throws \InvalidArgumentException if the template does not exist + * @throws \RuntimeException if the template cannot be rendered + * @throws \Twig_Error + */ + 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 $ex) { + } + } + + throw $e; + } + } + + /** + * 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 + */ + 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..65933fc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/twig-bundle", + "type": "symfony-bundle", + "description": "Symfony TwigBundle", + "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.3", + "symfony/twig-bridge": "~2.2", + "symfony/http-kernel": "~2.1" + }, + "require-dev": { + "symfony/stopwatch": "~2.2", + "symfony/dependency-injection": "~2.0", + "symfony/config": "~2.2" + }, + "autoload": { + "psr-0": { "Symfony\\Bundle\\TwigBundle\\": "" } + }, + "target-dir": "Symfony/Bundle/TwigBundle", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..56e10bd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./Resources + ./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..a27cdd8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md @@ -0,0 +1,19 @@ +CHANGELOG +========= + +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..c89f346 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.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\Bundle\WebProfilerBundle\Controller; + +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpKernel\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 + */ + 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(); + + return new Response($handler->getContent($exception)); + } + + $code = $exception->getStatusCode(); + + return new Response($this->twig->render( + $template, + array( + 'status_code' => $code, + 'status_text' => Response::$statusTexts[$code], + 'exception' => $exception, + 'logger' => null, + 'currentContent' => '', + ) + )); + } + + /** + * Renders the exception panel stylesheet for the given token. + * + * @param string $token The profiler token + * + * @return Response A Response instance + */ + 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(); + + return new Response($handler->getStylesheet($exception)); + } + + return new Response($this->twig->render('@WebProfiler/Collector/exception.css.twig')); + } + + 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..31bda03 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php @@ -0,0 +1,422 @@ + + * + * 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\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Bundle\WebProfilerBundle\Profiler\TemplateManager; +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 + */ + 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))); + } + + /** + * 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 (!$profile = $this->profiler->loadProfile($token)) { + return new Response($this->twig->render('@WebProfiler/Profiler/info.html.twig', array('about' => 'no_token', 'token' => $token))); + } + + 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(), + ))); + } + + /** + * Exports data for a given token. + * + * @param string $token The profiler token + * + * @return Response A Response instance + * + * @throws NotFoundHttpException + */ + public function exportAction($token) + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + if (!$profile = $this->profiler->loadProfile($token)) { + throw new NotFoundHttpException(sprintf('Token "%s" does not exist.', $token)); + } + + return new Response($this->profiler->export($profile), 200, array( + 'Content-Type' => 'text/plain', + 'Content-Disposition' => 'attachment; filename= '.$token.'.txt', + )); + } + + /** + * Purges all tokens. + * + * @return Response A Response instance + */ + public function purgeAction() + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + $this->profiler->purge(); + + return new RedirectResponse($this->generator->generate('_profiler_info', array('about' => 'purge'))); + } + + /** + * Imports token data. + * + * @param Request $request The current HTTP Request + * + * @return Response A Response instance + */ + public function importAction(Request $request) + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + $file = $request->files->get('file'); + + if (empty($file) || !$file->isValid()) { + return new RedirectResponse($this->generator->generate('_profiler_info', array('about' => 'upload_error'))); + } + + if (!$profile = $this->profiler->import(file_get_contents($file->getPathname()))) { + return new RedirectResponse($this->generator->generate('_profiler_info', array('about' => 'already_exists'))); + } + + return new RedirectResponse($this->generator->generate('_profiler', array('token' => $profile->getToken()))); + } + + /** + * Displays information page. + * + * @param string $about The about message + * + * @return Response A Response instance + */ + public function infoAction($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 + ))); + } + + /** + * Renders the Web Debug Toolbar. + * + * @param Request $request The current HTTP Request + * @param string $token The profiler token + * + * @return Response A Response instance + */ + 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->getFlashBag() instanceof AutoExpireFlashBag) { + // keep current flashes for one more request if using AutoExpireFlashBag + $session->getFlashBag()->setAll($session->getFlashBag()->peekAll()); + } + + if (null === $token) { + return new Response(); + } + + $this->profiler->disable(); + + if (!$profile = $this->profiler->loadProfile($token)) { + return new Response(); + } + + // 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( + 'position' => $position, + 'profile' => $profile, + 'templates' => $this->getTemplateManager()->getTemplates($profile), + 'profiler_url' => $url, + 'token' => $token, + ))); + } + + /** + * Renders the profiler search bar. + * + * @param Request $request The current HTTP Request + * + * @return Response A Response instance + */ + 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 = $session->get('_profiler_search_ip'); + $method = $session->get('_profiler_search_method'); + $url = $session->get('_profiler_search_url'); + $start = $session->get('_profiler_search_start'); + $end = $session->get('_profiler_search_end'); + $limit = $session->get('_profiler_search_limit'); + $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, + ))); + } + + /** + * Search results. + * + * @param Request $request The current HTTP Request + * @param string $token The token + * + * @return Response A Response instance + */ + 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( + '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, + ))); + } + + /** + * Narrow the search bar. + * + * @param Request $request The current HTTP Request + * + * @return Response A Response instance + */ + 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))); + } + + $tokens = $this->profiler->find($ip, $url, $limit, $method, $start, $end); + + return new RedirectResponse($this->generator->generate('_profiler_search_results', array( + 'token' => $tokens ? $tokens[0]['token'] : 'empty', + 'ip' => $ip, + 'method' => $method, + 'url' => $url, + 'start' => $start, + 'end' => $end, + 'limit' => $limit, + ))); + } + + /** + * Displays the PHP info. + * + * @return Response A Response instance + */ + 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); + } + + /** + * 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..55fd7d1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.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\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\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 = $routes; + + if (null === $this->routes && $this->matcher instanceof RouterInterface) { + $this->routes = $matcher->getRouteCollection(); + } + } + + /** + * Renders the profiler panel for the given token. + * + * @param string $token The profiler token + * + * @return Response A Response instance + */ + 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.'); + } + + $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()), + ))); + } +} 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..ece674c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.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\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 \Symfony\Component\Config\Definition\Builder\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() + ->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..e4b4cb7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.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\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'); + $loader->load('toolbar.xml'); + + $container->setParameter('web_profiler.debug_toolbar.intercept_redirects', $config['intercept_redirects']); + + if (!$config['toolbar']) { + $mode = WebDebugToolbarListener::DISABLED; + } else { + $mode = WebDebugToolbarListener::ENABLED; + } + + $container->setParameter('web_profiler.debug_toolbar.mode', $mode); + $container->setParameter('web_profiler.debug_toolbar.position', $config['position']); + } + + /** + * 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..1e0f691 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.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\WebProfilerBundle\EventListener; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * 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 $interceptRedirects; + protected $mode; + protected $position; + + public function __construct(\Twig_Environment $twig, $interceptRedirects = false, $mode = self::ENABLED, $position = 'bottom') + { + $this->twig = $twig; + $this->interceptRedirects = (Boolean) $interceptRedirects; + $this->mode = (integer) $mode; + $this->position = $position; + } + + public function isEnabled() + { + return self::DISABLED !== $this->mode; + } + + public function onKernelResponse(FilterResponseEvent $event) + { + if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + return; + } + + $response = $event->getResponse(); + $request = $event->getRequest(); + + // 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 ($session && $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); + } + + /** + * Injects the web debug toolbar into the given Response. + * + * @param Response $response A Response instance + */ + protected function injectToolbar(Response $response) + { + if (function_exists('mb_stripos')) { + $posrFunction = 'mb_strripos'; + $substrFunction = 'mb_substr'; + } else { + $posrFunction = 'strripos'; + $substrFunction = 'substr'; + } + + $content = $response->getContent(); + $pos = $posrFunction($content, ''); + + if (false !== $pos) { + $toolbar = "\n".str_replace("\n", '', $this->twig->render( + '@WebProfiler/Profiler/toolbar_js.html.twig', + array( + 'position' => $this->position, + 'token' => $response->headers->get('X-Debug-Token'), + ) + ))."\n"; + $content = $substrFunction($content, 0, $pos).$toolbar.$substrFunction($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/LICENSE b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/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..63032ec --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.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\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 100644 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..874ba8b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml @@ -0,0 +1,34 @@ + + + + + + Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController + Symfony\Bundle\WebProfilerBundle\Controller\RouterController + Symfony\Bundle\WebProfilerBundle\Controller\ExceptionController + + + + + + + + %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..1819e48 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml @@ -0,0 +1,59 @@ + + + + + + web_profiler.controller.profiler:homeAction + + + + web_profiler.controller.profiler:searchAction + + + + web_profiler.controller.profiler:searchBarAction + + + + web_profiler.controller.profiler:purgeAction + + + + web_profiler.controller.profiler:infoAction + + + + web_profiler.controller.profiler:importAction + + + + web_profiler.controller.profiler:exportAction + + + + 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..5f6851c --- /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..1372b0a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml @@ -0,0 +1,20 @@ + + + + + + Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener + + + + + + + %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/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..cdb7b8f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig @@ -0,0 +1,204 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {# Symfony Logo #} + {% set icon %} + + Symfony + + {% if collector.applicationname %} + {{ collector.applicationname }} {{ collector.applicationversion }} + {% else %} + {{ collector.symfonyversion }} + {% endif %} + + + {% endset %} + {% set text %} + {% if collector.applicationname %} +
    + {{ collector.applicationname }} {{ collector.applicationversion }} +
    + {% endif %} +
    + Symfony {{ collector.symfonyversion }} +
    + + {% endset %} + {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': false } %} + + {# PHP Information #} + {% set icon %} + + PHP + + {% endset %} + {% set text %} + {% spaceless %} +
    + PHP + {{ collector.phpversion }} +
    +
    + PHP Extensions + xdebug + accel +
    +
    + PHP SAPI + {{ collector.sapiName }} +
    + {% endspaceless %} + {% endset %} + {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': false } %} + + {# Environment #} + {% set debug_status_class %}sf-toolbar-status sf-toolbar-status-{{ collector.debug ? 'green' : 'red' }}{% endset %} + {% set icon %} + Environment + + {{ token }} + {% if 'n/a' != collector.appname or 'n/a' != collector.env %} + + {{ collector.appname }} + {{ collector.env }} + + {% endif %} + {% endset %} + {% set text %} + {% spaceless %} + {% if 'n/a' != collector.appname %} +
    + Name + {{ collector.appname }} +
    + {% endif %} + {% if 'n/a' != collector.env %} +
    + Environment + {{ collector.env }} +
    + {% endif %} + {% if 'n/a' != collector.debug %} +
    + Debug + {{ collector.debug ? 'en' : 'dis' }}abled +
    + {% endif %} +
    + Token + + {% if profiler_url %} + {{ collector.token }} + {% else %} + {{ collector.token }} + {% endif %} + +
    + {% endspaceless %} + {% endset %} + {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %} +{% endblock %} + +{% block menu %} + + Configuration + Config + +{% endblock %} + +{% block panel %} +

    Project Configuration

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

    PHP configuration

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    KeyValue
    PHP version{{ collector.phpversion }}
    Xdebug{{ collector.hasxdebug ? 'enabled' : 'disabled' }}
    PHP acceleration{{ collector.hasaccelerator ? 'enabled' : 'disabled' }}
    XCache{{ collector.hasxcache ? 'enabled' : 'disabled' }}
    APC{{ collector.hasapc ? 'enabled' : 'disabled' }}
    Zend OPcache{{ collector.haszendopcache ? 'enabled' : 'disabled' }}
    EAccelerator{{ collector.haseaccelerator ? 'enabled' : 'disabled' }}
    Full PHP configurationphpinfo
    + + {% if collector.bundles %} +

    Active bundles

    + + + + + + {% set bundles = collector.bundles %} + {% for name in bundles|keys|sort %} + + + + + {% endfor %} +
    NamePath
    {{ name }}{{ 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..6072ff6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig @@ -0,0 +1,68 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% from _self import display_listener %} + +{% block menu %} + + Events + Events + +{% endblock %} + +{% block panel %} + {% if collector.calledlisteners|length %} + {{ block('panelContent') }} + {% else %} +

    Events

    +

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

    + {% endif %} +{% endblock %} + +{% block panelContent %} +

    Called Listeners

    + + + + + + + {% for listener in collector.calledlisteners %} + + + + + {% endfor %} +
    Event nameListener
    {{ listener.event }}{{ display_listener(listener) }}
    + + {% if collector.notcalledlisteners %} +

    Not Called Listeners

    + + + + + + + {% set listeners = collector.notcalledlisteners %} + {% for listener in listeners|keys|sort %} + + + + + {% endfor %} +
    Event nameListener
    {{ listeners[listener].event }}{{ display_listener(listeners[listener]) }}
    + {% endif %} +{% endblock %} + +{% macro display_listener(listener) %} + {% if listener.type == "Closure" %} + Closure + {% elseif listener.type == "Function" %} + {% set link = listener.file|file_link(listener.line) %} + {% if link %}{{ listener.function }}{% else %}{{ listener.function }}{% endif %} + {% elseif listener.type == "Method" %} + {% set link = listener.file|file_link(listener.line) %} + {{ listener.class|abbr_class }}::{% if link %}{{ listener.method }}{% else %}{{ listener.method }}{% 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..1224081 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig @@ -0,0 +1,104 @@ +.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 { + 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; + 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; +} 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..121a9ee --- /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 %} + + Exception + Exception + + {% if collector.hasexception %} + 1 + {% endif %} + + +{% endblock %} + +{% block panel %} +

    Exception

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

    + No exception was thrown and uncaught 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/logger.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig new file mode 100644 index 0000000..3b72965 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig @@ -0,0 +1,127 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% import _self as logger %} + +{% block toolbar %} + {% if collector.counterrors or collector.countdeprecations %} + {% set icon %} + Logs + {% if collector.counterrors %} + {% set status_color = "red" %} + {% else %} + {% set status_color = "yellow" %} + {% endif %} + {% set error_count = collector.counterrors + collector.countdeprecations %} + {{ error_count }} + {% endset %} + {% set text %} + {% if collector.counterrors %} +
    + Exception + {{ collector.counterrors }} +
    + {% endif %} + {% if collector.countdeprecations %} +
    + Deprecated Calls + {{ collector.countdeprecations }} +
    + {% endif %} + {% endset %} + {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %} + {% endif %} +{% endblock %} + +{% block menu %} + + Logger + Logs + {% if collector.counterrors or collector.countdeprecations %} + {% set error_count = collector.counterrors + collector.countdeprecations %} + + {{ error_count }} + + {% endif %} + +{% endblock %} + +{% block panel %} +

    Logs

    + + {% set priority = request.query.get('priority', 0) %} + + + + + + +
    Filter +
    + + + + +
    +
    + + {% if collector.logs %} +
      + {% for log in collector.logs if priority >= 0 and log.priority >= priority or priority < 0 and log.context.type|default(0) == priority %} +
    • + {{ logger.display_message(loop.index, log) }} +
    • + {% else %} +
    • No logs available for this priority.
    • + {% endfor %} +
    + {% else %} +

    + No logs available. +

    + {% endif %} +{% endblock %} + + +{% macro display_message(log_index, log) %} + {% if constant('Symfony\\Component\\HttpKernel\\Debug\\ErrorHandler::TYPE_DEPRECATION') == log.context.type|default(0) %} + DEPRECATION - {{ log.message }} + {% set id = 'sf-call-stack-' ~ log_index %} + + + + + + {% for index, call in log.context.stack if index > 1 %} + {% if index == 2 %} + ' : '' }} + {% endfor %} + {% else %} + {{ log.priorityName }} - {{ log.message }} + {% if log.context is defined and log.context is not empty %} +
    + + Context: {{ log.context|json_encode(64 b-or 256) }} + + {% 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..7a1d5f5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig @@ -0,0 +1,17 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {% set icon %} + + Memory Usage + {{ '%.1f'|format(collector.memory / 1024 / 1024) }} MB + + {% endset %} + {% set text %} +
    + Memory usage + {{ '%.1f'|format(collector.memory / 1024 / 1024) }} / {{ collector.memoryLimit == -1 ? '∞' : '%.1f'|format(collector.memoryLimit / 1024 / 1024)|escape }} MB +
    + {% endset %} + {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': false } %} +{% 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..9c0ea3f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig @@ -0,0 +1,161 @@ +{% 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) %} + {{ collector.controller.class|abbr_class }} + + {{ collector.controller.method }} + + {% else %} + {{ collector.controller }} + {% endif %} + {% endset %} + {% set request_status_code_color = (400 > collector.statuscode) ? ((200 == collector.statuscode) ? 'green' : 'yellow') : 'red'%} + {% set request_route = collector.route ? collector.route : 'NONE' %} + {% set icon %} + Request + {{ collector.statuscode }} + {{ request_handler }} + on {{ request_route }} + {% endset %} + {% set text %} + {% spaceless %} +
    + Status + {{ collector.statuscode }} {{ collector.statustext }} +
    +
    + Controller + {{ request_handler }} +
    +
    + Route name + {{ request_route }} +
    +
    + Has session + {% if collector.sessionmetadata|length %}yes{% else %}no{% endif %} +
    + {% endspaceless %} + {% endset %} + {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %} +{% endblock %} + +{% block menu %} + + Request + Request + +{% endblock %} + +{% block panel %} +

    Request GET Parameters

    + + {% if collector.requestquery.all|length %} + {% include '@WebProfiler/Profiler/bag.html.twig' with { 'bag': collector.requestquery } only %} + {% else %} +

    + No GET parameters +

    + {% endif %} + +

    Request POST Parameters

    + + {% if collector.requestrequest.all|length %} + {% include '@WebProfiler/Profiler/bag.html.twig' with { 'bag': collector.requestrequest } only %} + {% else %} +

    + No POST parameters +

    + {% endif %} + +

    Request Attributes

    + + {% if collector.requestattributes.all|length %} + {% include '@WebProfiler/Profiler/bag.html.twig' with { 'bag': collector.requestattributes } only %} + {% else %} +

    + No attributes +

    + {% endif %} + +

    Request Cookies

    + + {% if collector.requestcookies.all|length %} + {% include '@WebProfiler/Profiler/bag.html.twig' with { 'bag': collector.requestcookies } only %} + {% else %} +

    + No cookies +

    + {% endif %} + +

    Request Headers

    + + {% include '@WebProfiler/Profiler/bag.html.twig' with { 'bag': collector.requestheaders } only %} + +

    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 %} + +

    Request Server Parameters

    + + {% include '@WebProfiler/Profiler/bag.html.twig' with { 'bag': collector.requestserver } only %} + +

    Response Headers

    + + {% include '@WebProfiler/Profiler/bag.html.twig' with { 'bag': collector.responseheaders } only %} + +

    Session Metadata

    + + {% if collector.sessionmetadata|length %} + {% include '@WebProfiler/Profiler/table.html.twig' with { 'data': collector.sessionmetadata } only %} + {% else %} +

    + No session metadata +

    + {% endif %} + +

    Session Attributes

    + + {% if collector.sessionattributes|length %} + {% include '@WebProfiler/Profiler/table.html.twig' with { 'data': collector.sessionattributes } only %} + {% else %} +

    + No session attributes +

    + {% endif %} + +

    Flashes

    + + {% if collector.flashes|length %} + {% include '@WebProfiler/Profiler/table.html.twig' with { 'data': collector.flashes } only %} + {% else %} +

    + No flashes +

    + {% endif %} + + {% if profile.parent %} +

    Parent request: {{ profile.parent.token }}

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

    Sub requests

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

    {{ child.token }}

    + {% include '@WebProfiler/Profiler/bag.html.twig' with { 'bag': child.getcollector('request').requestattributes } only %} + {% 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..782465d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/router.html.twig @@ -0,0 +1,15 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} +{% endblock %} + +{% block menu %} + + Routing + 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..6470ea5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig @@ -0,0 +1,490 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% from _self import display_timeline, dump_request_data %} + +{% if colors is not defined %} + {% set colors = { + 'default': '#aacd4e', + 'section': '#666', + 'event_listener': '#3dd', + 'event_listener_loading': '#add', + 'template': '#dd3', + 'doctrine': '#d3d', + 'propel': '#f4d', + 'child_sections': '#eed', + } %} +{% endif %} + +{% block toolbar %} + {% set duration = collector.events|length ? '%.0f ms'|format(collector.duration) : 'n/a' %} + {% set icon %} + Time + {{ duration }} + {% endset %} + {% set text %} +
    + Total time + {{ duration }} +
    + {% endset %} + {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %} +{% endblock %} + +{% block menu %} + + Timeline + Timeline + +{% endblock %} + +{% block panel %} +

    Timeline

    + {% if collector.events|length %} + {{ block('panelContent') }} + {% else %} +

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

    + {% endif %} +{% endblock %} + +{% block panelContent %} +
    + + + + + + + + + + + + + + +
    Total time{{ '%.0f'|format(collector.duration) }} ms
    Initialization time{{ '%.0f'|format(collector.inittime) }} ms
    Threshold ms
    +
    + +

    + {{ profile.parent ? "Request" : "Main Request" }} + + - {{ collector.events.__section__.duration }} ms + {% if profile.parent %} + - parent + {% endif %} + +

    + + {{ display_timeline('timeline_' ~ token, collector.events, colors) }} + + {% if profile.children|length %} + {% for child in profile.children %} + {% set events = child.getcollector('time').events %} +

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

    + + {{ display_timeline('timeline_' ~ child.token, events, colors) }} + {% endfor %} + {% endif %} + + +{% endblock %} + +{% macro dump_request_data(token, profile, events, origin) %} +{% from _self import dump_events %} + { + "id": "{{ token }}", + "left": {{ "%F"|format(events.__section__.origin - origin) }}, + "events": [ +{{ dump_events(events) }} + ] + } +{% endmacro %} + +{% macro dump_events(events) %} +{% for name, event in events %} +{% if '__section__' != name %} + { + "name": "{{ name|replace({"\\": "\\\\"}) }}", + "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 %} +{% 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/Profiler/admin.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/admin.html.twig new file mode 100644 index 0000000..3ca28ff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/admin.html.twig @@ -0,0 +1,27 @@ + 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..8947c9a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/bag.html.twig @@ -0,0 +1,17 @@ + + + + + + + + + {% for key in bag.keys|sort %} + + + {# JSON_UNESCAPED_SLASHES = 64, JSON_UNESCAPED_UNICODE = 256 #} + + + {% endfor %} + +
    KeyValue
    {{ key }}{{ bag.get(key)|json_encode(64 b-or 256) }}
    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..e4258a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig @@ -0,0 +1,23 @@ + + + + + + {% block title '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..c4e3df6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig @@ -0,0 +1,102 @@ + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/body.css.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/body.css.twig new file mode 100644 index 0000000..4fb1b2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/body.css.twig @@ -0,0 +1,148 @@ +/* +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 html{color:#000;background:#FFF;}.sf-reset body,.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 html, +.sf-reset body { + width: 100%; + min-height: 100%; + _height: 100%; + margin: 0; + padding: 0; +} +.sf-reset body { + font: 1em "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + text-align: left; + background-color: #efefef; +} +.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 { + border-radius: 16px; + margin-bottom: 20px; + background-color: #FFFFFF; + border: 1px solid #dfdfdf; + padding: 40px 50px; +} +.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/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..3ee8ba3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/header.html.twig @@ -0,0 +1,25 @@ + 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..aeffb2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig @@ -0,0 +1,43 @@ +{% extends '@WebProfiler/Profiler/base.html.twig' %} + +{% block body %} +
    + {% include '@WebProfiler/Profiler/header.html.twig' only %} + +
    +
    +
    +
    + {% block panel %} + {% if about == 'purge' %} +

    The profiler database was purged successfully

    +

    + Now you need to browse some pages with the Symfony Profiler enabled to collect data. +

    + {% elseif about == 'upload_error' %} +

    A problem occurred when uploading the data

    +

    + No file given or the file was not uploaded successfully. +

    + {% elseif about == 'already_exists' %} +

    A problem occurred when uploading the data

    +

    + The token already exists in the database. +

    + {% elseif about == 'no_token' %} +

    Token not found

    +

    + Token "{{ token }}" was not found in the database. +

    + {% endif %} + {% endblock %} +
    +
    + +
    +
    +
    +{% 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..4cbecc0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig @@ -0,0 +1,136 @@ +{% extends '@WebProfiler/Profiler/base.html.twig' %} + +{% block body %} + + {{ render(path('_wdt', { 'token': token, 'position': 'normal' })) }} + +
    + {% include '@WebProfiler/Profiler/header.html.twig' only %} + +
    + +
    +
    + {% if profile %} +
    + View last 10 + Profile for: + {{ profile.method|upper }} + {% if profile.method|upper in ['GET', 'HEAD'] %} + {{ profile.url }} + {% else %} + {{ profile.url }} + {% endif %} + + by {{ profile.ip }} at {{ profile.time|date('r') }} + +
    + {% endif %} + +
    + {% 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..7f10150 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -0,0 +1,389 @@ +/* +Copyright (c) 2008, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 2.6.0 +*/ +html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;} +html, body { + background-color: #efefef; +} +body { + font: 1em "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + text-align: left; +} +p { + font-size: 14px; + line-height: 20px; + color: #313131; + padding-bottom: 20px +} +strong { + color: #313131; + font-weight: bold; +} +em { + font-style: italic; +} +a { + color: #6c6159; +} +a img { + border: none; +} +a:hover { + text-decoration: underline; +} +button::-moz-focus-inner { + padding: 0; + border: none; +} +button { + overflow: visible; + width: auto; + background-color: transparent; + font-weight: bold; +} +caption { + margin-bottom: 7px; +} +table, tr, th, td { + border-collapse: collapse; + border: 1px solid #d0dbb3; +} +table { + width: 100%; + margin: 10px 0 30px; +} +table th { + font-weight: bold; + background-color: #f1f7e2; +} +table th, table td { + font-size: 12px; + padding: 8px 10px; +} +fieldset { + border: none; +} +abbr { + border-bottom: 1px dotted #000; + cursor: help; +} +.clear { + clear: both; + height: 0; + font-size: 0; + line-height: 0; +} +.clear-fix:after +{ + content: "\0020"; + display: block; + height: 0; + clear: both; + visibility: hidden; +} +* html .clear-fix +{ + height: 1%; +} +.clear-fix +{ + display: block; +} +#content { + padding: 0 50px; + margin: 0 auto 20px; + font-family: Arial, Helvetica, sans-serif; + min-width: 970px; +} +#header { + padding: 20px 30px 20px; +} +#header h1 { + float: left; +} +.search { + float: right; +} +#menu-profiler { + border-right: 1px solid #dfdfdf; +} +#menu-profiler li { + border-bottom: 1px solid #dfdfdf; + position: relative; + padding-bottom: 0; + display: block; + background-color: #f6f6f6; + z-index: 10000; +} +#menu-profiler li a { + color: #404040; + display: block; + font-size: 13px; + text-transform: uppercase; + text-decoration: none; + cursor: pointer; +} +#menu-profiler li a span.label { + display: block; + padding: 20px 0px 16px 65px; + min-height: 16px; + overflow: hidden; +} +#menu-profiler li a span.icon { + display: block; + position: absolute; + left: 0; + top: 12px; + width: 60px; + text-align: center; +} +#menu-profiler li.selected a, +#menu-profiler li a:hover { + background: #d1d1d1 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA7CAAAAACfn7+eAAAACXZwQWcAAAABAAAAOwDiPIGUAAAAJElEQVQIW2N4y8TA9B+KGZDYEP5/FD4Eo7IgNLJqZDUIMRRTAcmVHUZf/1g/AAAAAElFTkSuQmCC) repeat-x 0 0; +} +#navigation div:first-child, +#menu-profiler li:first-child, +#menu-profiler li:first-child a, +#menu-profiler li:first-child a span.label { + border-radius: 16px 0 0 0; +} +#menu-profiler li a span.count { + padding: 0; + position: absolute; + right: 10px; + top: 20px; +} +#collector-wrapper { + float: left; + width: 100%; +} +#collector-content { + margin-left: 250px; + padding: 30px 40px 40px; +} +#navigation { + float: left; + width: 250px; + margin-left: -100%; +} +#collector-content table td { + background-color: white; +} +h1 { + font-family: Georgia, "Times New Roman", Times, serif; + color: #404040; +} +h2, h3 { + font-weight: bold; + margin-bottom: 20px; +} +li { + padding-bottom: 10px; +} +#main { + border-radius: 16px; + margin-bottom: 20px; +} +#menu-profiler span.count span { + display: inline-block; + background-color: #aacd4e; + border-radius: 6px; + padding: 4px; + color: #fff; + margin-right: 2px; + font-size: 11px; +} +#resume { + background-color: #f6f6f6; + border-bottom: 1px solid #dfdfdf; + padding: 18px 10px 0px; + margin-left: 250px; + height: 34px; + color: #313131; + font-size: 12px; + border-top-right-radius: 16px; +} +a#resume-view-all { + display: inline-block; + padding: 0.2em 0.7em; + margin-right: 0.5em; + background-color: #666; + border-radius: 16px; + color: white; + font-weight: bold; + text-decoration: none; +} +table th.value { + width: 450px; + background-color: #dfeeb8; +} +#content h2 { + font-size: 24px; + color: #313131; + font-weight: bold; +} +#content #main { + padding: 0; + background-color: #FFF; + border: 1px solid #dfdfdf; +} +#content #main p { + color: #313131; + font-size: 14px; + padding-bottom: 20px; +} +.sf-toolbarreset { + border-top: 0; + padding: 0; +} +.sf-reset .block-exception-detected .text-exception { + left: 10px; + right: 10px; + width: 95%; +} +.sf-reset .block-exception-detected .illustration-exception { + display: none; +} +ul.alt { + margin: 10px 0 30px; +} +ul.alt li { + padding: 5px 7px; + font-size: 13px; +} +ul.alt li.even { + background: #f1f7e2; +} +ul.alt li.error { + background-color: #f66; + margin-bottom: 1px; +} +ul.alt li.warning { + background-color: #ffcc00; + margin-bottom: 1px; +} +ul.sf-call-stack li { + text-size: small; + padding: 0 0 0 20px; +} +td.main, td.menu { + text-align: left; + margin: 0; + padding: 0; + border: 0; + vertical-align: top; +} +.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; +} +#navigation div:first-child { + margin: 0 0 20px; + border-top: 0; +} +#navigation .search { + padding-top: 15px; + float: none; + background: none repeat scroll 0 0 #f6f6f6; + color: #333; + margin: 20px 0; + border: 1px solid #dfdfdf; + border-left: none; +} +#navigation .search h3 { + font-family: Arial, Helvetica, sans-serif; + text-transform: uppercase; + margin-left: 10px; + font-size: 13px; +} +#navigation .search form { + padding: 15px 0; +} +#navigation .search button { + float: right; + margin-right: 20px; +} +#navigation .search label { + display: block; + float: left; + width: 50px; +} +#navigation .search input, +#navigation .search select, +#navigation .search label, +#navigation .search a { + font-size: 12px; +} +#navigation .search form { + padding-left: 10px; +} +#navigation .search input { + width: 160px; +} +#navigation .import label { + float: none; + display: inline; +} +#navigation .import input { + width: 100px; +} +.timeline { + background-color: #fbfbfb; + margin-bottom: 15px; + margin-top: 5px; +} +#collector-content .routing tr.matches td { + background-color: #0e0; +} +#collector-content .routing tr.almost td { + background-color: #fa0; +} +.loading { + background: transparent url(data:image/gif;base64,R0lGODlhGAAYAPUmAAQCBFxeXBwaHOzq7JSWlAwODCQmJPT29JyenJSSlCQiJPTy9BQWFCwuLAQGBKyqrBweHOzu7Ly+vHx+fGxubLy6vMTCxMzKzBQSFKSmpLSytJyanAwKDHRydPz+/HR2dCwqLMTGxPz6/Hx6fISGhGxqbGRmZOTi5DQyNDw6PKSipFxaXExOTLS2tISChIyKjERCRMzOzOTm5Nze3FRSVNza3FRWVKyurExKTNTS1ERGRNTW1GRiZIyOjDQ2NDw+PCH/C05FVFNDQVBFMi4wAwEAAAAh+QQJBgAmACwAAAAAGAAYAAAGykCTcGhaRIiL0uNIbAoXEwaCeOAAMJ+Fc3hRAAAkogfzBUAsW43jC6k0BwQvwPFohqwAymFrOoy+DmhPcgl8RAhsTBNfFIZNiwAdRQxme45DByAABREPX4WXRBIkGwMlDgUDoXwDESKrsLGys7EeB1q0RQIcAZ0JgrCIAAgLBQAGlqEiDXOqH18jsCSMQhEQX1OXGV8MqkIWawATr1sH019uRBnhBsR2zNhbEgJlBeRCCdzpWxEUxg5MhDxwQMGbowgIAhg0MWDhkCAAIfkECQYAHQAsAAAAABgAGAAABsDAjnBI7AQMKdNjUWx2RMUXYArAjCJO4aUBHc5SBioAYnFqOICbc0BQTB2P4sUx3WQ7h9G7LFyEAQl3QwhTBl0TUxSCRAg3B30MY4+LTg9TgZROJlMnmU4pAAqeTmEpo0MnCTY0EzWnQiwAAq9EKAANtH10K7kdKlMIuQcNAA4DQiIVGZ5SAIpPtgDBixlTDMdCFnQAE12VVBVFGdsGCExNLcBOEgJUDg00rkMiBhJ3ERQFYi5Fk4IRCFY0gMBiURAAIfkECQYAGQAsAAAAABgAGAAABr/AjHAovJhSBkPK9FgQn9CdA0CtYkYRqDYzUqRgkCoAYtGenh7igKCgFmrPC2a3HR5Gqdxz0dal60J/RBNUHYB1CwxjB4dbD1QJjVEWJlRnkkMkDgEpAAqYRA0AKAYAKaBDLAACpTCoQqoCnQavGaINlRSCkgtTKxYxtSpUCLUZB6IOA8YkVBRQu1seOAAMy0QzNBMihzsFFU8nGFQGCE51cFASAlUODTQsKCOYERQFYlQOevQIKw0CAhqskLAlCAAh+QQJBgAVACwAAAAAGAAYAAAGvcCKcFhZPEwpgyFleiyI0OFiwgBYr1bGLArlYSGwpJXEhYoCit6AKNN4ylDPAU6vR0WliFBmj1MAHUUCCW99FSIgAAURD1YahkIIVggmVnyQC1YrKQAKkEMNAA0GACmfQiwAEKQwpxWpApwGrqENXgB6mA4AKxlWBJ8SkwsFAAYikB49BWsfADaFkFsVEStzrkPRdCLadBJPUiq2yHUbAA4NLCwou5rdUCdVWFcOFGt1EQgrDQICDTYI7kEJAgAh+QQJBgAiACwAAAAAGAAYAAAGvUCRcChaPEwpgyG1ITqdiwkDQK1KntiLogqAwFIBD1H81DiokIQMK3w9nJ5JAUA5sIURjMPylLXuQxJoEYCAE1QdhXcHIAAFhIpYCFQIKhdkkXhUKykAJplEDQANBgApoEMsAAKlMKhCqgKdBq8iJqO3AAOvHiEJGVQEtUILcwZ2wx9UE8NFEFR/hRa7ThIOHCeABy+OLphCDx93CyqilFjfIh0sLChnVAwVkTHvVQ4U1IobDQICDSsI8hEJAgAh+QQFBgAYACwAAAAAGAAYAAAGv0CMcIhZPEy/n4fIbBYnDIDUxqwsnMKLQipVZJgoiMWpcUghiVMzYnY8mBczgHLAHkZSx1i4gEgTWEQIZxFCLSBzgUwTUh1DHid1ikMHiAWFk1iDAAiZWBFSAZ5YDQANo04PNj44PDeoTB4pAAawTDxSmLYYGVIEu3wFtJKZIgNLQh9SI6MkDg0tQhF+nJm9AAwDQxZyEyJ2JFwVTBlyakwLCChcnU0SAgbIhihy2OOfr0S4eRTasDANbCDwxyQIADs=) scroll no-repeat 50% 50%; + height: 30px; + display: none; +} +.sf-profiler-timeline .legends { + font-size: 12px; + line-height: 1.5em; +} +.sf-profiler-timeline .legends span { + border-left-width: 10px; + border-left-style: solid; + padding: 0 10px 0 5px; +} +.sf-profiler-timeline canvas { + border: 1px solid #999; + border-width: 1px 0; +} +.collapsed-menu-parents #resume, +.collapsed-menu-parents #collector-content { + margin-left: 60px !important; +} +.collapsed-menu { + width: 60px !important; +} +.collapsed-menu span :not(.icon) { + display: none !important; +} +.collapsed-menu span.icon img { + display: inline !important; +} 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..b86ea74 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/results.html.twig @@ -0,0 +1,35 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block panel %} +

    Search Results

    + + {% if tokens %} + + + + + + + + + + + + {% for elements in tokens %} + + + + + + + + {% endfor %} + +
    TokenIPMethodURLTime
    {{ elements.token }}{{ elements.ip }}{{ elements.method }}{{ elements.url }}{{ elements.time|date('r') }}
    + {% 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..08defd6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig @@ -0,0 +1,45 @@ + 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..1c5130d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/table.html.twig @@ -0,0 +1,17 @@ + + + + + + + + + {% for key in data|keys|sort %} + + + {# JSON_UNESCAPED_SLASHES = 64, JSON_UNESCAPED_UNICODE = 256 #} + + + {% endfor %} + +
    KeyValue
    {{ key }}{{ data[key]|json_encode(64 b-or 256) }}
    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..a2b2af0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -0,0 +1,363 @@ +.sf-minitoolbar { + display: none; + + position: fixed; + bottom: 0; + right: 0; + + padding: 5px 5px 0; + + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #e4e4e4, #ffffff); + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#e4e4e4), to(#ffffff)); + background-image: -o-linear-gradient(top, #e4e4e4, #ffffff); + background: linear-gradient(top, #e4e4e4, #ffffff); + + border-radius: 16px 0 0 0; + + z-index: 6000000; +} + +.sf-toolbarreset { + position: fixed; + background-color: #f7f7f7; + left: 0; + right: 0; + height: 38px; + margin: 0; + padding: 0 40px 0 0; + z-index: 6000000; + font: 11px Verdana, Arial, sans-serif; + text-align: left; + color: #2f2f2f; + + background-image: -moz-linear-gradient(top, #e4e4e4, #ffffff); + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#e4e4e4), to(#ffffff)); + background-image: -o-linear-gradient(top, #e4e4e4, #ffffff); + background: linear-gradient(top, #e4e4e4, #ffffff); + + bottom: 0; + border-top: 1px solid #bbb; +} +.sf-toolbarreset abbr { + border-bottom: 1px dotted #000000; + cursor: help; +} +.sf-toolbarreset span, +.sf-toolbarreset div { + font-size: 11px; +} +.sf-toolbarreset img { + width: auto; + display: inline; +} + +.sf-toolbarreset .hide-button { + display: block; + position: absolute; + top: 0; + right: 0; + width: 40px; + height: 40px; + cursor: pointer; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAAAllBMVEXDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PExMTPz8/Q0NDR0dHT09Pb29vc3Nzf39/h4eHi4uLj4+P6+vr7+/v8/Pz9/f3///+Nh2QuAAAAIXRSTlMABgwPGBswMzk8QktRV4SKjZOWmaKlq7TAxszb3urt+fy1vNEpAAAAiklEQVQIHUXBBxKCQBREwRFzDqjoGh+C2YV//8u5Sll2S0E/dof1tKdKM6GyqCto7PjZRJIS/mbSELgXOSd/BzpKIH1ZefVWpDDTHsi8mZVnwImPi5ndCLbaAZk3M58Bay0h9VbeSvMpjDUAHj4jL55AW1rxN5fU2PLjIgVRzNdxVFOlGzvnJi0Fb1XNGBHA9uQOAAAAAElFTkSuQmCC'); + background-repeat: no-repeat; + background-position: 13px 11px; +} + +.sf-toolbar-block { + white-space: nowrap; + color: #2f2f2f; + display: block; + min-height: 28px; + border-right: 1px solid #e4e4e4; + padding: 0; + float: left; + cursor: default; +} + +.sf-toolbar-block span { + display: inline-block; +} + +.sf-toolbar-block .sf-toolbar-info-piece { + line-height: 19px; + margin-bottom: 5px; +} + +.sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-status { + padding: 0px 5px; + border-radius: 5px; + margin-bottom: 0px; + vertical-align: top; +} + +.sf-toolbar-block .sf-toolbar-info-piece:last-child { + margin-bottom: 0; +} + +.sf-toolbar-block .sf-toolbar-info-piece a, +.sf-toolbar-block .sf-toolbar-info-piece abbr { + color: #2f2f2f; +} + +.sf-toolbar-block .sf-toolbar-info-piece b { + display: inline-block; + width: 110px; + vertical-align: top; +} + +.sf-toolbar-block .sf-toolbar-info-with-next-pointer:after { + content: ' :: '; + color: #999; +} + +.sf-toolbar-block .sf-toolbar-info-with-delimiter { + border-right: 1px solid #999; + padding-right: 5px; + margin-right: 5px; +} + +.sf-toolbar-block .sf-toolbar-info { + display: none; + position: absolute; + background-color: #fff; + border: 1px solid #bbb; + padding: 9px 0; + margin-left: -1px; + + bottom: 38px; + border-bottom-width: 0; + border-bottom: 1px solid #bbb; + border-radius: 4px 4px 0 0; +} + +.sf-toolbarreset > div:last-of-type .sf-toolbar-info { + right: -1px; +} + +.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; + border-radius: 3px; + margin-bottom: 2px; + vertical-align: middle; + min-width: 6px; + min-height: 13px; +} + +.sf-toolbar-block .sf-toolbar-status abbr { + color: #fff; + border-bottom: 1px dotted black; +} + +.sf-toolbar-block .sf-toolbar-status-green { + background-color: #759e1a; +} + +.sf-toolbar-block .sf-toolbar-status-red { + background-color: #a33; +} + +.sf-toolbar-block .sf-toolbar-status-yellow { + background-color: #ffcc00; + color: #000; +} + +.sf-toolbar-block .sf-toolbar-status-black { + background-color: #000; +} + +.sf-toolbar-block .sf-toolbar-icon { + display: block; +} + +.sf-toolbar-block .sf-toolbar-icon > a, +.sf-toolbar-block .sf-toolbar-icon > span { + display: block; + text-decoration: none; + margin: 0; + padding: 5px 8px; + min-width: 20px; + text-align: center; + vertical-align: middle; +} + +.sf-toolbar-block .sf-toolbar-icon > a, +.sf-toolbar-block .sf-toolbar-icon > a:link +.sf-toolbar-block .sf-toolbar-icon > a:hover { + color: black !important; +} + +.sf-toolbar-block .sf-toolbar-icon img { + border-width: 0; + vertical-align: middle; +} + +.sf-toolbar-block .sf-toolbar-icon img + span { + margin-left: 5px; + margin-top: 2px; +} + +.sf-toolbar-block .sf-toolbar-icon .sf-toolbar-status { + border-radius: 12px; + border-bottom-left-radius: 0; + margin-top: 0; +} + +.sf-toolbar-block .sf-toolbar-info-method { + border-bottom: 1px dashed #ccc; + cursor: help; +} + +.sf-toolbar-block .sf-toolbar-info-method[onclick=""] { + border-bottom: none; + cursor: inherit; +} + +.sf-toolbar-info-php {} +.sf-toolbar-info-php-ext {} + +.sf-toolbar-info-php-ext .sf-toolbar-status { + margin-left: 2px; +} + +.sf-toolbar-info-php-ext .sf-toolbar-status:first-child { + margin-left: 0; +} + +.sf-toolbar-block a .sf-toolbar-info-piece-additional, +.sf-toolbar-block a .sf-toolbar-info-piece-additional-detail { + display: inline-block; +} + +.sf-toolbar-block a .sf-toolbar-info-piece-additional:empty, +.sf-toolbar-block a .sf-toolbar-info-piece-additional-detail:empty { + display: none; +} + +.sf-toolbarreset:hover { + box-shadow: rgba(0, 0, 0, 0.3) 0 0 50px; +} + +.sf-toolbar-block:hover { + box-shadow: rgba(0, 0, 0, 0.35) 0 0 5px; + border-right: none; + margin-right: 1px; + position: relative; +} + +.sf-toolbar-block:hover .sf-toolbar-icon { + background-color: #fff; + border-top: 1px dotted #DDD; + position: relative; + margin-top: -1px; + z-index: 10002; +} + +.sf-toolbar-block:hover .sf-toolbar-info { + display: block; + min-width: -webkit-calc(100% + 2px); + min-width: calc(100% + 2px); + z-index: 10001; + box-sizing: border-box; + padding: 9px; + line-height: 19px; +} + +/***** Override the setting when the toolbar is on the top *****/ +{% if position == 'top' %} + .sf-minitoolbar { + top: 0; + bottom: auto; + right: 0; + + background-color: #f7f7f7; + + background-image: -moz-linear-gradient(225deg, #e4e4e4, #ffffff); + background-image: -webkit-gradient(linear, 100% 0%, 0% 0%, from(#e4e4e4), to(#ffffff)); + background-image: -o-linear-gradient(135deg, #e4e4e4, #ffffff); + background: linear-gradient(225deg, #e4e4e4, #ffffff); + + border-radius: 0 0 0 16px; + } + + .sf-toolbarreset { + background-image: -moz-linear-gradient(225deg, #e4e4e4, #ffffff); + background-image: -webkit-gradient(linear, 100% 0%, 0% 0%, from(#e4e4e4), to(#ffffff)); + background-image: -o-linear-gradient(135deg, #e4e4e4, #ffffff); + background: linear-gradient(225deg, #e4e4e4, #ffffff); + + top: 0; + bottom: auto; + border-bottom: 1px solid #bbb; + } + + .sf-toolbar-block .sf-toolbar-info { + top: 39px; + bottom: auto; + border-top-width: 0; + border-radius: 0 0 4px 4px; + } + + .sf-toolbar-block:hover .sf-toolbar-icon { + border-top: none; + border-bottom: 1px dotted #DDD; + margin-top: 0; + margin-bottom: -1px; + } +{% endif %} + +{% if not floatable %} + .sf-toolbarreset { + position: static; + background: #cbcbcb; + + background-image: -moz-linear-gradient(90deg, #cbcbcb, #e8e8e8); !important; + background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, from(#cbcbcb), to(#e8e8e8)); !important; + background-image: -o-linear-gradient(180deg, #cbcbcb, #e8e8e8); !important; + background: linear-gradient(90deg, #cbcbcb, #e8e8e8); !important; + } +{% endif %} + +/***** Media query *****/ +@media screen and (max-width: 779px) { + .sf-toolbar-block .sf-toolbar-icon > * > :first-child ~ * { + display: none; + } + + .sf-toolbar-block .sf-toolbar-icon > * > .sf-toolbar-info-piece-additional, + .sf-toolbar-block .sf-toolbar-icon > * > .sf-toolbar-info-piece-additional-detail { + display: none !important; + } +} + +@media screen and (min-width: 880px) { + .sf-toolbar-block .sf-toolbar-icon a[href$="config"] .sf-toolbar-info-piece-additional { + display: inline-block; + } +} + +@media screen and (min-width: 980px) { + .sf-toolbar-block .sf-toolbar-icon a[href$="security"] .sf-toolbar-info-piece-additional { + display: inline-block; + } +} + +@media screen and (max-width: 1179px) { + .sf-toolbar-block .sf-toolbar-icon > * > .sf-toolbar-info-piece-additional { + display: none; + } +} + +@media screen and (max-width: 1439px) { + .sf-toolbar-block .sf-toolbar-icon > * > .sf-toolbar-info-piece-additional-detail { + 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..d0a6a49 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.html.twig @@ -0,0 +1,48 @@ + +{% if 'normal' != position %} + +
    + + Symfony + +
    +
    +{% endif %} + +
    + {% for name, template in templates %} + {{ template.renderblock('toolbar', { + 'collector': profile.getcollector(name), + 'profiler_url': profiler_url, + 'token': profile.token, + 'name': name + }) + }} + {% endfor %} + + {% if 'normal' != position %} + + {% 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..42bbfea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_item.html.twig @@ -0,0 +1,9 @@ +{% if link %} + {% set icon %} + {{ icon }} + {% endset %} +{% endif %} +
    +
    {{ icon|default('') }}
    +
    {{ 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..f3d5a71 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig @@ -0,0 +1,40 @@ + +{% 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..b4ec407 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_redirect.html.twig @@ -0,0 +1,18 @@ +{% extends 'TwigBundle::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..db7610e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Router/panel.html.twig @@ -0,0 +1,44 @@ +

    Routing for "{{ request.pathinfo }}"

    + +
      +
    • + Route:  + {% if request.route %} + {{ request.route }} + {% else %} + No matching route + {% endif %} +
    • +
    • + Route parameters:  + {% if request.routeParams|length %} + {% include '@WebProfiler/Profiler/table.html.twig' with { 'data': request.routeParams, 'class': 'inline' } only %} + {% else %} + No parameters + {% endif %} +
    • + {% if router.redirect %} +
    • + Redirecting to:  "{{ router.targetUrl }}" {% if router.targetRoute %}(route: "{{ router.targetRoute }}"){% endif %} +
    • + {% endif %} +
    • + Route matching logs + + + + + + + {% for trace in traces %} + + + + + + {% endfor %} +
      Route namePatternLog
      {{ trace.name }}{{ trace.path }}{{ trace.log }}
      + Note: The above matching is based on the configuration for the current router which might differ from + the configuration used while routing this request. +
    • +
    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..8d47caf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.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\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(array()); + $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')), + array(array('intercept_redirects' => true), array('intercept_redirects' => true, 'toolbar' => false, 'position' => 'bottom')), + array(array('intercept_redirects' => false), array('intercept_redirects' => false, 'toolbar' => false, 'position' => 'bottom')), + array(array('toolbar' => true), array('intercept_redirects' => false, 'toolbar' => true, 'position' => 'bottom')), + array(array('position' => 'top'), array('intercept_redirects' => false, 'toolbar' => false, 'position' => 'top')), + ); + } +} 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..f9dd1ea --- /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; +use Symfony\Component\DependencyInjection\Scope; + +class WebProfilerExtensionTest extends TestCase +{ + private $kernel; + /** + * @var Symfony\Component\DependencyInjection\Container $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->addScope(new Scope('request')); + $this->container->register('request', 'Symfony\\Component\\HttpFoundation\\Request')->setScope('request'); + $this->container->register('router', $this->getMockClass('Symfony\\Component\\Routing\\RouterInterface')); + $this->container->register('templating.helper.assets', $this->getMockClass('Symfony\\Component\\Templating\\Helper\\AssetsHelper')); + $this->container->register('templating.helper.router', $this->getMockClass('Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\RouterHelper')) + ->addArgument(new Reference('router')); + $this->container->register('twig', 'Twig_Environment'); + $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->get('web_profiler.debug_toolbar')->isEnabled()); + + $this->assertSaneContainer($this->getDumpedContainer()); + } + + /** + * @dataProvider getDebugModes + */ + public function testToolbarConfig($enabled) + { + $extension = new WebProfilerExtension(); + $extension->load(array(array('toolbar' => $enabled)), $this->container); + + $this->assertSame($enabled, $this->container->get('web_profiler.debug_toolbar')->isEnabled()); + + $this->assertSaneContainer($this->getDumpedContainer()); + } + + public function getDebugModes() + { + return array( + array(true), + array(false), + ); + } + + 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->enterScope('request'); + $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..beb7b2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.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\Bundle\WebProfilerBundle\Tests\EventListener; + +use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener; +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); + $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()); + } + + 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 getKernelMock() + { + return $this->getMock('Symfony\Component\HttpKernel\Kernel', array(), array(), '', false); + } +} 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..c46ca05 --- /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; + + public 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')->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..586da13 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/TestCase.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\WebProfilerBundle\Tests; + +class TestCase extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Twig_Environment')) { + $this->markTestSkipped('Twig is not available.'); + } + } +} 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..c3f20e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -0,0 +1,39 @@ +{ + "name": "symfony/web-profiler-bundle", + "type": "symfony-bundle", + "description": "Symfony WebProfilerBundle", + "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.3", + "symfony/http-kernel": "~2.2", + "symfony/routing": "~2.2", + "symfony/twig-bridge": "~2.2" + }, + "require-dev": { + "symfony/config": "~2.2", + "symfony/dependency-injection": "~2.0", + "symfony/stopwatch": "~2.2" + }, + "autoload": { + "psr-0": { "Symfony\\Bundle\\WebProfilerBundle\\": "" } + }, + "target-dir": "Symfony/Bundle/WebProfilerBundle", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..7750cbd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./Resources + ./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..dcffdd6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Client.php @@ -0,0 +1,570 @@ + + * + * 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 + * + * @api + */ +abstract class Client +{ + protected $history; + protected $cookieJar; + protected $server; + protected $internalRequest; + protected $request; + protected $internalResponse; + protected $response; + protected $crawler; + protected $insulated; + protected $redirect; + protected $followRedirects; + + private $maxRedirects; + private $redirectCount; + private $isMainRequest; + + /** + * 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 + * + * @api + */ + public function __construct(array $server = array(), History $history = null, CookieJar $cookieJar = null) + { + $this->setServerParameters($server); + $this->history = null === $history ? new History() : $history; + $this->cookieJar = null === $cookieJar ? new CookieJar() : $cookieJar; + $this->insulated = false; + $this->followRedirects = true; + $this->maxRedirects = -1; + $this->redirectCount = 0; + $this->isMainRequest = true; + } + + /** + * Sets whether to automatically follow redirects or not. + * + * @param Boolean $followRedirect Whether to follow redirects + * + * @api + */ + public function followRedirects($followRedirect = true) + { + $this->followRedirects = (Boolean) $followRedirect; + } + + /** + * Sets the maximum number of requests that crawler can follow. + * + * @param integer $maxRedirects + */ + public function setMaxRedirects($maxRedirects) + { + $this->maxRedirects = $maxRedirects < 0 ? -1 : $maxRedirects; + $this->followRedirects = -1 != $this->maxRedirects; + } + + /** + * Sets the insulated flag. + * + * @param Boolean $insulated Whether to insulate the requests or not + * + * @throws \RuntimeException When Symfony Process Component is not installed + * + * @api + */ + public function insulate($insulated = true) + { + if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) { + // @codeCoverageIgnoreStart + throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.'); + // @codeCoverageIgnoreEnd + } + + $this->insulated = (Boolean) $insulated; + } + + /** + * Sets server parameters. + * + * @param array $server An array of server parameters + * + * @api + */ + 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 + * + * @api + */ + public function getHistory() + { + return $this->history; + } + + /** + * Returns the CookieJar instance. + * + * @return CookieJar A CookieJar instance + * + * @api + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + /** + * Returns the current Crawler instance. + * + * @return Crawler|null A Crawler instance + * + * @api + */ + public function getCrawler() + { + return $this->crawler; + } + + /** + * Returns the current BrowserKit Response instance. + * + * @return Response|null A BrowserKit Response instance + * + * @api + */ + 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 + * + * @api + */ + public function getResponse() + { + return $this->response; + } + + /** + * Returns the current BrowserKit Request instance. + * + * @return Request|null A BrowserKit Request instance + * + * @api + */ + 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 + * + * @api + */ + public function getRequest() + { + return $this->request; + } + + /** + * Clicks on a given link. + * + * @param Link $link A Link instance + * + * @return Crawler + * + * @api + */ + 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 + * + * @api + */ + 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 Boolean $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload()) + * + * @return Crawler + * + * @api + */ + 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); + + $server = array_merge($this->server, $server); + if (!$this->history->isEmpty()) { + $server['HTTP_REFERER'] = $this->history->current()->getUri(); + } + $server['HTTP_HOST'] = parse_url($uri, PHP_URL_HOST); + $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) + { + // We set the TMPDIR (for Macs) and TEMP (for Windows), because on these platforms the temp directory changes based on the user. + $process = new PhpProcess($this->getScript($request), null, array('TMPDIR' => sys_get_temp_dir(), 'TEMP' => sys_get_temp_dir())); + $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) + { + // @codeCoverageIgnoreStart + throw new \LogicException('To insulate requests, you need to override the getScript() method.'); + // @codeCoverageIgnoreEnd + } + + /** + * 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 null; + } + + $crawler = new Crawler(null, $uri); + $crawler->addContent($content, $type); + + return $crawler; + } + + /** + * Goes back in the browser history. + * + * @return Crawler + * + * @api + */ + public function back() + { + return $this->requestFromRequest($this->history->back(), false); + } + + /** + * Goes forward in the browser history. + * + * @return Crawler + * + * @api + */ + public function forward() + { + return $this->requestFromRequest($this->history->forward(), false); + } + + /** + * Reloads the current browser. + * + * @return Crawler + * + * @api + */ + public function reload() + { + return $this->requestFromRequest($this->history->current(), false); + } + + /** + * Follow redirects? + * + * @return Crawler + * + * @throws \LogicException If request was not a redirect + * + * @api + */ + 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)); + } + } + + $this->isMainRequest = false; + + $response = $this->request('get', $this->redirect); + + $this->isMainRequest = true; + + return $response; + } + + /** + * Restarts the client. + * + * It flushes history and all cookies. + * + * @api + */ + 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')) { + 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' + ); + } + + // 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 Boolean $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); + } +} 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..1e2c64c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Cookie.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\BrowserKit; + +/** + * Cookie represents an HTTP cookie. + * + * @author Fabien Potencier + * + * @api + */ +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 Boolean $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client + * @param Boolean $httponly The cookie httponly flag + * @param Boolean $encodedValue Whether the value is encoded or not + * + * @api + */ + 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->expires = null === $expires ? null : (integer) $expires; + $this->path = empty($path) ? '/' : $path; + $this->domain = $domain; + $this->secure = (Boolean) $secure; + $this->httponly = (Boolean) $httponly; + } + + /** + * Returns the HTTP representation of the Cookie. + * + * @return string The HTTP representation of the Cookie + * + * @api + */ + public function __toString() + { + $cookie = sprintf('%s=%s', $this->name, $this->rawValue); + + if (null !== $this->expires) { + $cookie .= '; expires='.substr(\DateTime::createFromFormat('U', $this->expires, new \DateTimeZone('GMT'))->format(self::$dateFormats[0]), 0, -5); + } + + 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 + * + * @api + */ + public static function fromString($cookie, $url = null) + { + $parts = explode(';', $cookie); + + if (false === strpos($parts[0], '=')) { + throw new \InvalidArgumentException('The cookie string "%s" is not valid.'); + } + + 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']) || !isset($urlParts['path'])) { + throw new \InvalidArgumentException(sprintf('The URL "%s" is not valid.', $url)); + } + + $values['domain'] = $urlParts['host']; + $values['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 + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the value of the cookie. + * + * @return string The cookie value + * + * @api + */ + public function getValue() + { + return $this->value; + } + + /** + * Gets the raw value of the cookie. + * + * @return string The cookie value + * + * @api + */ + public function getRawValue() + { + return $this->rawValue; + } + + /** + * Gets the expires time of the cookie. + * + * @return string The cookie expires time + * + * @api + */ + public function getExpiresTime() + { + return $this->expires; + } + + /** + * Gets the path of the cookie. + * + * @return string The cookie path + * + * @api + */ + public function getPath() + { + return $this->path; + } + + /** + * Gets the domain of the cookie. + * + * @return string The cookie domain + * + * @api + */ + public function getDomain() + { + return $this->domain; + } + + /** + * Returns the secure flag of the cookie. + * + * @return Boolean The cookie secure flag + * + * @api + */ + public function isSecure() + { + return $this->secure; + } + + /** + * Returns the httponly flag of the cookie. + * + * @return Boolean The cookie httponly flag + * + * @api + */ + public function isHttpOnly() + { + return $this->httponly; + } + + /** + * Returns true if the cookie has expired. + * + * @return Boolean true if the cookie has expired, false otherwise + * + * @api + */ + 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..90571b2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/CookieJar.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; + +/** + * CookieJar. + * + * @author Fabien Potencier + * + * @api + */ +class CookieJar +{ + protected $cookieJar = array(); + + /** + * Sets a cookie. + * + * @param Cookie $cookie A Cookie instance + * + * @api + */ + 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 + * + * @api + */ + public function get($name, $path = '/', $domain = null) + { + $this->flushExpiredCookies(); + + if (!empty($domain)) { + return isset($this->cookieJar[$domain][$path][$name]) ? $this->cookieJar[$domain][$path][$name] : null; + } + + // avoid relying on this behavior that is mainly here for BC reasons + foreach ($this->cookieJar as $domain => $cookies) { + if (isset($cookies[$path][$name])) { + return $cookies[$path][$name]; + } + } + + return null; + } + + /** + * 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 + * + * @api + */ + 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. + * + * @api + */ + 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 Boolean $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..a22847e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/History.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\BrowserKit; + +/** + * History. + * + * @author Fabien Potencier + */ +class History +{ + protected $stack = array(); + protected $position = -1; + + /** + * Constructor. + */ + public function __construct() + { + $this->clear(); + } + + /** + * 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 Boolean 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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Component/BrowserKit/README.md b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/README.md new file mode 100644 index 0000000..da19188 --- /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 provide 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/fabpot/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.phar install --dev + $ 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..6d381d2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Request.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\BrowserKit; + +/** + * Request object. + * + * @author Fabien Potencier + * + * @api + */ +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 + * + * @api + */ + 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 + * + * @api + */ + public function getUri() + { + return $this->uri; + } + + /** + * Gets the request HTTP method. + * + * @return string The request HTTP method + * + * @api + */ + public function getMethod() + { + return $this->method; + } + + /** + * Gets the request parameters. + * + * @return array The request parameters + * + * @api + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets the request server files. + * + * @return array The request files + * + * @api + */ + public function getFiles() + { + return $this->files; + } + + /** + * Gets the request cookies. + * + * @return array The request cookies + * + * @api + */ + public function getCookies() + { + return $this->cookies; + } + + /** + * Gets the request server parameters. + * + * @return array The request server parameters + * + * @api + */ + public function getServer() + { + return $this->server; + } + + /** + * Gets the request raw body data. + * + * @return string The request raw body data. + * + * @api + */ + 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..182fdda --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Response.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\BrowserKit; + +/** + * Response object. + * + * @author Fabien Potencier + * + * @api + */ +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 integer $status The response status code + * @param array $headers An array of headers + * + * @api + */ + 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 + * + * @api + */ + public function getContent() + { + return $this->content; + } + + /** + * Gets the response status code. + * + * @return integer The response status code + * + * @api + */ + public function getStatus() + { + return $this->status; + } + + /** + * Gets the response headers. + * + * @return array The response headers + * + * @api + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * Gets a response header. + * + * @param string $header The header name + * @param Boolean $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) + { + foreach ($this->headers as $key => $value) { + if (str_replace('-', '_', strtolower($key)) == str_replace('-', '_', strtolower($header))) { + 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..d0609a6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/ClientTest.php @@ -0,0 +1,520 @@ + + * + * 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\Request; +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 +{ + /** + * @covers Symfony\Component\BrowserKit\Client::getHistory + */ + public function testGetHistory() + { + $client = new TestClient(array(), $history = new History()); + $this->assertSame($history, $client->getHistory(), '->getHistory() returns the History'); + } + + /** + * @covers Symfony\Component\BrowserKit\Client::getCookieJar + */ + public function testGetCookieJar() + { + $client = new TestClient(array(), null, $cookieJar = new CookieJar()); + $this->assertSame($cookieJar, $client->getCookieJar(), '->getCookieJar() returns the CookieJar'); + } + + /** + * @covers Symfony\Component\BrowserKit\Client::getRequest + */ + 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 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()); + } + + /** + * @covers Symfony\Component\BrowserKit\Client::getCrawler + */ + 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'); + } + + 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'); + } + + 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() + { + if (!class_exists('Symfony\Component\DomCrawler\Crawler')) { + $this->markTestSkipped('The "DomCrawler" component is not available'); + } + + if (!class_exists('Symfony\Component\CssSelector\CssSelector')) { + $this->markTestSkipped('The "CssSelector" component is not available'); + } + + $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() + { + if (!class_exists('Symfony\Component\DomCrawler\Crawler')) { + $this->markTestSkipped('The "DomCrawler" component is not available'); + } + + if (!class_exists('Symfony\Component\CssSelector\CssSelector')) { + $this->markTestSkipped('The "CssSelector" component is not available'); + } + + $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() + { + if (!class_exists('Symfony\Component\DomCrawler\Crawler')) { + $this->markTestSkipped('The "DomCrawler" component is not available'); + } + + if (!class_exists('Symfony\Component\CssSelector\CssSelector')) { + $this->markTestSkipped('The "CssSelector" component is not available'); + } + + $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() + { + if (!class_exists('Symfony\Component\DomCrawler\Crawler')) { + $this->markTestSkipped('The "DomCrawler" component is not available'); + } + + if (!class_exists('Symfony\Component\CssSelector\CssSelector')) { + $this->markTestSkipped('The "CssSelector" component is not available'); + } + + $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 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'); + } + + 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 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() + { + if (!class_exists('Symfony\Component\Process\Process')) { + $this->markTestSkipped('The "Process" component is not available'); + } + + $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')); + } +} 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..bdbd40e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.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 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 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/')); + } +} 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..606b2e2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/CookieTest.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\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=/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()); + } +} 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..882b730 --- /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..878752c --- /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..38a51e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/composer.json @@ -0,0 +1,39 @@ +{ + "name": "symfony/browser-kit", + "type": "library", + "description": "Symfony BrowserKit Component", + "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.3", + "symfony/dom-crawler": "~2.0" + }, + "require-dev": { + "symfony/process": "~2.0", + "symfony/css-selector": "~2.0" + }, + "suggest": { + "symfony/process": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\BrowserKit\\": "" } + }, + "target-dir": "Symfony/Component/BrowserKit", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..8dff1ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./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..c4c156f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ApcClassLoader.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\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 + * allow using it as a wrapper around the other loaders of the component (the + * ClassLoader and the UniversalClassLoader for instance) but also around any + * other autoloader following this convention (the Composer one for instance) + * + * $loader = new ClassLoader(); + * + * // register classes with namespaces + * $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 + * + * @api + */ +class ApcClassLoader +{ + private $prefix; + + /** + * The class loader object being decorated. + * + * @var \Symfony\Component\ClassLoader\ClassLoader + * A class loader object that implements the findFile() method. + */ + 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 + * + * @api + */ + 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 Boolean $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 Boolean|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/ApcUniversalClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php new file mode 100644 index 0000000..023f7ba --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.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\ClassLoader; + +/** + * ApcUniversalClassLoader implements a "universal" autoloader cached in APC for PHP 5.3. + * + * It is able to load classes that use either: + * + * * The technical interoperability standards for PHP 5.3 namespaces and + * class names (https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md); + * + * * The PEAR naming convention for classes (http://pear.php.net/). + * + * Classes from a sub-namespace or a sub-hierarchy of PEAR classes can be + * looked for in a list of locations to ease the vendoring of a sub-set of + * classes for large projects. + * + * Example usage: + * + * require 'vendor/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.php'; + * require 'vendor/symfony/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php'; + * + * use Symfony\Component\ClassLoader\ApcUniversalClassLoader; + * + * $loader = new ApcUniversalClassLoader('apc.prefix.'); + * + * // register classes with namespaces + * $loader->registerNamespaces(array( + * 'Symfony\Component' => __DIR__.'/component', + * 'Symfony' => __DIR__.'/framework', + * 'Sensio' => array(__DIR__.'/src', __DIR__.'/vendor'), + * )); + * + * // register a library using the PEAR naming convention + * $loader->registerPrefixes(array( + * 'Swift_' => __DIR__.'/Swift', + * )); + * + * // activate the autoloader + * $loader->register(); + * + * 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 Kris Wallsmith + * + * @api + */ +class ApcUniversalClassLoader extends UniversalClassLoader +{ + private $prefix; + + /** + * Constructor. + * + * @param string $prefix A prefix to create a namespace in APC + * + * @throws \RuntimeException + * + * @api + */ + public function __construct($prefix) + { + if (!extension_loaded('apc')) { + throw new \RuntimeException('Unable to use ApcUniversalClassLoader as APC is not enabled.'); + } + + $this->prefix = $prefix; + } + + /** + * Finds a file by class name while caching lookups to APC. + * + * @param string $class A class name to resolve to file + * + * @return string|null The path, if found + */ + public function findFile($class) + { + if (false === $file = apc_fetch($this->prefix.$class)) { + apc_store($this->prefix.$class, $file = parent::findFile($class)); + } + + return $file; + } +} 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..54ffb64 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/CHANGELOG.md @@ -0,0 +1,20 @@ +CHANGELOG +========= + +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..be1c7e2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassCollectionLoader.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\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 Boolean $autoReload Whether to flush the cache when the cache is stale or not + * @param Boolean $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()); + if (function_exists('get_declared_traits')) { + $declared = array_merge($declared, 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(md5(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(); + + if (function_exists('get_declared_traits')) { + 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(); + $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..1a35979 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassLoader.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; + +/** + * 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])) { + $this->prefixes[$prefix] = array_merge( + $this->prefixes[$prefix], + (array) $paths + ); + } else { + $this->prefixes[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include for class files. + * + * @param Boolean $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 Boolean + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Registers this instance as an autoloader. + * + * @param Boolean $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 Boolean|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 (0 === strpos($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..3b09305 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassMapGenerator.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\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); + $T_TRAIT = version_compare(PHP_VERSION, '5.4', '<') ? -1 : T_TRAIT; + + $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: + // 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/DebugClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/DebugClassLoader.php new file mode 100644 index 0000000..842f474 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/DebugClassLoader.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\ClassLoader; + +/** + * Autoloader checking if the class is really defined in the file found. + * + * The DebugClassLoader will wrap all registered autoloaders providing a + * findFile method and will throw an exception if a file is found but does + * not declare the class. + * + * @author Fabien Potencier + * @author Christophe Coevoet + * + * @api + */ +class DebugClassLoader +{ + private $classFinder; + + /** + * Constructor. + * + * @param object $classFinder + * + * @api + */ + public function __construct($classFinder) + { + $this->classFinder = $classFinder; + } + + /** + * Replaces all autoloaders implementing a findFile method by a DebugClassLoader wrapper. + */ + public static function enable() + { + 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 && method_exists($function[0], 'findFile')) { + $function = array(new static($function[0]), 'loadClass'); + } + + spl_autoload_register($function); + } + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Finds a file by class name + * + * @param string $class A class name to resolve to file + * + * @return string|null + */ + public function findFile($class) + { + return $this->classFinder->findFile($class); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return Boolean|null True, if loaded + * + * @throws \RuntimeException + */ + public function loadClass($class) + { + if ($file = $this->classFinder->findFile($class)) { + require $file; + + if (!class_exists($class, false) && !interface_exists($class, false) && (!function_exists('trait_exists') || !trait_exists($class, false))) { + 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)); + } + + return true; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/DebugUniversalClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/DebugUniversalClassLoader.php new file mode 100644 index 0000000..96c6290 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/DebugUniversalClassLoader.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\ClassLoader; + +/** + * Checks that the class is actually declared in the included file. + * + * @author Fabien Potencier + */ +class DebugUniversalClassLoader extends UniversalClassLoader +{ + /** + * Replaces all regular UniversalClassLoader instances by a DebugUniversalClassLoader ones. + */ + public static function enable() + { + 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 UniversalClassLoader) { + $loader = new static(); + $loader->registerNamespaceFallbacks($function[0]->getNamespaceFallbacks()); + $loader->registerPrefixFallbacks($function[0]->getPrefixFallbacks()); + $loader->registerNamespaces($function[0]->getNamespaces()); + $loader->registerPrefixes($function[0]->getPrefixes()); + $loader->useIncludePath($function[0]->getUseIncludePath()); + + $function[0] = $loader; + } + + spl_autoload_register($function); + } + } + + /** + * {@inheritDoc} + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + if (!class_exists($class, false) && !interface_exists($class, false) && (!function_exists('trait_exists') || !trait_exists($class, false))) { + 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)); + } + } + } +} 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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Component/ClassLoader/MapClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/MapClassLoader.php new file mode 100644 index 0000000..82010a7 --- /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 Boolean $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/README.md b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/README.md new file mode 100644 index 0000000..5ac31c6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/README.md @@ -0,0 +1,69 @@ +ClassLoader Component +===================== + +ClassLoader loads your project classes automatically if they follow some +standard PHP conventions. + +The Universal ClassLoader is able to autoload classes that implement the PSR-0 +standard or the PEAR naming convention. + +First, register the autoloader: + + require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php'; + + use Symfony\Component\ClassLoader\UniversalClassLoader; + + $loader = new UniversalClassLoader(); + $loader->register(); + +Then, register some namespaces with the `registerNamespace()` method: + + $loader->registerNamespace('Symfony', __DIR__.'/src'); + $loader->registerNamespace('Monolog', __DIR__.'/vendor/monolog/src'); + +The `registerNamespace()` method takes a namespace prefix and a path where to +look for the classes as arguments. + +You can also register a sub-namespaces: + + $loader->registerNamespace('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: + + $loader->registerNamespace('Symfony', array(__DIR__.'/src', __DIR__.'/symfony/src')); + +Alternatively, you can use the `registerNamespaces()` method to register more +than one namespace at once: + + $loader->registerNamespaces(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 based version of the universal +class loader: + + require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php'; + require_once __DIR__.'/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php'; + + use Symfony\Component\ClassLoader\ApcUniversalClassLoader; + + $loader = new ApcUniversalClassLoader('apc.prefix.'); + +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.phar install --dev + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ApcUniversalClassLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ApcUniversalClassLoaderTest.php new file mode 100644 index 0000000..9a7acfd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ApcUniversalClassLoaderTest.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\Component\ClassLoader\Tests; + +use Symfony\Component\ClassLoader\ApcUniversalClassLoader; + +class ApcUniversalClassLoaderTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!extension_loaded('apc')) { + $this->markTestSkipped('The apc extension is not available.'); + } + + if (!(ini_get('apc.enabled') && ini_get('apc.enable_cli'))) { + $this->markTestSkipped('The apc extension is available, but not enabled.'); + } else { + apc_clear_cache('user'); + } + } + + protected function tearDown() + { + if (ini_get('apc.enabled') && ini_get('apc.enable_cli')) { + apc_clear_cache('user'); + } + } + + public function testConstructor() + { + $loader = new ApcUniversalClassLoader('test.prefix.'); + $loader->registerNamespace('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + + $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 ApcUniversalClassLoader('test.prefix.'); + $loader->registerNamespace('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->registerPrefix('Apc_Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $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'), + array('\\Apc\\Namespaced\\Bar', '\\Apc\\Namespaced\\Bar', '->loadClass() loads Apc\Namespaced\Bar class with a leading slash'), + array('Apc_Pearlike_Bar', '\\Apc_Pearlike_Bar', '->loadClass() loads Apc_Pearlike_Bar class with a leading slash'), + ); + } + + /** + * @dataProvider getLoadClassFromFallbackTests + */ + public function testLoadClassFromFallback($className, $testClassName, $message) + { + $loader = new ApcUniversalClassLoader('test.prefix.fallback'); + $loader->registerNamespace('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->registerPrefix('Apc_Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->registerNamespaceFallbacks(array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/fallback')); + $loader->registerPrefixFallbacks(array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/fallback')); + $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 ApcUniversalClassLoader('test.prefix.collision.'); + $loader->registerNamespaces($namespaces); + + $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 ApcUniversalClassLoader('test.prefix.collision.'); + $loader->registerPrefixes($prefixes); + + $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..dfa51e3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php @@ -0,0 +1,260 @@ + + * + * 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() + { + if (version_compare(phpversion(), '5.4', '<')) { + $this->markTestSkipped('Requires PHP > 5.4'); + + return; + } + + 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) + { + if (version_compare(phpversion(), '5.4', '<')) { + $this->markTestSkipped('Requires PHP > 5.4'); + + return; + } + + 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', + )), + ); + } + + /** + * @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 exising Pearlike3_Bar class with a leading slash'), + ); + } + + public function testAddPrefix() + { + $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(2, $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..18f64f7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ClassMapGeneratorTest.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\ClassLoader\Tests; + +use Symfony\Component\ClassLoader\ClassMapGenerator; + +class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var string $workspace + */ + private $workspace = null; + + public function prepare_workspace() + { + $this->workspace = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.time().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, $expected) + { + $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', + )), + ); + + if (version_compare(PHP_VERSION, '5.4', '>=')) { + $data[] = 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', + )); + } + + return $data; + } + + public function testCreateMapFinderSupport() + { + if (!class_exists('Symfony\\Component\\Finder\\Finder')) { + $this->markTestSkipped('Finder component is not available'); + } + + $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] = strtr($path, '\\', '/'); + } + foreach ($actual as $ns => $path) { + $actual[$ns] = strtr($path, '\\', '/'); + } + $this->assertEquals($expected, $actual, $message); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/DebugClassLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/DebugClassLoaderTest.php new file mode 100644 index 0000000..873515c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/DebugClassLoaderTest.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\ClassLoader\Tests; + +use Symfony\Component\ClassLoader\ClassLoader; +use Symfony\Component\ClassLoader\DebugClassLoader; + +class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase +{ + private $loader; + + protected function setUp() + { + $this->loader = new ClassLoader(); + spl_autoload_register(array($this->loader, 'loadClass')); + } + + protected function tearDown() + { + spl_autoload_unregister(array($this->loader, 'loadClass')); + } + + public function testIdempotence() + { + DebugClassLoader::enable(); + 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('classFinder'); + $reflProp->setAccessible(true); + + $this->assertNotInstanceOf('Symfony\Component\ClassLoader\DebugClassLoader', $reflProp->getValue($function[0])); + + return; + } + } + + throw new \Exception('DebugClassLoader did not register'); + } +} 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..dff891d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/ClassesWithParents/A.php @@ -0,0 +1,5 @@ + + * + * 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..53d5200 --- /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 shoult 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..09d7a8f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeInterface.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 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..5a859a9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeParent.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 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..d19e07f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/multipleNs.php @@ -0,0 +1,14 @@ + + * + * 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..a5537ac --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/deps/traits.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 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\UniversalClassLoader; + +class UniversalClassLoaderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getLoadClassTests + */ + public function testLoadClass($className, $testClassName, $message) + { + $loader = new UniversalClassLoader(); + $loader->registerNamespace('Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->registerPrefix('Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $this->assertTrue($loader->loadClass($testClassName)); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassTests() + { + return array( + array('\\Namespaced\\Foo', 'Namespaced\\Foo', '->loadClass() loads Namespaced\Foo class'), + array('\\Pearlike_Foo', 'Pearlike_Foo', '->loadClass() loads Pearlike_Foo class'), + ); + } + + public function testUseIncludePath() + { + $loader = new UniversalClassLoader(); + $this->assertFalse($loader->getUseIncludePath()); + + $this->assertNull($loader->findFile('Foo')); + + $includePath = get_include_path(); + + $loader->useIncludePath(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); + } + + public function testGetNamespaces() + { + $loader = new UniversalClassLoader(); + $loader->registerNamespace('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->registerNamespace('Bar', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->registerNamespace('Bas', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $namespaces = $loader->getNamespaces(); + $this->assertArrayHasKey('Foo', $namespaces); + $this->assertArrayNotHasKey('Foo1', $namespaces); + $this->assertArrayHasKey('Bar', $namespaces); + $this->assertArrayHasKey('Bas', $namespaces); + } + + public function testGetPrefixes() + { + $loader = new UniversalClassLoader(); + $loader->registerPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->registerPrefix('Bar', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->registerPrefix('Bas', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertArrayNotHasKey('Foo1', $prefixes); + $this->assertArrayHasKey('Bar', $prefixes); + $this->assertArrayHasKey('Bas', $prefixes); + } + + /** + * @dataProvider getLoadClassFromFallbackTests + */ + public function testLoadClassFromFallback($className, $testClassName, $message) + { + $loader = new UniversalClassLoader(); + $loader->registerNamespace('Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->registerPrefix('Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->registerNamespaceFallbacks(array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/fallback')); + $loader->registerPrefixFallbacks(array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/fallback')); + $this->assertTrue($loader->loadClass($testClassName)); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassFromFallbackTests() + { + return array( + array('\\Namespaced\\Baz', 'Namespaced\\Baz', '->loadClass() loads Namespaced\Baz class'), + array('\\Pearlike_Baz', 'Pearlike_Baz', '->loadClass() loads Pearlike_Baz class'), + array('\\Namespaced\\FooBar', 'Namespaced\\FooBar', '->loadClass() loads Namespaced\Baz class from fallback dir'), + array('\\Pearlike_FooBar', 'Pearlike_FooBar', '->loadClass() loads Pearlike_Baz class from fallback dir'), + ); + } + + public function testRegisterPrefixFallback() + { + $loader = new UniversalClassLoader(); + $loader->registerPrefixFallback(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/fallback'); + $this->assertEquals(array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/fallback'), $loader->getPrefixFallbacks()); + } + + public function testRegisterNamespaceFallback() + { + $loader = new UniversalClassLoader(); + $loader->registerNamespaceFallback(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/Namespaced/fallback'); + $this->assertEquals(array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/Namespaced/fallback'), $loader->getNamespaceFallbacks()); + } + + /** + * @dataProvider getLoadClassNamespaceCollisionTests + */ + public function testLoadClassNamespaceCollision($namespaces, $className, $message) + { + $loader = new UniversalClassLoader(); + $loader->registerNamespaces($namespaces); + + $this->assertTrue($loader->loadClass($className)); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassNamespaceCollisionTests() + { + return array( + array( + array( + 'NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'NamespaceCollision\A\Foo', + '->loadClass() loads NamespaceCollision\A\Foo from alpha.', + ), + array( + array( + 'NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + 'NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'NamespaceCollision\A\Bar', + '->loadClass() loads NamespaceCollision\A\Bar from alpha.', + ), + array( + array( + 'NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'NamespaceCollision\A\B\Foo', + '->loadClass() loads NamespaceCollision\A\B\Foo from beta.', + ), + array( + array( + 'NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + 'NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'NamespaceCollision\A\B\Bar', + '->loadClass() loads NamespaceCollision\A\B\Bar from beta.', + ), + ); + } + + /** + * @dataProvider getLoadClassPrefixCollisionTests + */ + public function testLoadClassPrefixCollision($prefixes, $className, $message) + { + $loader = new UniversalClassLoader(); + $loader->registerPrefixes($prefixes); + + $this->assertTrue($loader->loadClass($className)); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassPrefixCollisionTests() + { + return array( + array( + array( + 'PrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'PrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'PrefixCollision_A_Foo', + '->loadClass() loads PrefixCollision_A_Foo from alpha.', + ), + array( + array( + 'PrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + 'PrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'PrefixCollision_A_Bar', + '->loadClass() loads PrefixCollision_A_Bar from alpha.', + ), + array( + array( + 'PrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'PrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'PrefixCollision_A_B_Foo', + '->loadClass() loads PrefixCollision_A_B_Foo from beta.', + ), + array( + array( + 'PrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + 'PrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'PrefixCollision_A_B_Bar', + '->loadClass() loads PrefixCollision_A_B_Bar from beta.', + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.php new file mode 100644 index 0000000..734af74 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.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\ClassLoader; + +/** + * UniversalClassLoader implements a "universal" autoloader for PHP 5.3. + * + * It is able to load classes that use either: + * + * * The technical interoperability standards for PHP 5.3 namespaces and + * class names (https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md); + * + * * The PEAR naming convention for classes (http://pear.php.net/). + * + * Classes from a sub-namespace or a sub-hierarchy of PEAR classes can be + * looked for in a list of locations to ease the vendoring of a sub-set of + * classes for large projects. + * + * Example usage: + * + * $loader = new UniversalClassLoader(); + * + * // register classes with namespaces + * $loader->registerNamespaces(array( + * 'Symfony\Component' => __DIR__.'/component', + * 'Symfony' => __DIR__.'/framework', + * 'Sensio' => array(__DIR__.'/src', __DIR__.'/vendor'), + * )); + * + * // register a library using the PEAR naming convention + * $loader->registerPrefixes(array( + * 'Swift_' => __DIR__.'/Swift', + * )); + * + * + * // to enable searching the include path (e.g. for PEAR packages) + * $loader->useIncludePath(true); + * + * // activate the autoloader + * $loader->register(); + * + * 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 + * + * @api + */ +class UniversalClassLoader +{ + private $namespaces = array(); + private $prefixes = array(); + private $namespaceFallbacks = array(); + private $prefixFallbacks = array(); + private $useIncludePath = false; + + /** + * Turns on searching the include for class files. Allows easy loading + * of installed PEAR packages + * + * @param Boolean $useIncludePath + */ + public function useIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return Boolean + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Gets the configured namespaces. + * + * @return array A hash with namespaces as keys and directories as values + */ + public function getNamespaces() + { + return $this->namespaces; + } + + /** + * Gets the configured class prefixes. + * + * @return array A hash with class prefixes as keys and directories as values + */ + public function getPrefixes() + { + return $this->prefixes; + } + + /** + * Gets the directory(ies) to use as a fallback for namespaces. + * + * @return array An array of directories + */ + public function getNamespaceFallbacks() + { + return $this->namespaceFallbacks; + } + + /** + * Gets the directory(ies) to use as a fallback for class prefixes. + * + * @return array An array of directories + */ + public function getPrefixFallbacks() + { + return $this->prefixFallbacks; + } + + /** + * Registers the directory to use as a fallback for namespaces. + * + * @param array $dirs An array of directories + * + * @api + */ + public function registerNamespaceFallbacks(array $dirs) + { + $this->namespaceFallbacks = $dirs; + } + + /** + * Registers a directory to use as a fallback for namespaces. + * + * @param string $dir A directory + */ + public function registerNamespaceFallback($dir) + { + $this->namespaceFallbacks[] = $dir; + } + + /** + * Registers directories to use as a fallback for class prefixes. + * + * @param array $dirs An array of directories + * + * @api + */ + public function registerPrefixFallbacks(array $dirs) + { + $this->prefixFallbacks = $dirs; + } + + /** + * Registers a directory to use as a fallback for class prefixes. + * + * @param string $dir A directory + */ + public function registerPrefixFallback($dir) + { + $this->prefixFallbacks[] = $dir; + } + + /** + * Registers an array of namespaces + * + * @param array $namespaces An array of namespaces (namespaces as keys and locations as values) + * + * @api + */ + public function registerNamespaces(array $namespaces) + { + foreach ($namespaces as $namespace => $locations) { + $this->namespaces[$namespace] = (array) $locations; + } + } + + /** + * Registers a namespace. + * + * @param string $namespace The namespace + * @param array|string $paths The location(s) of the namespace + * + * @api + */ + public function registerNamespace($namespace, $paths) + { + $this->namespaces[$namespace] = (array) $paths; + } + + /** + * Registers an array of classes using the PEAR naming convention. + * + * @param array $classes An array of classes (prefixes as keys and locations as values) + * + * @api + */ + public function registerPrefixes(array $classes) + { + foreach ($classes as $prefix => $locations) { + $this->prefixes[$prefix] = (array) $locations; + } + } + + /** + * Registers a set of classes using the PEAR naming convention. + * + * @param string $prefix The classes prefix + * @param array|string $paths The location(s) of the classes + * + * @api + */ + public function registerPrefix($prefix, $paths) + { + $this->prefixes[$prefix] = (array) $paths; + } + + /** + * Registers this instance as an autoloader. + * + * @param Boolean $prepend Whether to prepend the autoloader or not + * + * @api + */ + 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 + * + * @return Boolean|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 + $namespace = substr($class, 0, $pos); + $className = substr($class, $pos + 1); + $normalizedClass = str_replace('\\', DIRECTORY_SEPARATOR, $namespace).DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $className).'.php'; + foreach ($this->namespaces as $ns => $dirs) { + if (0 !== strpos($namespace, $ns)) { + continue; + } + + foreach ($dirs as $dir) { + $file = $dir.DIRECTORY_SEPARATOR.$normalizedClass; + if (is_file($file)) { + return $file; + } + } + } + + foreach ($this->namespaceFallbacks as $dir) { + $file = $dir.DIRECTORY_SEPARATOR.$normalizedClass; + if (is_file($file)) { + return $file; + } + } + + } else { + // PEAR-like class name + $normalizedClass = str_replace('_', DIRECTORY_SEPARATOR, $class).'.php'; + foreach ($this->prefixes as $prefix => $dirs) { + if (0 !== strpos($class, $prefix)) { + continue; + } + + foreach ($dirs as $dir) { + $file = $dir.DIRECTORY_SEPARATOR.$normalizedClass; + if (is_file($file)) { + return $file; + } + } + } + + foreach ($this->prefixFallbacks as $dir) { + $file = $dir.DIRECTORY_SEPARATOR.$normalizedClass; + if (is_file($file)) { + return $file; + } + } + } + + if ($this->useIncludePath && $file = stream_resolve_include_path($normalizedClass)) { + return $file; + } + } +} 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..3d09fa9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/WinCacheClassLoader.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\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 and the UniversalClassLoader for instance) but also around any + * other autoloader following this convention (the Composer one for instance) + * + * $loader = new ClassLoader(); + * + * // register classes with namespaces + * $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; + + /** + * The class loader object being decorated. + * + * @var \Symfony\Component\ClassLoader\ClassLoader + * A class loader object that implements the findFile() method. + */ + 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 Boolean $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 Boolean|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..31bb006 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/XcacheClassLoader.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\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 and the UniversalClassLoader for instance) but also around any + * other autoloader following this convention (the Composer one for instance) + * + * $loader = new ClassLoader(); + * + * // register classes with namespaces + * $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 + * + * @api + */ +class XcacheClassLoader +{ + private $prefix; + private $classFinder; + + /** + * Constructor. + * + * @param string $prefix A prefix to create a namespace in Xcache + * @param object $classFinder An object that implements findFile() method. + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + * + * @api + */ + public function __construct($prefix, $classFinder) + { + if (!extension_loaded('Xcache')) { + throw new \RuntimeException('Unable to use XcacheClassLoader as Xcache is not enabled.'); + } + + if (!method_exists($classFinder, 'findFile')) { + throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); + } + + $this->prefix = $prefix; + $this->classFinder = $classFinder; + } + + /** + * Registers this instance as an autoloader. + * + * @param Boolean $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 Boolean|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 { + xcache_set($this->prefix.$class, $file = $this->classFinder->findFile($class)); + } + + return $file; + } +} 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..06a1c62 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/class-loader", + "type": "library", + "description": "Symfony ClassLoader Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "minimum-stability": "dev", + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/finder": "~2.0" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\ClassLoader\\": "" } + }, + "target-dir": "Symfony/Component/ClassLoader", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..0d29c11 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./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..59b30a3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/CHANGELOG.md @@ -0,0 +1,21 @@ +CHANGELOG +========= + +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..cbb1984 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCache.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\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Filesystem\Filesystem; + +/** + * ConfigCache manages PHP cache files. + * + * When debug is enabled, it knows when to flush the cache + * thanks to an array of ResourceInterface instances. + * + * @author Fabien Potencier + */ +class ConfigCache +{ + private $debug; + private $file; + + /** + * Constructor. + * + * @param string $file The absolute cache path + * @param Boolean $debug Whether debugging is enabled or not + */ + public function __construct($file, $debug) + { + $this->file = $file; + $this->debug = (Boolean) $debug; + } + + /** + * Gets the cache file path. + * + * @return string The cache file path + */ + public function __toString() + { + return $this->file; + } + + /** + * Checks if the cache is still fresh. + * + * This method always returns true when debug is off and the + * cache file exists. + * + * @return Boolean true if the cache is fresh, false otherwise + */ + public function isFresh() + { + if (!is_file($this->file)) { + return false; + } + + if (!$this->debug) { + return true; + } + + $metadata = $this->getMetaFile(); + if (!is_file($metadata)) { + return false; + } + + $time = filemtime($this->file); + $meta = unserialize(file_get_contents($metadata)); + foreach ($meta as $resource) { + if (!$resource->isFresh($time)) { + return false; + } + } + + return true; + } + + /** + * Writes cache. + * + * @param string $content The content to write in the cache + * @param ResourceInterface[] $metadata An array of ResourceInterface instances + * + * @throws \RuntimeException When cache file can't be wrote + */ + public function write($content, array $metadata = null) + { + $mode = 0666 & ~umask(); + $filesystem = new Filesystem(); + $filesystem->dumpFile($this->file, $content, $mode); + + if (null !== $metadata && true === $this->debug) { + $filesystem->dumpFile($this->getMetaFile(), serialize($metadata), $mode); + } + } + + /** + * 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/Definition/ArrayNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ArrayNode.php new file mode 100644 index 0000000..cf9ba08 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ArrayNode.php @@ -0,0 +1,400 @@ + + * + * 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; + protected $children; + protected $allowFalse; + protected $allowNewKeys; + protected $addIfNotSet; + protected $performDeepMerging; + protected $ignoreExtraKeys; + protected $normalizeKeys; + + /** + * Constructor. + * + * @param string $name The Node's name + * @param NodeInterface $parent The node parent + */ + public function __construct($name, NodeInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->children = array(); + $this->xmlRemappings = array(); + $this->removeKeyAttribute = true; + $this->allowFalse = false; + $this->addIfNotSet = false; + $this->allowNewKeys = true; + $this->performDeepMerging = true; + $this->normalizeKeys = true; + } + + public function setNormalizeKeys($normalizeKeys) + { + $this->normalizeKeys = (Boolean) $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; + } + + foreach ($value as $k => $v) { + if (false !== strpos($k, '-') && false === strpos($k, '_') && !array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) { + $value[$normalizedKey] = $v; + unset($value[$k]); + } + } + + return $value; + } + + /** + * 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; + } + + /** + * Sets whether to add default values for this array if it has not been + * defined in any of the configuration files. + * + * @param Boolean $boolean + */ + public function setAddIfNotSet($boolean) + { + $this->addIfNotSet = (Boolean) $boolean; + } + + /** + * Sets whether false is allowed as value indicating that the array should be unset. + * + * @param Boolean $allow + */ + public function setAllowFalse($allow) + { + $this->allowFalse = (Boolean) $allow; + } + + /** + * Sets whether new keys can be defined in subsequent configurations. + * + * @param Boolean $allow + */ + public function setAllowNewKeys($allow) + { + $this->allowNewKeys = (Boolean) $allow; + } + + /** + * Sets if deep merging should occur. + * + * @param Boolean $boolean + */ + public function setPerformDeepMerging($boolean) + { + $this->performDeepMerging = (Boolean) $boolean; + } + + /** + * Whether extra keys should just be ignore without an exception. + * + * @param Boolean $boolean To allow extra keys + */ + public function setIgnoreExtraKeys($boolean) + { + $this->ignoreExtraKeys = (Boolean) $boolean; + } + + /** + * 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 Boolean + */ + 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 (empty($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 $unset) { + 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) + )); + $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 ($this->children as $name => $child) { + if (array_key_exists($name, $value)) { + $normalized[$name] = $child->normalize($value[$name]); + unset($value[$name]); + } + } + + // if extra fields are present, throw exception + if (count($value) && !$this->ignoreExtraKeys) { + $msg = sprintf('Unrecognized options "%s" under "%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 $transformation) { + list($singular, $plural) = $transformation; + + 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..cbb8df9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/BaseNode.php @@ -0,0 +1,353 @@ + + * + * 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; + +/** + * The base node class + * + * @author Johannes M. Schmitt + */ +abstract class BaseNode implements NodeInterface +{ + protected $name; + protected $parent; + protected $normalizationClosures; + protected $finalValidationClosures; + protected $allowOverwrite; + protected $required; + protected $equivalentValues; + 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; + $this->normalizationClosures = array(); + $this->finalValidationClosures = array(); + $this->allowOverwrite = true; + $this->required = false; + $this->equivalentValues = array(); + } + + 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 Boolean $boolean Required node + */ + public function setRequired($boolean) + { + $this->required = (Boolean) $boolean; + } + + /** + * Sets if this node can be overridden. + * + * @param Boolean $allow + */ + public function setAllowOverwrite($allow) + { + $this->allowOverwrite = (Boolean) $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 Boolean + */ + 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; + } + + /** + * Finalizes a value, applying all finalization closures. + * + * @param mixed $value The value to finalize + * + * @return mixed The finalized value + * + * @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 $correctEx) { + throw $correctEx; + } catch (\Exception $invalid) { + throw new InvalidConfigurationException(sprintf( + 'Invalid configuration for path "%s": %s', + $this->getPath(), + $invalid->getMessage() + ), $invalid->getCode(), $invalid); + } + } + + 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..fb37d62 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/BooleanNode.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\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) + )); + $ex->setPath($this->getPath()); + + throw $ex; + } + } +} 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..a658050 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -0,0 +1,496 @@ + + * + * 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; + protected $ignoreExtraKeys; + protected $children; + protected $prototype; + protected $atLeastOne; + protected $allowNewKeys; + protected $key; + protected $removeKeyItem; + protected $addDefaults; + protected $addDefaultChildren; + protected $nodeBuilder; + protected $normalizeKeys; + + /** + * {@inheritDoc} + */ + public function __construct($name, NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->children = array(); + $this->addDefaults = false; + $this->addDefaultChildren = false; + $this->allowNewKeys = true; + $this->atLeastOne = false; + $this->allowEmptyValue = true; + $this->performDeepMerging = true; + $this->nullEquivalent = array(); + $this->trueEquivalent = array(); + $this->normalizeKeys = true; + } + + /** + * 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 integer|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 Boolean $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 Boolean $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. 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. + * + * @return ArrayNodeDefinition + */ + public function ignoreExtraKeys() + { + $this->ignoreExtraKeys = true; + + return $this; + } + + /** + * Sets key normalization. + * + * @param Boolean $bool Whether to enable key normalization + * + * @return ArrayNodeDefinition + */ + public function normalizeKeys($bool) + { + $this->normalizeKeys = (Boolean) $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); + $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_integer($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..7ee4d4d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/BooleanNodeDefinition.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\Builder; + +use Symfony\Component\Config\Definition\BooleanNode; + +/** + * 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); + } + +} 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..67fc63b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.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\Definition\Builder; + +use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition; + +/** + * Enum Node Definition. + * + * @author Johannes M. Schmitt + */ +class EnumNodeDefinition extends ScalarNodeDefinition +{ + private $values; + + 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..7e06944 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.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\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 ExprBuilder) { + $expressions[$k] = function($v) use ($expr) { + return call_user_func($expr->ifPart, $v) ? call_user_func($expr->thenPart, $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..5e09ff5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/MergeBuilder.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\Definition\Builder; + +/** + * This class builds merge conditions. + * + * @author Johannes M. Schmitt + */ +class MergeBuilder +{ + protected $node; + public $allowFalse; + public $allowOverwrite; + + /** + * Constructor + * + * @param NodeDefinition $node The related node + */ + public function __construct(NodeDefinition $node) + { + $this->node = $node; + $this->allowFalse = false; + $this->allowOverwrite = true; + } + + /** + * Sets whether the node can be unset. + * + * @param Boolean $allow + * + * @return MergeBuilder + */ + public function allowUnset($allow = true) + { + $this->allowFalse = $allow; + + return $this; + } + + /** + * Sets whether the node can be overwritten. + * + * @param Boolean $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..5ea1c0e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.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\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..289e1c4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php @@ -0,0 +1,347 @@ + + * + * 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; + protected $required; + protected $merge; + protected $allowEmptyValue; + protected $nullEquivalent; + protected $trueEquivalent; + protected $falseEquivalent; + /** + * @var NodeParentInterface|NodeInterface + */ + protected $parent; + protected $attributes = array(); + + /** + * Constructor + * + * @param string $name The name of the node + * @param NodeParentInterface $parent The parent + */ + public function __construct($name, NodeParentInterface $parent = null) + { + $this->parent = $parent; + $this->name = $name; + $this->default = false; + $this->required = false; + $this->trueEquivalent = true; + $this->falseEquivalent = false; + } + + /** + * Sets the parent node. + * + * @param NodeParentInterface $parent The parent + * + * @return NodeDefinition + */ + public function setParent(NodeParentInterface $parent) + { + $this->parent = $parent; + + return $this; + } + + /** + * Sets info message. + * + * @param string $info The info text + * + * @return NodeDefinition + */ + public function info($info) + { + return $this->attribute('info', $info); + } + + /** + * Sets example configuration. + * + * @param string|array $example + * + * @return NodeDefinition + */ + public function example($example) + { + return $this->attribute('example', $example); + } + + /** + * Sets an attribute on the node. + * + * @param string $key + * @param mixed $value + * + * @return NodeDefinition + */ + public function attribute($key, $value) + { + $this->attributes[$key] = $value; + + return $this; + } + + /** + * Returns the parent node. + * + * @return NodeParentInterface The builder of the parent node + */ + public function end() + { + return $this->parent; + } + + /** + * Creates the node. + * + * @param Boolean $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 + */ + public function defaultValue($value) + { + $this->default = true; + $this->defaultValue = $value; + + return $this; + } + + /** + * Sets the node as required. + * + * @return NodeDefinition + */ + public function isRequired() + { + $this->required = true; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains null. + * + * @param mixed $value + * + * @return NodeDefinition + */ + public function treatNullLike($value) + { + $this->nullEquivalent = $value; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains true. + * + * @param mixed $value + * + * @return NodeDefinition + */ + public function treatTrueLike($value) + { + $this->trueEquivalent = $value; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains false. + * + * @param mixed $value + * + * @return NodeDefinition + */ + public function treatFalseLike($value) + { + $this->falseEquivalent = $value; + + return $this; + } + + /** + * Sets null as the default value. + * + * @return NodeDefinition + */ + public function defaultNull() + { + return $this->defaultValue(null); + } + + /** + * Sets true as the default value. + * + * @return NodeDefinition + */ + public function defaultTrue() + { + return $this->defaultValue(true); + } + + /** + * Sets false as the default value. + * + * @return NodeDefinition + */ + 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 + */ + 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 Boolean $deny Whether the overwriting is forbidden or not + * + * @return NodeDefinition + */ + 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..24f3971 --- /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..87f25b7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.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\Definition\Builder; + +/** + * This class builds normalization conditions. + * + * @author Johannes M. Schmitt + */ +class NormalizationBuilder +{ + protected $node; + public $before; + public $remappings; + + /** + * Constructor + * + * @param NodeDefinition $node The related node + */ + public function __construct(NodeDefinition $node) + { + $this->node = $node; + $this->keys = false; + $this->remappings = array(); + $this->before = array(); + } + + /** + * 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..5b6e955 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NumericNodeDefinition.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\Definition\Builder; + +/** + * Abstract class that contain common code of integer and float node definition. + * + * @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; + } +} 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..005c26b --- /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..6a115fe --- /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..22f54a1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ValidationBuilder.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; + +/** + * This class builds validation conditions. + * + * @author Christophe Coevoet + */ +class ValidationBuilder +{ + protected $node; + public $rules; + + /** + * Constructor + * + * @param NodeDefinition $node The related node + */ + public function __construct(NodeDefinition $node) + { + $this->node = $node; + + $this->rules = array(); + } + + /** + * 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..75da3ab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.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\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); + } + + if (false === $this->allowEmptyValue) { + $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..336cb00 --- /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/EnumNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/EnumNode.php new file mode 100644 index 0000000..1622725 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/EnumNode.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\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\ScalarNode; + +/** + * 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 (count($values) <= 1) { + throw new \InvalidArgumentException('$values must contain at least two distinct elements.'); + } + + 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..1fda6c2 --- /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..840e3f3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/InvalidConfigurationException.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\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; + + public function setPath($path) + { + $this->path = $path; + } + + public function getPath() + { + return $this->path; + } +} 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..17c570a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/FloatNode.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\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))); + $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..dbc0412 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/IntegerNode.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\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))); + $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..cdbc0ef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/NodeInterface.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\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 Boolean If the node is required + */ + public function isRequired(); + + /** + * Returns true when the node has a default value. + * + * @return Boolean 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..df45f2e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/NumericNode.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\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: %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: %s', $value, $this->getPath(), $this->max); + } + if (isset($errorMsg)) { + $ex = new InvalidConfigurationException($errorMsg); + $ex->setPath($this->getPath()); + throw $ex; + } + + return $value; + } +} 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..2e76156 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php @@ -0,0 +1,345 @@ + + * + * 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; + protected $minNumberOfElements; + protected $defaultValue; + protected $defaultChildren; + + /** + * Constructor. + * + * @param string $name The Node's name + * @param NodeInterface $parent The node parent + */ + public function __construct($name, NodeInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->minNumberOfElements = 0; + $this->defaultValue = array(); + } + + /** + * Sets the minimum number of elements that a prototype based node must + * contain. By default this is zero, meaning no elements. + * + * @param integer $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 Boolean $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 Boolean + */ + public function hasDefaultValue() + { + return true; + } + + /** + * Adds default children when none are set. + * + * @param integer|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_integer($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 $unset) { + 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/ReferenceDumper.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ReferenceDumper.php new file mode 100644 index 0000000..5430902 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ReferenceDumper.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\Config\Definition; + +/** + * Dumps a reference configuration for the given configuration/node instance. + * + * Currently, only YML format is supported. + * + * @author Kevin Bond + */ +class ReferenceDumper +{ + 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 integer $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()) { + $keyNode = new ArrayNode($key, $node); + $keyNode->setInfo('Prototype'); + + // 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 = '[]'; + } + } + } else { + $default = '~'; + + if ($node->hasDefaultValue()) { + $default = $node->getDefaultValue(); + + if (true === $default) { + $default = 'true'; + } elseif (false === $default) { + $default = 'false'; + } elseif (null === $default) { + $default = '~'; + } elseif (is_array($default)) { + if ($node->hasDefaultValue() && count($defaultArray = $node->getDefaultValue())) { + $default = ''; + } elseif (!is_array($example)) { + $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/ScalarNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ScalarNode.php new file mode 100644 index 0000000..51141c4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ScalarNode.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\Config\Definition; + +use Symfony\Component\Config\Definition\VariableNode; +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) + )); + $ex->setPath($this->getPath()); + + throw $ex; + } + } +} 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..69dfea6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/VariableNode.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\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() + { + return $this->defaultValue instanceof \Closure ? call_user_func($this->defaultValue) : $this->defaultValue; + } + + /** + * Sets if this node is allowed to have an empty value. + * + * @param Boolean $boolean True if this entity will accept empty values. + */ + public function setAllowEmptyValue($boolean) + { + $this->allowEmptyValue = (Boolean) $boolean; + } + + /** + * {@inheritDoc} + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritDoc} + */ + protected function validateType($value) + { + } + + /** + * {@inheritDoc} + */ + protected function finalizeValue($value) + { + if (!$this->allowEmptyValue && empty($value)) { + $ex = new InvalidConfigurationException(sprintf( + 'The path "%s" cannot contain an empty value, but got %s.', + $this->getPath(), + json_encode($value) + )); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } + + /** + * {@inheritDoc} + */ + protected function normalizeValue($value) + { + return $value; + } + + /** + * {@inheritDoc} + */ + protected function mergeValues($leftSide, $rightSide) + { + return $rightSide; + } +} 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..b1ad442 --- /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]); + + call_user_func('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..71fe8d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Exception/FileLoaderLoadException.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\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 integer $code The error code + * @param \Exception $previous A previous exception + */ + public function __construct($resource, $sourceResource = null, $code = null, $previous = null) + { + if (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); + } elseif ($previous) { + // include the previous exception, to help the user see what might be the underlying cause + $message .= ' '.sprintf('(%s)', $previous->getMessage()); + } + + 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..7f9dadc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/FileLocator.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; + +/** + * 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; + } + + /** + * Returns a full path for a given file name. + * + * @param mixed $name The file name to locate + * @param string $currentPath The current path + * @param Boolean $first Whether to return the first occurrence or an array of filenames + * + * @return string|array The full path to the file|An array of file paths + * + * @throws \InvalidArgumentException When file is not found + */ + public function locate($name, $currentPath = null, $first = true) + { + if ($this->isAbsolutePath($name)) { + if (!file_exists($name)) { + throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $name)); + } + + return $name; + } + + $filepaths = array(); + if (null !== $currentPath && file_exists($file = $currentPath.DIRECTORY_SEPARATOR.$name)) { + if (true === $first) { + return $file; + } + $filepaths[] = $file; + } + + foreach ($this->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%s).', $name, null !== $currentPath ? $currentPath.', ' : '', implode(', ', $this->paths))); + } + + return array_values(array_unique($filepaths)); + } + + /** + * Returns whether the file path is an absolute path. + * + * @param string $file A file path + * + * @return Boolean + */ + 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..4ff19b4 --- /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 mixed $name The file name to locate + * @param string $currentPath The current path + * @param Boolean $first Whether to return the first occurrence or an array of filenames + * + * @return string|array The full path to the file|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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/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..775946b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/DelegatingLoader.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\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; + } + + /** + * Loads a resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return mixed + * + * @throws FileLoaderLoadException if no loader is found. + */ + 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) ? false : true; + } +} 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..d96d236 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/FileLoader.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\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 +{ + protected static $loading = array(); + + protected $locator; + + private $currentDir; + + /** + * Constructor. + * + * @param FileLocatorInterface $locator A FileLocatorInterface instance + */ + public function __construct(FileLocatorInterface $locator) + { + $this->locator = $locator; + } + + public function setCurrentDir($dir) + { + $this->currentDir = $dir; + } + + public function getLocator() + { + return $this->locator; + } + + /** + * Imports a resource. + * + * @param mixed $resource A Resource + * @param string $type The resource type + * @param Boolean $ignoreErrors Whether to ignore import errors or not + * @param string $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 FileLoader && null !== $this->currentDir) { + $resource = $this->locator->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; + + $ret = $loader->load($resource, $type); + + 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..705a6a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/Loader.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\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; + + /** + * Gets the loader resolver. + * + * @return LoaderResolverInterface A LoaderResolverInterface instance + */ + public function getResolver() + { + return $this->resolver; + } + + /** + * Sets the loader resolver. + * + * @param LoaderResolverInterface $resolver A LoaderResolverInterface instance + */ + public function setResolver(LoaderResolverInterface $resolver) + { + $this->resolver = $resolver; + } + + /** + * Imports a resource. + * + * @param mixed $resource A Resource + * @param string $type The resource type + * + * @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 $type The resource type + * + * @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..d0ac5b8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/LoaderInterface.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\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 $type The resource type + */ + public function load($resource, $type = null); + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean 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..2340fe0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/LoaderResolver.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\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; + + /** + * Constructor. + * + * @param LoaderInterface[] $loaders An array of loaders + */ + public function __construct(array $loaders = array()) + { + $this->loaders = array(); + foreach ($loaders as $loader) { + $this->addLoader($loader); + } + } + + /** + * Returns a loader able to load the resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return LoaderInterface|false A LoaderInterface instance + */ + 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..57d8781 --- /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 $type The resource type + * + * @return LoaderInterface A LoaderInterface instance + */ + 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..c7865b3 --- /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.phar install --dev + $ 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..5ccd204 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/DirectoryResource.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\Config\Resource; + +/** + * DirectoryResource represents a resources stored in a subdirectory tree. + * + * @author Fabien Potencier + */ +class DirectoryResource implements ResourceInterface, \Serializable +{ + private $resource; + private $pattern; + + /** + * Constructor. + * + * @param string $resource The file path to the resource + * @param string $pattern A pattern to restrict monitored files + */ + public function __construct($resource, $pattern = null) + { + $this->resource = $resource; + $this->pattern = $pattern; + } + + /** + * Returns a string representation of the Resource. + * + * @return string A string representation of the Resource + */ + public function __toString() + { + return (string) $this->resource; + } + + /** + * Returns the resource tied to this Resource. + * + * @return mixed The resource + */ + public function getResource() + { + return $this->resource; + } + + public function getPattern() + { + return $this->pattern; + } + + /** + * Returns true if the resource has not been updated since the given timestamp. + * + * @param integer $timestamp The last time the resource was loaded + * + * @return Boolean true if the resource has not been updated, false otherwise + */ + 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/FileResource.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/FileResource.php new file mode 100644 index 0000000..619f84b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/FileResource.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\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 ResourceInterface, \Serializable +{ + private $resource; + + /** + * Constructor. + * + * @param string $resource The file path to the resource + */ + public function __construct($resource) + { + $this->resource = realpath($resource); + } + + /** + * Returns a string representation of the Resource. + * + * @return string A string representation of the Resource + */ + public function __toString() + { + return (string) $this->resource; + } + + /** + * Returns the resource tied to this Resource. + * + * @return mixed The resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * Returns true if the resource has not been updated since the given timestamp. + * + * @param integer $timestamp The last time the resource was loaded + * + * @return Boolean true if the resource has not been updated, false otherwise + */ + public function isFresh($timestamp) + { + if (!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..d624a57 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/ResourceInterface.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\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. + * + * @return string A string representation of the Resource + */ + public function __toString(); + + /** + * Returns true if the resource has not been updated since the given timestamp. + * + * @param integer $timestamp The last time the resource was loaded + * + * @return Boolean true if the resource has not been updated, false otherwise + */ + public function isFresh($timestamp); + + /** + * Returns the resource tied to this Resource. + * + * @return mixed The resource + */ + public function getResource(); +} 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..8925dd8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/ConfigCacheTest.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\Config\Tests; + +use Symfony\Component\Config\ConfigCache; +use Symfony\Component\Config\Resource\FileResource; + +class ConfigTest extends \PHPUnit_Framework_TestCase +{ + private $resourceFile = null; + + private $cacheFile = null; + + private $metaFile = null; + + public function setUp() + { + $this->resourceFile = tempnam(sys_get_temp_dir(), '_resource'); + $this->cacheFile = tempnam(sys_get_temp_dir(), 'config_'); + $this->metaFile = $this->cacheFile.'.meta'; + + $this->makeCacheFresh(); + $this->generateMetaFile(); + } + + public function tearDown() + { + $files = array($this->cacheFile, $this->metaFile, $this->resourceFile); + + foreach ($files as $file) { + if (file_exists($file)) { + unlink($file); + } + } + } + + public function testToString() + { + $cache = new ConfigCache($this->cacheFile, true); + + $this->assertSame($this->cacheFile, (string) $cache); + } + + public function testCacheIsNotFreshIfFileDoesNotExist() + { + unlink($this->cacheFile); + + $cache = new ConfigCache($this->cacheFile, false); + + $this->assertFalse($cache->isFresh()); + } + + public function testCacheIsAlwaysFreshIfFileExistsWithDebugDisabled() + { + $this->makeCacheStale(); + + $cache = new ConfigCache($this->cacheFile, false); + + $this->assertTrue($cache->isFresh()); + } + + public function testCacheIsNotFreshWithoutMetaFile() + { + unlink($this->metaFile); + + $cache = new ConfigCache($this->cacheFile, true); + + $this->assertFalse($cache->isFresh()); + } + + public function testCacheIsFreshIfResourceIsFresh() + { + $cache = new ConfigCache($this->cacheFile, true); + + $this->assertTrue($cache->isFresh()); + } + + public function testCacheIsNotFreshIfOneOfTheResourcesIsNotFresh() + { + $this->makeCacheStale(); + + $cache = new ConfigCache($this->cacheFile, true); + + $this->assertFalse($cache->isFresh()); + } + + public function testWriteDumpsFile() + { + unlink($this->cacheFile); + unlink($this->metaFile); + + $cache = new ConfigCache($this->cacheFile, false); + $cache->write('FOOBAR'); + + $this->assertFileExists($this->cacheFile, 'Cache file is created'); + $this->assertSame('FOOBAR', file_get_contents($this->cacheFile)); + $this->assertFileNotExists($this->metaFile, 'Meta file is not created'); + } + + public function testWriteDumpsMetaFileWithDebugEnabled() + { + unlink($this->cacheFile); + unlink($this->metaFile); + + $metadata = array(new FileResource($this->resourceFile)); + + $cache = new ConfigCache($this->cacheFile, true); + $cache->write('FOOBAR', $metadata); + + $this->assertFileExists($this->cacheFile, 'Cache file is created'); + $this->assertFileExists($this->metaFile, 'Meta file is created'); + $this->assertSame(serialize($metadata), file_get_contents($this->metaFile)); + } + + private function makeCacheFresh() + { + touch($this->resourceFile, filemtime($this->cacheFile) - 3600); + } + + private function makeCacheStale() + { + touch($this->cacheFile, time() - 3600); + } + + private function generateMetaFile() + { + file_put_contents($this->metaFile, serialize(array(new FileResource($this->resourceFile)))); + } +} 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..34a2aec --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.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\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\ArrayNode; + +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 options "foo" under "root" + */ + public function testExceptionThrownOnUnrecognizedChild() + { + $node = new ArrayNode('root'); + $node->normalize(array('foo' => 'bar')); + } + + /** + * Tests that no exception is thrown for an unrecognized child if the + * ignoreExtraKeys option is set to true. + * + * Related to testExceptionThrownOnUnrecognizedChild + */ + public function testIgnoreExtraKeysNoException() + { + $node = new ArrayNode('roo'); + $node->setIgnoreExtraKeys(true); + + $node->normalize(array('foo' => 'bar')); + $this->assertTrue(true, 'No exception was thrown when setIgnoreExtraKeys is true'); + } + + /** + * @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('foo-bar' => null, 'foo_bar' => 'foo'), + array('foo-bar' => null, 'foo_bar' => 'foo'), + ) + ); + } +} 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..0753d64 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/BooleanNodeTest.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\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)); + } + + 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..06dacf2 --- /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/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..2b8a876 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/ExprBuilderTest.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\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($config === null ? array('key'=>'value') : $config) + ; + } + + /** + * Return a closure that will return the given value + * @param $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..8d0a845 --- /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->assertEquals(get_class($node), $class); + } + + public function testOverridingAnExistingNodeType() + { + $class = __NAMESPACE__.'\\SomeNodeDefinition'; + + $builder = new BaseNodeBuilder(); + $node = $builder + ->setNodeClass('variable', $class) + ->node('', 'variable'); + + $this->assertEquals(get_class($node), $class); + } + + public function testNodeTypesAreNotCaseSensitive() + { + $builder = new BaseNodeBuilder(); + + $node1 = $builder->node('', 'VaRiAbLe'); + $node2 = $builder->node('', 'variable'); + + $this->assertEquals(get_class($node1), get_class($node2)); + + $builder->setNodeClass('CuStOm', __NAMESPACE__.'\\SomeNodeDefinition'); + + $node1 = $builder->node('', 'CUSTOM'); + $node2 = $builder->node('', 'custom'); + + $this->assertEquals(get_class($node1), get_class($node2)); + } + + public function testNumericNodeCreation() + { + $builder = new NodeBuilder(); + + $node = $builder->integerNode('foo')->min(3)->max(5); + $this->assertEquals('Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition', get_class($node)); + + $node = $builder->floatNode('bar')->min(3.0)->max(5.0); + $this->assertEquals('Symfony\Component\Config\Definition\Builder\FloatNodeDefinition', get_class($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..1cb08f9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/NumericNodeDefinitionTest.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\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: 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: 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: 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: 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)); + } +} 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..8afbaf8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.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\Config\Tests\Definition\Builder; + +use Symfony\Component\Config\Tests\Definition\Builder\NodeBuilder as CustomNodeBuilder; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +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->assertEquals(get_class($nodeBuilder), 'Symfony\Component\Config\Tests\Definition\Builder\NodeBuilder'); + + $nodeBuilder = $nodeBuilder->arrayNode('deeper')->children(); + + $this->assertEquals(get_class($nodeBuilder), 'Symfony\Component\Config\Tests\Definition\Builder\NodeBuilder'); + } + + public function testOverrideABuiltInNodeType() + { + $builder = new TreeBuilder(); + $root = $builder->root('override', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->variableNode('variable'); + + $this->assertEquals(get_class($definition), 'Symfony\Component\Config\Tests\Definition\Builder\VariableNodeDefinition'); + } + + public function testAddANodeType() + { + $builder = new TreeBuilder(); + $root = $builder->root('override', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->barNode('variable'); + + $this->assertEquals(get_class($definition), 'Symfony\Component\Config\Tests\Definition\Builder\BarNodeDefinition'); + } + + public function testCreateABuiltInNodeTypeWithACustomNodeBuilder() + { + $builder = new TreeBuilder(); + $root = $builder->root('builtin', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->booleanNode('boolean'); + + $this->assertEquals(get_class($definition), 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition'); + } + + 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/EnumNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php new file mode 100644 index 0000000..2b84de6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.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\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 + */ + public function testConstructionWithOneValue() + { + new EnumNode('foo', null, array('foo', '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..bdf79ec --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/FloatNodeTest.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\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)); + } + + 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..1527db7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/IntegerNodeTest.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\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)); + } + + 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..d78027d --- /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..4b0e6bf --- /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 NormalizerTest 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..d200574 --- /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/ReferenceDumperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/ReferenceDumperTest.php new file mode 100644 index 0000000..137caf8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/ReferenceDumperTest.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\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\ReferenceDumper; +use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration; + +class ReferenceDumperTest extends \PHPUnit_Framework_TestCase +{ + public function testDumper() + { + $configuration = new ExampleConfiguration(); + + $dumper = new ReferenceDumper(); + $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\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()), + ); + } +} 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..8e8f442 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/FileLocatorTest.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\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 + */ + 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__); + } +} 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..7511dc6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.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\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('root'); + + $rootNode + ->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() + ->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('array_prototype') + ->children() + ->arrayNode('parameters') + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ->scalarNode('value')->isRequired()->end() + ->end() + ->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..7641e24 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/DelegatingLoaderTest.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\Loader; + +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\Loader\DelegatingLoader; + +class DelegatingLoaderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\Config\Loader\DelegatingLoader::__construct + */ + public function testConstructor() + { + $loader = new DelegatingLoader($resolver = new LoaderResolver()); + $this->assertTrue(true, '__construct() takes a loader resolver as its first argument'); + } + + /** + * @covers Symfony\Component\Config\Loader\DelegatingLoader::getResolver + * @covers Symfony\Component\Config\Loader\DelegatingLoader::setResolver + */ + 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'); + } + + /** + * @covers Symfony\Component\Config\Loader\DelegatingLoader::supports + */ + 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'); + } + + /** + * @covers Symfony\Component\Config\Loader\DelegatingLoader::load + */ + 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..6bb3c73 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.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\Config\Tests\Loader; + +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException; + +class FileLoaderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\Config\Loader\FileLoader + */ + public function testImport() + { + $locatorMock = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); + $locatorMock->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->setCurrentDir('.'); + $fileLoader->setResolver($loaderResolver = new LoaderResolver(array($fileLoader))); + + // 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 +{ + public function load($resource, $type = null) + { + return $resource; + } + + public function supports($resource, $type = null) + { + return true; + } + + public function addLoading($resource) + { + self::$loading[$resource] = true; + } + + public function removeLoading($resource) + { + unset(self::$loading[$resource]); + } + + public function clearLoading() + { + self::$loading = array(); + } +} 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..8ee276b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/LoaderResolverTest.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\Config\Tests\Loader; + +use Symfony\Component\Config\Loader\LoaderResolver; + +class LoaderResolverTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\Config\Loader\LoaderResolver::__construct + */ + 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'); + } + + /** + * @covers Symfony\Component\Config\Loader\LoaderResolver::resolve + */ + 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'); + } + + /** + * @covers Symfony\Component\Config\Loader\LoaderResolver::getLoaders + * @covers Symfony\Component\Config\Loader\LoaderResolver::addLoader + */ + 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..05ea772 --- /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..29c6312 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.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\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'); + $this->assertSame($this->directory, (string) $resource, '->__toString() 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'.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 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()); + } +} 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..5eb3d57 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.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\Tests\Resource; + +use Symfony\Component\Config\Resource\FileResource; + +class FileResourceTest extends \PHPUnit_Framework_TestCase +{ + protected $resource; + protected $file; + + protected function setUp() + { + $this->file = sys_get_temp_dir().'/tmp.xml'; + touch($this->file); + $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(time() + 10), '->isFresh() returns true if the resource has not changed'); + $this->assertFalse($this->resource->isFresh(time() - 86400), '->isFresh() returns false if the resource has been updated'); + + $resource = new FileResource('/____foo/foobar'.rand(1, 999999)); + $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the resource does not exist'); + } + + public function testSerializeUnserialize() + { + $unserialized = unserialize(serialize($this->resource)); + + $this->assertSame($this->file, $this->resource->getResource()); + } +} 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..feb796b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.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\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'))); + } + + /** + * @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' => '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(null, 'null'), + array(true, 'true'), + array(false, 'false'), + array(null, 'Null'), + array(true, 'True'), + array(false, 'False'), + array(0, '0'), + 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'), + ); + } +} + +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..2e9d86e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Util/XmlUtils.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\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 $schemaOrCallable An XSD schema file path or callable + * + * @return \DOMDocument + * + * @throws \InvalidArgumentException When loading of XML file returns error + */ + public static function loadFile($file, $schemaOrCallable = null) + { + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + libxml_clear_errors(); + + $dom = new \DOMDocument(); + $dom->validateOnParse = true; + if (!$dom->loadXML(file_get_contents($file), 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)) { + $valid = @$dom->schemaValidate($schemaOrCallable); + } 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_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 Boolean $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 null; + case ctype_digit($value): + $raw = $value; + $cast = intval($value); + + return '0' == $value[0] ? octdec($value) : (((string) $raw == (string) $cast) ? $cast : $raw); + case 'true' === $lowercaseValue: + return true; + case 'false' === $lowercaseValue: + return false; + case is_numeric($value): + return '0x' == $value[0].$value[1] ? hexdec($value) : floatval($value); + case preg_match('/^(-|\+)?[0-9]+(\.[0-9]+)?$/', $value): + return floatval($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 ? $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..6e8967c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/composer.json @@ -0,0 +1,32 @@ +{ + "name": "symfony/config", + "type": "library", + "description": "Symfony Config Component", + "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.3", + "symfony/filesystem": "~2.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Config\\": "" } + }, + "target-dir": "Symfony/Component/Config", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..7ba6e55 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./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..a3998a8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php @@ -0,0 +1,1129 @@ + + * + * 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\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +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\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\Helper\DialogHelper; +use Symfony\Component\Console\Helper\ProgressHelper; +use Symfony\Component\Console\Helper\TableHelper; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +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 + * + * @api + */ +class Application +{ + private $commands; + private $wantHelps = false; + private $runningCommand; + private $name; + private $version; + private $catchExceptions; + private $autoExit; + private $definition; + private $helperSet; + private $dispatcher; + + /** + * Constructor. + * + * @param string $name The name of the application + * @param string $version The version of the application + * + * @api + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + { + $this->name = $name; + $this->version = $version; + $this->catchExceptions = true; + $this->autoExit = true; + $this->commands = array(); + $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 integer 0 if everything went fine, or an error code + * + * @throws \Exception When doRun returns Exception + * + * @api + */ + 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; + } + // @codeCoverageIgnoreStart + exit($exitCode); + // @codeCoverageIgnoreEnd + } + + return $exitCode; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return integer 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--version', '-V'))) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + $name = $this->getCommandName($input); + if (true === $input->hasParameterOption(array('--help', '-h'))) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(array('command' => 'help')); + } else { + $this->wantHelps = true; + } + } + + if (!$name) { + $name = 'list'; + $input = new ArrayInput(array('command' => 'list')); + } + + // 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 + * + * @api + */ + 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 + * + * @api + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Set an input definition set to be used with this application + * + * @param InputDefinition $definition The input definition + * + * @api + */ + 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() + { + $messages = array( + $this->getLongVersion(), + '', + 'Usage:', + ' [options] command [arguments]', + '', + 'Options:', + ); + + foreach ($this->getDefinition()->getOptions() as $option) { + $messages[] = sprintf(' %-29s %s %s', + '--'.$option->getName().'', + $option->getShortcut() ? '-'.$option->getShortcut().'' : ' ', + $option->getDescription() + ); + } + + return implode(PHP_EOL, $messages); + } + + /** + * Sets whether to catch exceptions or not during commands execution. + * + * @param Boolean $boolean Whether to catch exceptions or not during commands execution + * + * @api + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (Boolean) $boolean; + } + + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param Boolean $boolean Whether to automatically exit after a command execution or not + * + * @api + */ + public function setAutoExit($boolean) + { + $this->autoExit = (Boolean) $boolean; + } + + /** + * Gets the name of the application. + * + * @return string The application name + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the application name. + * + * @param string $name The application name + * + * @api + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the application version. + * + * @return string The application version + * + * @api + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the application version. + * + * @param string $version The application version + * + * @api + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string The long application version + * + * @api + */ + 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 + * + * @api + */ + public function register($name) + { + return $this->add(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * @param Command[] $commands An array of commands + * + * @api + */ + 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 + * + * @api + */ + public function add(Command $command) + { + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return; + } + + $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 \InvalidArgumentException When command name given does not exist + * + * @api + */ + public function get($name) + { + if (!isset($this->commands[$name])) { + throw new \InvalidArgumentException(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 Boolean true if the command exists, false otherwise + * + * @api + */ + 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[] = $this->extractNamespace($command->getName()); + + foreach ($command->getAliases() as $alias) { + $namespaces[] = $this->extractNamespace($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 \InvalidArgumentException When namespace is incorrect or ambiguous + */ + public function findNamespace($namespace) + { + $allNamespaces = $this->getNamespaces(); + $found = ''; + foreach (explode(':', $namespace) as $i => $part) { + // select sub-namespaces matching the current namespace we found + $namespaces = array(); + foreach ($allNamespaces as $n) { + if ('' === $found || 0 === strpos($n, $found)) { + $namespaces[$n] = explode(':', $n); + } + } + + $abbrevs = static::getAbbreviations(array_unique(array_values(array_filter(array_map(function ($p) use ($i) { return isset($p[$i]) ? $p[$i] : ''; }, $namespaces))))); + + if (!isset($abbrevs[$part])) { + $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); + + if (1 <= $i) { + $part = $found.':'.$part; + } + + if ($alternatives = $this->findAlternativeNamespace($part, $abbrevs)) { + 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 \InvalidArgumentException($message); + } + + // there are multiple matches, but $part is an exact match of one of them so we select it + if (in_array($part, $abbrevs[$part])) { + $abbrevs[$part] = array($part); + } + + if (count($abbrevs[$part]) > 1) { + throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions($abbrevs[$part]))); + } + + $found .= $found ? ':' . $abbrevs[$part][0] : $abbrevs[$part][0]; + } + + return $found; + } + + /** + * 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 \InvalidArgumentException When command name is incorrect or ambiguous + * + * @api + */ + public function find($name) + { + // namespace + $namespace = ''; + $searchName = $name; + if (false !== $pos = strrpos($name, ':')) { + $namespace = $this->findNamespace(substr($name, 0, $pos)); + $searchName = $namespace.substr($name, $pos); + } + + // name + $commands = array(); + foreach ($this->commands as $command) { + $extractedNamespace = $this->extractNamespace($command->getName()); + if ($extractedNamespace === $namespace + || !empty($namespace) && 0 === strpos($extractedNamespace, $namespace) + ) { + $commands[] = $command->getName(); + } + } + + $abbrevs = static::getAbbreviations(array_unique($commands)); + if (isset($abbrevs[$searchName]) && 1 == count($abbrevs[$searchName])) { + return $this->get($abbrevs[$searchName][0]); + } + + if (isset($abbrevs[$searchName]) && in_array($searchName, $abbrevs[$searchName])) { + return $this->get($searchName); + } + + if (isset($abbrevs[$searchName]) && count($abbrevs[$searchName]) > 1) { + $suggestions = $this->getAbbreviationSuggestions($abbrevs[$searchName]); + + throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions)); + } + + // aliases + $aliases = array(); + foreach ($this->commands as $command) { + foreach ($command->getAliases() as $alias) { + $extractedNamespace = $this->extractNamespace($alias); + if ($extractedNamespace === $namespace + || !empty($namespace) && 0 === strpos($extractedNamespace, $namespace) + ) { + $aliases[] = $alias; + } + } + } + + $aliases = static::getAbbreviations(array_unique($aliases)); + if (!isset($aliases[$searchName])) { + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternativeCommands($searchName, $abbrevs)) { + 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 \InvalidArgumentException($message); + } + + if (count($aliases[$searchName]) > 1) { + throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $this->getAbbreviationSuggestions($aliases[$searchName]))); + } + + return $this->get($aliases[$searchName][0]); + } + + /** + * 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 + * + * @api + */ + 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; + } + + /** + * Returns a text representation of the Application. + * + * @param string $namespace An optional namespace name + * @param boolean $raw Whether to return raw command list + * + * @return string A string representing the Application + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. + */ + public function asText($namespace = null, $raw = false) + { + $descriptor = new TextDescriptor(); + + return $descriptor->describe($this, array('namespace' => $namespace, 'raw_text' => $raw)); + } + + /** + * Returns an XML representation of the Application. + * + * @param string $namespace An optional namespace name + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|\DOMDocument An XML string representing the Application + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. + */ + public function asXml($namespace = null, $asDom = false) + { + $descriptor = new XmlDescriptor(); + + return $descriptor->describe($this, array('namespace' => $namespace, 'as_dom' => $asDom)); + } + + /** + * Renders a caught exception. + * + * @param Exception $e An exception instance + * @param OutputInterface $output An OutputInterface instance + */ + public function renderException($e, $output) + { + $strlen = function ($string) { + if (!function_exists('mb_strlen')) { + return strlen($string); + } + + if (false === $encoding = mb_detect_encoding($string)) { + return strlen($string); + } + + return mb_strlen($string, $encoding); + }; + + do { + $title = sprintf(' [%s] ', get_class($e)); + $len = $strlen($title); + $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX; + $lines = array(); + foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) { + foreach (str_split($line, $width - 4) as $line) { + $lines[] = sprintf(' %s ', $line); + $len = max($strlen($line) + 4, $len); + } + } + + $messages = array(str_repeat(' ', $len), $title.str_repeat(' ', max(0, $len - $strlen($title)))); + + foreach ($lines as $line) { + $messages[] = $line.str_repeat(' ', $len - $strlen($line)); + } + + $messages[] = str_repeat(' ', $len); + + $output->writeln(""); + $output->writeln(""); + foreach ($messages as $message) { + $output->writeln(''.$message.''); + } + $output->writeln(""); + $output->writeln(""); + + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $output->writeln('Exception trace:'); + + // 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)); + } + + $output->writeln(""); + $output->writeln(""); + } + } while ($e = $e->getPrevious()); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName()))); + $output->writeln(""); + $output->writeln(""); + } + } + + /** + * 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 (defined('PHP_WINDOWS_VERSION_BUILD')) { + // 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); + } + + /** + * 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'))) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(array('--no-ansi'))) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) { + $input->setInteractive(false); + } + + if (function_exists('posix_isatty') && $this->getHelperSet()->has('dialog')) { + $inputStream = $this->getHelperSet()->get('dialog')->getInputStream(); + if (!posix_isatty($inputStream)) { + $input->setInteractive(false); + } + } + + if (true === $input->hasParameterOption(array('--quiet', '-q'))) { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } else { + if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) { + $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); + } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); + } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) { + $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 integer 0 if everything went fine, or an error code + */ + protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) + { + if (null === $this->dispatcher) { + return $command->run($input, $output); + } + + $event = new ConsoleCommandEvent($command, $input, $output); + $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + + try { + $exitCode = $command->run($input, $output); + } catch (\Exception $e) { + $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + $event = new ConsoleExceptionEvent($command, $input, $output, $e, $event->getExitCode()); + $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); + + throw $event->getException(); + } + + $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 DialogHelper(), + new ProgressHelper(), + new TableHelper(), + )); + } + + /** + * 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 commands of $name + * + * @param string $name The full name of the command + * @param array $abbrevs The abbreviations + * + * @return array A sorted array of similar commands + */ + private function findAlternativeCommands($name, $abbrevs) + { + $callback = function($item) { + return $item->getName(); + }; + + return $this->findAlternatives($name, $this->commands, $abbrevs, $callback); + } + + /** + * Finds alternative namespace of $name + * + * @param string $name The full name of the namespace + * @param array $abbrevs The abbreviations + * + * @return array A sorted array of similar namespace + */ + private function findAlternativeNamespace($name, $abbrevs) + { + return $this->findAlternatives($name, $this->getNamespaces(), $abbrevs); + } + + /** + * 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 + * @param array $abbrevs The abbreviations + * @param Closure|string|array $callback The callable to transform collection item before comparison + * + * @return array A sorted array of similar string + */ + private function findAlternatives($name, $collection, $abbrevs, $callback = null) + { + $alternatives = array(); + + foreach ($collection as $item) { + if (null !== $callback) { + $item = call_user_func($callback, $item); + } + + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = $lev; + } + } + + if (!$alternatives) { + foreach ($abbrevs as $key => $values) { + $lev = levenshtein($name, $key); + if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) { + foreach ($values as $value) { + $alternatives[$value] = $lev; + } + } + } + } + + asort($alternatives); + + return array_keys($alternatives); + } +} 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..e4213af --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/CHANGELOG.md @@ -0,0 +1,38 @@ +CHANGELOG +========= + +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..af1b91f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php @@ -0,0 +1,605 @@ + + * + * 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\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +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; + +/** + * Base class for all commands. + * + * @author Fabien Potencier + * + * @api + */ +class Command +{ + private $application; + private $name; + private $aliases; + private $definition; + private $help; + private $description; + private $ignoreValidationErrors; + private $applicationDefinitionMerged; + private $applicationDefinitionMergedWithArgs; + private $code; + private $synopsis; + private $helperSet; + + /** + * Constructor. + * + * @param string $name The name of the command + * + * @throws \LogicException When the command name is empty + * + * @api + */ + public function __construct($name = null) + { + $this->definition = new InputDefinition(); + $this->ignoreValidationErrors = false; + $this->applicationDefinitionMerged = false; + $this->applicationDefinitionMergedWithArgs = false; + $this->aliases = array(); + + if (null !== $name) { + $this->setName($name); + } + + $this->configure(); + + if (!$this->name) { + throw new \LogicException('The command name cannot be empty.'); + } + } + + /** + * 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 + * + * @api + */ + 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 + * + * @api + */ + 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 Boolean + */ + 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|integer 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. + * + * @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 integer The command exit code + * + * @throws \Exception + * + * @see setCode() + * @see execute() + * + * @api + */ + public function run(InputInterface $input, OutputInterface $output) + { + // force the creation of the synopsis before the merge with the app definition + $this->getSynopsis(); + + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->definition); + } catch (\Exception $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + $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() + * + * @api + */ + public function setCode($code) + { + if (!is_callable($code)) { + throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.'); + } + + $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 Boolean $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 + * + * @api + */ + 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 + * + * @api + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the InputDefinition to be used to create XML and Text 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 integer $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 + * + * @api + */ + 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 integer $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 + * + * @api + */ + 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 command name given is empty + * + * @api + */ + public function setName($name) + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * Returns the command name. + * + * @return string The command name + * + * @api + */ + 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 + * + * @api + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + * + * @return string The description for the command + * + * @api + */ + 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 + * + * @api + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + * + * @return string The help for the command + * + * @api + */ + 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()); + } + + /** + * Sets the aliases for the command. + * + * @param array $aliases An array of aliases for the command + * + * @return Command The current instance + * + * @api + */ + public function setAliases($aliases) + { + 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 + * + * @api + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @return string The synopsis + */ + public function getSynopsis() + { + if (null === $this->synopsis) { + $this->synopsis = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis())); + } + + return $this->synopsis; + } + + /** + * 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 + * + * @api + */ + public function getHelper($name) + { + return $this->helperSet->get($name); + } + + /** + * Returns a text representation of the command. + * + * @return string A string representing the command + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. + */ + public function asText() + { + $descriptor = new TextDescriptor(); + + return $descriptor->describe($this); + } + + /** + * Returns an XML representation of the command. + * + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|\DOMDocument An XML string representing the command + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. + */ + public function asXml($asDom = false) + { + $descriptor = new XmlDescriptor(); + + return $descriptor->describe($this, array('as_dom' => $asDom)); + } + + 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..2aa933e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Command/HelpCommand.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\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('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output help in other formats'), + 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')); + } + + if ($input->getOption('xml')) { + $input->setOption('format', 'xml'); + } + + $helper = new DescriptorHelper(); + $helper->describe($output, $this->command, $input->getOption('format'), $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..f3d7438 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Command/ListCommand.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\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) + { + if ($input->getOption('xml')) { + $input->setOption('format', 'xml'); + } + + $helper = new DescriptorHelper(); + $helper->describe($output, $this->getApplication(), $input->getOption('format'), $input->getOption('raw'), $input->getArgument('namespace')); + } + + /** + * {@inheritdoc} + */ + private function createDefinition() + { + return new InputDefinition(array( + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output list as XML'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output list in other formats'), + )); + } +} 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..12ede2d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/ConsoleEvents.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\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. + * + * @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. + * + * @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. + * + * @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..cdf1493 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/ApplicationDescription.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\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; + +/** + * @author Jean-François Simon + */ +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 \InvalidArgumentException + */ + public function getCommand($name) + { + if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { + throw new \InvalidArgumentException(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(); + foreach ($commands as $name => $command) { + $key = $this->application->extractNamespace($name, 1); + if (!$key) { + $key = '_global'; + } + + $namespacedCommands[$key][$name] = $command; + } + ksort($namespacedCommands); + + foreach ($namespacedCommands as &$commands) { + ksort($commands); + } + + 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..659b8f4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/Descriptor.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\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; + +/** + * @author Jean-François Simon + */ +abstract class Descriptor implements DescriptorInterface +{ + public function describe($object, array $options = array()) + { + switch (true) { + case $object instanceof InputArgument: + return $this->describeInputArgument($object, $options); + case $object instanceof InputOption: + return $this->describeInputOption($object, $options); + case $object instanceof InputDefinition: + return $this->describeInputDefinition($object, $options); + case $object instanceof Command: + return $this->describeCommand($object, $options); + case $object instanceof Application: + return $this->describeApplication($object, $options); + } + + throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object))); + } + + /** + * 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..a4e8633 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/DescriptorInterface.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\Console\Descriptor; + +/** + * Descriptor interface. + * + * @author Jean-François Simon + */ +interface DescriptorInterface +{ + /** + * Describes an InputArgument instance. + * + * @param object $object + * @param array $options + * + * @return string|mixed + */ + public function describe($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..c2b2a53 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/JsonDescriptor.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\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 + */ +class JsonDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + return $this->output(array( + 'name' => $argument->getName(), + 'is_required' => $argument->isRequired(), + 'is_array' => $argument->isArray(), + 'description' => $argument->getDescription(), + 'default' => $argument->getDefault(), + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + return $this->output(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' => $option->getDescription(), + 'default' => $option->getDefault(), + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $inputArguments = array(); + foreach ($definition->getArguments() as $name => $argument) { + $inputArguments[$name] = $this->describeInputArgument($argument, array('as_array' => true)); + } + + $inputOptions = array(); + foreach ($definition->getOptions() as $name => $option) { + $inputOptions[$name] = $this->describeInputOption($option, array('as_array' => true)); + } + + return $this->output(array('arguments' => $inputArguments, 'options' => $inputOptions), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + return $this->output(array( + 'name' => $command->getName(), + 'usage' => $command->getSynopsis(), + 'description' => $command->getDescription(), + 'help' => $command->getProcessedHelp(), + 'aliases' => $command->getAliases(), + 'definition' => $this->describeInputDefinition($command->getNativeDefinition(), array('as_array' => true)), + ), $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->describeCommand($command, array('as_array' => true)); + } + + $data = $describedNamespace + ? array('commands' => $commands, 'namespace' => $describedNamespace) + : array('commands' => $commands, 'namespaces' => array_values($description->getNamespaces())); + + return $this->output($data, $options); + } + + /** + * Outputs data as array or string according to options. + * + * @param array $data + * @param array $options + * + * @return array|string + */ + private function output(array $data, array $options) + { + if (isset($options['as_array']) && $options['as_array']) { + return $data; + } + + return json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0); + } +} 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..770af38 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.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\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 + */ +class MarkdownDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + return '**'.$argument->getName().':**'."\n\n" + .'* Name: '.($argument->getName() ?: '')."\n" + .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n" + .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n" + .'* Description: '.($argument->getDescription() ?: '')."\n" + .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`'; + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + return '**'.$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: '.($option->getDescription() ?: '')."\n" + .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'; + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $blocks = array(); + + if (count($definition->getArguments()) > 0) { + $blocks[] = '### Arguments:'; + foreach ($definition->getArguments() as $argument) { + $blocks[] = $this->describeInputArgument($argument); + } + } + + if (count($definition->getOptions()) > 0) { + $blocks[] = '### Options:'; + foreach ($definition->getOptions() as $option) { + $blocks[] = $this->describeInputOption($option); + } + } + + return implode("\n\n", $blocks); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $markdown = $command->getName()."\n" + .str_repeat('-', strlen($command->getName()))."\n\n" + .'* Description: '.($command->getDescription() ?: '')."\n" + .'* Usage: `'.$command->getSynopsis().'`'."\n" + .'* Aliases: '.(count($command->getAliases()) ? '`'.implode('`, `', $command->getAliases()).'`' : ''); + + if ($help = $command->getProcessedHelp()) { + $markdown .= "\n\n".$help; + } + + if ($definitionMarkdown = $this->describeInputDefinition($command->getNativeDefinition())) { + $markdown .= "\n\n".$definitionMarkdown; + } + + return $markdown; + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + $blocks = array($application->getName()."\n".str_repeat('=', strlen($application->getName()))); + + foreach ($description->getNamespaces() as $namespace) { + if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $blocks[] = '**'.$namespace['id'].':**'; + } + + $blocks[] = implode("\n", array_map(function ($commandName) { + return '* '.$commandName; + } , $namespace['commands'])); + } + + foreach ($description->getCommands() as $command) { + $blocks[] = $this->describeCommand($command); + } + + return implode("\n\n", $blocks); + } +} 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..2003237 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/TextDescriptor.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\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 + */ +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 = ''; + } + + $nameWidth = isset($options['name_width']) ? $options['name_width'] : strlen($argument->getName()); + $output = str_replace("\n", "\n".str_repeat(' ', $nameWidth + 2), $argument->getDescription()); + $output = sprintf(" %-${nameWidth}s %s%s", $argument->getName(), $output, $default); + + return isset($options['raw_text']) && $options['raw_text'] ? strip_tags($output) : $output; + } + + /** + * {@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 = ''; + } + + $nameWidth = isset($options['name_width']) ? $options['name_width'] : strlen($option->getName()); + $nameWithShortcutWidth = $nameWidth - strlen($option->getName()) - 2; + + $output = sprintf(" %s %-${nameWithShortcutWidth}s%s%s%s", + '--'.$option->getName(), + $option->getShortcut() ? sprintf('(-%s) ', $option->getShortcut()) : '', + str_replace("\n", "\n".str_repeat(' ', $nameWidth + 2), $option->getDescription()), + $default, + $option->isArray() ? ' (multiple values allowed)' : '' + ); + + return isset($options['raw_text']) && $options['raw_text'] ? strip_tags($output) : $output; + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $nameWidth = 0; + foreach ($definition->getOptions() as $option) { + $nameLength = strlen($option->getName()) + 2; + if ($option->getShortcut()) { + $nameLength += strlen($option->getShortcut()) + 3; + } + $nameWidth = max($nameWidth, $nameLength); + } + foreach ($definition->getArguments() as $argument) { + $nameWidth = max($nameWidth, strlen($argument->getName())); + } + ++$nameWidth; + + $messages = array(); + + if ($definition->getArguments()) { + $messages[] = 'Arguments:'; + foreach ($definition->getArguments() as $argument) { + $messages[] = $this->describeInputArgument($argument, array('name_width' => $nameWidth)); + } + $messages[] = ''; + } + + if ($definition->getOptions()) { + $messages[] = 'Options:'; + foreach ($definition->getOptions() as $option) { + $messages[] = $this->describeInputOption($option, array('name_width' => $nameWidth)); + } + $messages[] = ''; + } + + $output = implode("\n", $messages); + + return isset($options['raw_text']) && $options['raw_text'] ? strip_tags($output) : $output; + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + $messages = array('Usage:', ' '.$command->getSynopsis(), ''); + + if ($command->getAliases()) { + $messages[] = 'Aliases: '.implode(', ', $command->getAliases()).''; + } + + $messages[] = $this->describeInputDefinition($command->getNativeDefinition()); + + if ($help = $command->getProcessedHelp()) { + $messages[] = 'Help:'; + $messages[] = ' '.str_replace("\n", "\n ", $help)."\n"; + } + + $output = implode("\n", $messages); + + return isset($options['raw_text']) && $options['raw_text'] ? strip_tags($output) : $output; + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + $messages = array(); + + if (isset($options['raw_text']) && $options['raw_text']) { + $width = $this->getColumnWidth($description->getCommands()); + + foreach ($description->getCommands() as $command) { + $messages[] = sprintf("%-${width}s %s", $command->getName(), $command->getDescription()); + } + } else { + $width = $this->getColumnWidth($description->getCommands()); + + $messages[] = $application->getHelp(); + $messages[] = ''; + + if ($describedNamespace) { + $messages[] = sprintf("Available commands for the \"%s\" namespace:", $describedNamespace); + } else { + $messages[] = 'Available commands:'; + } + + // add commands by namespace + foreach ($description->getNamespaces() as $namespace) { + if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $messages[] = ''.$namespace['id'].''; + } + + foreach ($namespace['commands'] as $name) { + $messages[] = sprintf(" %-${width}s %s", $name, $description->getCommand($name)->getDescription()); + } + } + } + + $output = implode("\n", $messages); + + return isset($options['raw_text']) && $options['raw_text'] ? strip_tags($output) : $output; + } + + /** + * Formats input option/argument default value. + * + * @param mixed $default + * + * @return string + */ + private function formatDefaultValue($default) + { + if (version_compare(PHP_VERSION, '5.4', '<')) { + return str_replace('\/', '/', json_encode($default)); + } + + return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + /** + * @param Command[] $commands + * + * @return int + */ + private function getColumnWidth(array $commands) + { + $width = 0; + foreach ($commands as $command) { + $width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width; + } + + return $width + 2; + } +} 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..c828a9c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/XmlDescriptor.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\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 + */ +class XmlDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $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 $this->output($dom, $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $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 $this->output($dom, $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $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->describeInputArgument($argument, array('as_dom' => true))); + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($definition->getOptions() as $option) { + $this->appendDocument($optionsXML, $this->describeInputOption($option, array('as_dom' => true))); + } + + return $this->output($dom, $options); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $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($usageXML = $dom->createElement('usage')); + $usageXML->appendChild($dom->createTextNode(sprintf($command->getSynopsis(), ''))); + + $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()))); + + $commandXML->appendChild($aliasesXML = $dom->createElement('aliases')); + foreach ($command->getAliases() as $alias) { + $aliasesXML->appendChild($aliasXML = $dom->createElement('alias')); + $aliasXML->appendChild($dom->createTextNode($alias)); + } + + $definitionXML = $this->describeInputDefinition($command->getNativeDefinition(), array('as_dom' => true)); + $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0)); + + return $this->output($dom, $options); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($rootXml = $dom->createElement('symfony')); + $rootXml->appendChild($commandsXML = $dom->createElement('commands')); + + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + if ($describedNamespace) { + $commandsXML->setAttribute('namespace', $describedNamespace); + } + + foreach ($description->getCommands() as $command) { + $this->appendDocument($commandsXML, $this->describeCommand($command, array('as_dom' => true))); + } + + if (!$describedNamespace) { + $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces')); + + foreach ($description->getNamespaces() as $namespace) { + $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace')); + $namespaceArrayXML->setAttribute('id', $namespace['id']); + + foreach ($namespace['commands'] as $name) { + $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command')); + $commandXML->appendChild($dom->createTextNode($name)); + } + } + } + + return $this->output($dom, $options); + } + + /** + * 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)); + } + } + + /** + * Outputs document as DOMDocument or string according to options. + * + * @param \DOMDocument $dom + * @param array $options + * + * @return \DOMDocument|string + */ + private function output(\DOMDocument $dom, array $options) + { + if (isset($options['as_dom']) && $options['as_dom']) { + return $dom; + } + + $dom->formatOutput = true; + + return $dom->saveXML(); + } +} 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..c754299 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleCommandEvent.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\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to do things before the command is executed. + * + * @author Fabien Potencier + */ +class ConsoleCommandEvent extends ConsoleEvent +{ +} 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..fa054ec --- /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 = $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 integer 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..9f80cb7 --- /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 integer + */ + 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 integer $exitCode The command exit code + */ + public function setExitCode($exitCode) + { + $this->exitCode = $exitCode; + } + + /** + * Gets the exit code. + * + * @return integer The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} 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..db4a2fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatter.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\Console\Formatter; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov + * + * @api + */ +class OutputFormatter implements OutputFormatterInterface +{ + /** + * The pattern to phrase the format. + */ + const FORMAT_PATTERN = '#(\\\\?)<(/?)([a-z][a-z0-9_=;-]+)?>((?: [^<\\\\]+ | (?!<(?:/?[a-z]|/>)). | .(?<=\\\\<) )*)#isx'; + + 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 + * + * @api + */ + public function __construct($decorated = null, array $styles = array()) + { + $this->decorated = (Boolean) $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 Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + public function setDecorated($decorated) + { + $this->decorated = (Boolean) $decorated; + } + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + public function isDecorated() + { + return $this->decorated; + } + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + * + * @api + */ + public function setStyle($name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return Boolean + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + public function format($message) + { + $message = preg_replace_callback(self::FORMAT_PATTERN, array($this, 'replaceStyle'), $message); + + return str_replace('\\<', '<', $message); + } + + /** + * @return OutputFormatterStyleStack + */ + public function getStyleStack() + { + return $this->styleStack; + } + + /** + * Replaces style of the output. + * + * @param array $match + * + * @return string The replaced style + */ + private function replaceStyle($match) + { + // we got "\<" escaped char + if ('\\' === $match[1]) { + return $this->applyCurrentStyle($match[0]); + } + + if ('' === $match[3]) { + if ('/' === $match[2]) { + // we got "" tag + $this->styleStack->pop(); + + return $this->applyCurrentStyle($match[4]); + } + + // we got "<>" tag + return '<>'.$this->applyCurrentStyle($match[4]); + } + + if (isset($this->styles[strtolower($match[3])])) { + $style = $this->styles[strtolower($match[3])]; + } else { + $style = $this->createStyleFromString($match[3]); + + if (false === $style) { + return $this->applyCurrentStyle($match[0]); + } + } + + if ('/' === $match[2]) { + $this->styleStack->pop($style); + } else { + $this->styleStack->push($style); + } + + return $this->applyCurrentStyle($match[4]); + } + + /** + * Tries to create new style instance from string. + * + * @param string $string + * + * @return OutputFormatterStyle|Boolean false if string is not format string + */ + private function createStyleFromString($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 { + $style->setOption($match[1]); + } + } + + 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..0836084 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterInterface.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\Console\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov + * + * @api + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + public function isDecorated(); + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + * + * @api + */ + public function setStyle($name, OutputFormatterStyleInterface $style); + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return Boolean + * + * @api + */ + public function hasStyle($name); + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @api + */ + public function getStyle($name); + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + * + * @api + */ + 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..ec47169 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.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\Console\Formatter; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov + * + * @api + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + private static $availableForegroundColors = array( + 'black' => 30, + 'red' => 31, + 'green' => 32, + 'yellow' => 33, + 'blue' => 34, + 'magenta' => 35, + 'cyan' => 36, + 'white' => 37 + ); + private static $availableBackgroundColors = array( + 'black' => 40, + 'red' => 41, + 'green' => 42, + 'yellow' => 43, + 'blue' => 44, + 'magenta' => 45, + 'cyan' => 46, + 'white' => 47 + ); + private static $availableOptions = array( + 'bold' => 1, + 'underscore' => 4, + 'blink' => 5, + 'reverse' => 7, + 'conceal' => 8 + ); + + private $foreground; + private $background; + private $options = array(); + + /** + * Initializes output formatter style. + * + * @param string $foreground The style foreground color name + * @param string $background The style background color name + * @param array $options The style options + * + * @api + */ + 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 $color The color name + * + * @throws \InvalidArgumentException When the color name isn't defined + * + * @api + */ + 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 $color The color name + * + * @throws \InvalidArgumentException When the color name isn't defined + * + * @api + */ + 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 + * + * @api + */ + 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 (false === array_search(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) + { + $codes = array(); + + if (null !== $this->foreground) { + $codes[] = $this->foreground; + } + if (null !== $this->background) { + $codes[] = $this->background; + } + if (count($this->options)) { + $codes = array_merge($codes, $this->options); + } + + if (0 === count($codes)) { + return $text; + } + + return sprintf("\033[%sm%s\033[0m", implode(';', $codes), $text); + } +} 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..e8642b3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.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\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov + * + * @api + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + * + * @param string $color The color name + * + * @api + */ + public function setForeground($color = null); + + /** + * Sets style background color. + * + * @param string $color The color name + * + * @api + */ + public function setBackground($color = null); + + /** + * Sets some specific style option. + * + * @param string $option The option name + * + * @api + */ + 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..5915023 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.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\Console\Formatter; + +/** + * @author Jean-François Simon + */ +class OutputFormatterStyleStack +{ + /** + * @var OutputFormatterStyleInterface[] + */ + private $styles; + + /** + * @var OutputFormatterStyleInterface + */ + private $emptyStyle; + + /** + * Constructor. + * + * @param OutputFormatterStyleInterface $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 $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/DescriptorHelper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/DescriptorHelper.php new file mode 100644 index 0000000..b897a33 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/DescriptorHelper.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\Console\Helper; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +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\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * 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. + * + * @param OutputInterface $output + * @param object $object + * @param string $format + * @param boolean $raw + */ + public function describe(OutputInterface $output, $object, $format = null, $raw = false, $namespace = null) + { + $options = array('raw_text' => $raw, 'format' => $format ?: 'txt', 'namespace' => $namespace); + $type = !$raw && 'txt' === $options['format'] ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW; + + if (!isset($this->descriptors[$options['format']])) { + throw new \InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); + } + + $descriptor = $this->descriptors[$options['format']]; + + $output->writeln($descriptor->describe($object, $options), $type); + } + + /** + * 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/DialogHelper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/DialogHelper.php new file mode 100644 index 0000000..7fc2b3a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/DialogHelper.php @@ -0,0 +1,469 @@ + + * + * 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\Formatter\OutputFormatterStyle; + +/** + * The Dialog class provides helpers to interact with the user. + * + * @author Fabien Potencier + */ +class DialogHelper extends Helper +{ + private $inputStream; + private static $shell; + private static $stty; + + /** + * Asks the user to select a value. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param array $choices List of choices to pick from + * @param Boolean $default The default answer if the user enters nothing + * @param Boolean|integer $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $errorMessage Message which will be shown if invalid value from choice list would be picked + * @param Boolean $multiselect Select more than one value separated by comma + * + * @return integer|string|array The selected value or values (the key of the choices array) + * + * @throws \InvalidArgumentException + */ + public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false) + { + $width = max(array_map('strlen', array_keys($choices))); + + $messages = (array) $question; + foreach ($choices as $key => $value) { + $messages[] = sprintf(" [%-${width}s] %s", $key, $value); + } + + $output->writeln($messages); + + $result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage, $multiselect) { + // Collapse all spaces. + $selectedChoices = str_replace(" ", "", $picked); + + 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, $picked)); + } + $selectedChoices = explode(",", $selectedChoices); + } else { + $selectedChoices = array($picked); + } + + $multiselectChoices = array(); + + foreach ($selectedChoices as $value) { + if (empty($choices[$value])) { + throw new \InvalidArgumentException(sprintf($errorMessage, $value)); + } + array_push($multiselectChoices, $value); + } + + if ($multiselect){ + return $multiselectChoices; + } + + return $picked; + }, $attempts, $default); + + return $result; + } + + /** + * Asks a question to the user. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param string $default The default answer if none is given by the user + * @param array $autocomplete List of values to autocomplete + * + * @return string The user answer + * + * @throws \RuntimeException If there is no data to read in the input stream + */ + public function ask(OutputInterface $output, $question, $default = null, array $autocomplete = null) + { + $output->write($question); + + $inputStream = $this->inputStream ?: STDIN; + + if (null === $autocomplete || !$this->hasSttyAvailable()) { + $ret = fgets($inputStream, 4096); + if (false === $ret) { + throw new \RuntimeException('Aborted'); + } + $ret = trim($ret); + } else { + $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 ('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 strlen($ret) > 0 ? $ret : $default; + } + + /** + * Asks a confirmation to the user. + * + * The question will be asked until the user answers by nothing, yes, or no. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param Boolean $default The default answer if the user enters nothing + * + * @return Boolean true if the user has confirmed, false otherwise + */ + public function askConfirmation(OutputInterface $output, $question, $default = true) + { + $answer = 'z'; + while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) { + $answer = $this->ask($output, $question); + } + + if (false === $default) { + return $answer && 'y' == strtolower($answer[0]); + } + + return !$answer || 'y' == strtolower($answer[0]); + } + + /** + * Asks a question to the user, the response is hidden + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question + * @param Boolean $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not + * + * @return string The answer + * + * @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden + */ + public function askHiddenResponse(OutputInterface $output, $question, $fallback = true) + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $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; + } + + $output->write($question); + $value = rtrim(shell_exec($exe)); + $output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if ($this->hasSttyAvailable()) { + $output->write($question); + + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -echo'); + $value = fgets($this->inputStream ?: STDIN, 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()) { + $output->write($question); + $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; + } + + if ($fallback) { + return $this->ask($output, $question); + } + + throw new \RuntimeException('Unable to hide the response'); + } + + /** + * Asks for a value and validates the response. + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param callable $validator A PHP callback + * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $default The default answer if none is given by the user + * @param array $autocomplete List of values to autocomplete + * + * @return mixed + * + * @throws \Exception When any of the validators return an error + */ + public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null, array $autocomplete = null) + { + $that = $this; + + $interviewer = function() use ($output, $question, $default, $autocomplete, $that) { + return $that->ask($output, $question, $default, $autocomplete); + }; + + return $this->validateAttempts($interviewer, $output, $validator, $attempts); + } + + /** + * Asks for a value, hide and validates the response. + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param callable $validator A PHP callback + * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param Boolean $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not + * + * @return string The response + * + * @throws \Exception When any of the validators return an error + * @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden + * + */ + public function askHiddenResponseAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $fallback = true) + { + $that = $this; + + $interviewer = function() use ($output, $question, $fallback, $that) { + return $that->askHiddenResponse($output, $question, $fallback); + }; + + return $this->validateAttempts($interviewer, $output, $validator, $attempts); + } + + /** + * 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 + */ + public function setInputStream($stream) + { + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream + * + * @return string + */ + public function getInputStream() + { + return $this->inputStream; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'dialog'; + } + + /** + * Return a valid unix shell + * + * @return string|Boolean 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; + } + + private function hasSttyAvailable() + { + if (null !== self::$stty) { + return self::$stty; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = $exitcode === 0; + } + + /** + * Validate an attempt + * + * @param callable $interviewer A callable that will ask for a question and return the result + * @param OutputInterface $output An Output instance + * @param callable $validator A PHP callback + * @param integer $attempts Max number of times to ask before giving up ; false will ask infinitely + * + * @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($interviewer, OutputInterface $output, $validator, $attempts) + { + $error = null; + while (false === $attempts || $attempts--) { + if (null !== $error) { + $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); + } + + try { + return call_user_func($validator, $interviewer()); + } catch (\Exception $error) { + } + } + + throw $error; + } +} 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..a5f1d1c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/FormatterHelper.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\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 Boolean $large Whether to return a large block + * + * @return string The formatter message + */ + public function formatBlock($messages, $style, $large = false) + { + $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(); + foreach ($lines as $line) { + $messages[] = $line.str_repeat(' ', $len - $this->strlen($line)); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + foreach ($messages as &$message) { + $message = sprintf('<%s>%s', $style, $message, $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..534b9f4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/Helper.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\Helper; + +/** + * 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_strlen if it is available. + * + * @param string $string The string to check its length + * + * @return integer The length of the string + */ + protected function strlen($string) + { + if (!function_exists('mb_strlen')) { + return strlen($string); + } + + if (false === $encoding = mb_detect_encoding($string)) { + return strlen($string); + } + + return mb_strlen($string, $encoding); + } +} 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..6d39449 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/HelperInterface.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\Console\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier + * + * @api + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + * + * @api + */ + public function setHelperSet(HelperSet $helperSet = null); + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + * + * @api + */ + public function getHelperSet(); + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + * + * @api + */ + 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..d95c9a3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/HelperSet.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\Console\Helper; + +use Symfony\Component\Console\Command\Command; + +/** + * HelperSet represents a set of helpers to be used with a command. + * + * @author Fabien Potencier + */ +class HelperSet +{ + private $helpers; + private $command; + + /** + * Constructor. + * + * @param Helper[] $helpers An array of helper. + */ + public function __construct(array $helpers = array()) + { + $this->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 Boolean 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; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProgressHelper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProgressHelper.php new file mode 100644 index 0000000..4ed92cf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProgressHelper.php @@ -0,0 +1,445 @@ + + * + * 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; + +/** + * The Progress class provides helpers to display progress output. + * + * @author Chris Jones + * @author Fabien Potencier + */ +class ProgressHelper extends Helper +{ + const FORMAT_QUIET = ' %percent%%'; + const FORMAT_NORMAL = ' %current%/%max% [%bar%] %percent%%'; + const FORMAT_VERBOSE = ' %current%/%max% [%bar%] %percent%% Elapsed: %elapsed%'; + const FORMAT_QUIET_NOMAX = ' %current%'; + const FORMAT_NORMAL_NOMAX = ' %current% [%bar%]'; + const FORMAT_VERBOSE_NOMAX = ' %current% [%bar%] Elapsed: %elapsed%'; + + // options + private $barWidth = 28; + private $barChar = '='; + private $emptyBarChar = '-'; + private $progressChar = '>'; + private $format = null; + private $redrawFreq = 1; + + private $lastMessagesLength; + private $barCharOriginal; + + /** + * @var OutputInterface + */ + private $output; + + /** + * Current step + * + * @var integer + */ + private $current; + + /** + * Maximum number of steps + * + * @var integer + */ + private $max; + + /** + * Start time of the progress bar + * + * @var integer + */ + private $startTime; + + /** + * List of formatting variables + * + * @var array + */ + private $defaultFormatVars = array( + 'current', + 'max', + 'bar', + 'percent', + 'elapsed', + ); + + /** + * Available formatting variables + * + * @var array + */ + private $formatVars; + + /** + * Stored format part widths (used for padding) + * + * @var array + */ + private $widths = array( + 'current' => 4, + 'max' => 4, + 'percent' => 3, + 'elapsed' => 6, + ); + + /** + * Various time formats + * + * @var array + */ + private $timeFormats = array( + array(0, '???'), + 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), + ); + + /** + * Sets the progress bar width. + * + * @param int $size The progress bar size + */ + public function setBarWidth($size) + { + $this->barWidth = (int) $size; + } + + /** + * Sets the bar character. + * + * @param string $char A character + */ + public function setBarCharacter($char) + { + $this->barChar = $char; + } + + /** + * Sets the empty bar character. + * + * @param string $char A character + */ + public function setEmptyBarCharacter($char) + { + $this->emptyBarChar = $char; + } + + /** + * Sets the progress bar character. + * + * @param string $char A character + */ + public function setProgressCharacter($char) + { + $this->progressChar = $char; + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + public function setFormat($format) + { + $this->format = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int $freq The frequency in seconds + */ + public function setRedrawFrequency($freq) + { + $this->redrawFreq = (int) $freq; + } + + /** + * Starts the progress output. + * + * @param OutputInterface $output An Output instance + * @param integer $max Maximum steps + */ + public function start(OutputInterface $output, $max = null) + { + $this->startTime = time(); + $this->current = 0; + $this->max = (int) $max; + $this->output = $output; + + if (null === $this->format) { + switch ($output->getVerbosity()) { + case OutputInterface::VERBOSITY_QUIET: + $this->format = self::FORMAT_QUIET_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_QUIET; + } + break; + case OutputInterface::VERBOSITY_VERBOSE: + case OutputInterface::VERBOSITY_VERY_VERBOSE: + case OutputInterface::VERBOSITY_DEBUG: + $this->format = self::FORMAT_VERBOSE_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_VERBOSE; + } + break; + default: + $this->format = self::FORMAT_NORMAL_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_NORMAL; + } + break; + } + } + + $this->initialize(); + } + + /** + * Advances the progress output X steps. + * + * @param integer $step Number of steps to advance + * @param Boolean $redraw Whether to redraw or not + * + * @throws \LogicException + */ + public function advance($step = 1, $redraw = false) + { + if (null === $this->startTime) { + throw new \LogicException('You must start the progress bar before calling advance().'); + } + + if (0 === $this->current) { + $redraw = true; + } + + $this->current += $step; + if ($redraw || 0 === $this->current % $this->redrawFreq) { + $this->display(); + } + } + + /** + * Sets the current progress. + * + * @param integer $current The current progress + * @param Boolean $redraw Whether to redraw or not + * + * @throws \LogicException + */ + public function setCurrent($current, $redraw = false) + { + if (null === $this->startTime) { + throw new \LogicException('You must start the progress bar before calling setCurrent().'); + } + + $current = (int) $current; + + if ($current < $this->current) { + throw new \LogicException('You can\'t regress the progress bar'); + } + + if (0 === $this->current) { + $redraw = true; + } + + $this->current = $current; + if ($redraw || 0 === $this->current % $this->redrawFreq) { + $this->display(); + } + } + + /** + * Outputs the current progress string. + * + * @param Boolean $finish Forces the end result + * + * @throws \LogicException + */ + public function display($finish = false) + { + if (null === $this->startTime) { + throw new \LogicException('You must start the progress bar before calling display().'); + } + + $message = $this->format; + foreach ($this->generate($finish) as $name => $value) { + $message = str_replace("%{$name}%", $value, $message); + } + $this->overwrite($this->output, $message); + } + + /** + * Finishes the progress output. + */ + public function finish() + { + if (null === $this->startTime) { + throw new \LogicException('You must start the progress bar before calling finish().'); + } + + if (null !== $this->startTime) { + if (!$this->max) { + $this->barChar = $this->barCharOriginal; + $this->display(true); + } + $this->startTime = null; + $this->output->writeln(''); + $this->output = null; + } + } + + /** + * Initializes the progress helper. + */ + private function initialize() + { + $this->formatVars = array(); + foreach ($this->defaultFormatVars as $var) { + if (false !== strpos($this->format, "%{$var}%")) { + $this->formatVars[$var] = true; + } + } + + if ($this->max > 0) { + $this->widths['max'] = $this->strlen($this->max); + $this->widths['current'] = $this->widths['max']; + } else { + $this->barCharOriginal = $this->barChar; + $this->barChar = $this->emptyBarChar; + } + } + + /** + * Generates the array map of format variables to values. + * + * @param Boolean $finish Forces the end result + * + * @return array Array of format vars and values + */ + private function generate($finish = false) + { + $vars = array(); + $percent = 0; + if ($this->max > 0) { + $percent = (double) round($this->current / $this->max, 2); + } + + if (isset($this->formatVars['bar'])) { + $completeBars = 0; + $emptyBars = 0; + if ($this->max > 0) { + $completeBars = floor($percent * $this->barWidth); + } else { + if (!$finish) { + $completeBars = floor($this->current % $this->barWidth); + } else { + $completeBars = $this->barWidth; + } + } + + $emptyBars = $this->barWidth - $completeBars - $this->strlen($this->progressChar); + $bar = str_repeat($this->barChar, $completeBars); + if ($completeBars < $this->barWidth) { + $bar .= $this->progressChar; + $bar .= str_repeat($this->emptyBarChar, $emptyBars); + } + + $vars['bar'] = $bar; + } + + if (isset($this->formatVars['elapsed'])) { + $elapsed = time() - $this->startTime; + $vars['elapsed'] = str_pad($this->humaneTime($elapsed), $this->widths['elapsed'], ' ', STR_PAD_LEFT); + } + + if (isset($this->formatVars['current'])) { + $vars['current'] = str_pad($this->current, $this->widths['current'], ' ', STR_PAD_LEFT); + } + + if (isset($this->formatVars['max'])) { + $vars['max'] = $this->max; + } + + if (isset($this->formatVars['percent'])) { + $vars['percent'] = str_pad($percent * 100, $this->widths['percent'], ' ', STR_PAD_LEFT); + } + + return $vars; + } + + /** + * Converts seconds into human-readable format. + * + * @param integer $secs Number of seconds + * + * @return string Time in readable format + */ + private function humaneTime($secs) + { + $text = ''; + foreach ($this->timeFormats as $format) { + if ($secs < $format[0]) { + if (count($format) == 2) { + $text = $format[1]; + break; + } else { + $text = ceil($secs / $format[2]).' '.$format[1]; + break; + } + } + } + + return $text; + } + + /** + * Overwrites a previous message to the output. + * + * @param OutputInterface $output An Output instance + * @param string $message The message + */ + private function overwrite(OutputInterface $output, $message) + { + $length = $this->strlen($message); + + // append whitespace to match the last line's length + if (null !== $this->lastMessagesLength && $this->lastMessagesLength > $length) { + $message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + + // carriage return + $output->write("\x0D"); + $output->write($message); + + $this->lastMessagesLength = $this->strlen($message); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'progress'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableHelper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableHelper.php new file mode 100644 index 0000000..cf18e49 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableHelper.php @@ -0,0 +1,453 @@ + + * + * 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 InvalidArgumentException; + +/** + * Provides helpers to display table output. + * + * @author Саша Стаменковић + */ +class TableHelper extends Helper +{ + const LAYOUT_DEFAULT = 0; + const LAYOUT_BORDERLESS = 1; + + /** + * Table headers. + * + * @var array + */ + private $headers = array(); + + /** + * Table rows. + * + * @var array + */ + private $rows = array(); + + // Rendering options + private $paddingChar; + private $horizontalBorderChar; + private $verticalBorderChar; + private $crossingChar; + private $cellHeaderFormat; + private $cellRowFormat; + private $borderFormat; + private $padType; + + /** + * Column widths cache. + * + * @var array + */ + private $columnWidths = array(); + + /** + * Number of columns cache. + * + * @var array + */ + private $numberOfColumns; + + /** + * @var OutputInterface + */ + private $output; + + public function __construct() + { + $this->setLayout(self::LAYOUT_DEFAULT); + } + + /** + * Sets table layout type. + * + * @param int $layout self::LAYOUT_* + * + * @return TableHelper + */ + public function setLayout($layout) + { + switch ($layout) { + case self::LAYOUT_BORDERLESS: + $this + ->setPaddingChar(' ') + ->setHorizontalBorderChar('=') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ->setCellHeaderFormat('%s') + ->setCellRowFormat('%s') + ->setBorderFormat('%s') + ->setPadType(STR_PAD_RIGHT) + ; + break; + + case self::LAYOUT_DEFAULT: + $this + ->setPaddingChar(' ') + ->setHorizontalBorderChar('-') + ->setVerticalBorderChar('|') + ->setCrossingChar('+') + ->setCellHeaderFormat('%s') + ->setCellRowFormat('%s') + ->setBorderFormat('%s') + ->setPadType(STR_PAD_RIGHT) + ; + break; + + default: + throw new InvalidArgumentException(sprintf('Invalid table layout "%s".', $layout)); + break; + }; + + return $this; + } + + public function setHeaders(array $headers) + { + $this->headers = array_values($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(array $row) + { + $this->rows[] = array_values($row); + + return $this; + } + + public function setRow($column, array $row) + { + $this->rows[$column] = $row; + + return $this; + } + + /** + * Sets padding character, used for cell padding. + * + * @param string $paddingChar + * + * @return TableHelper + */ + public function setPaddingChar($paddingChar) + { + $this->paddingChar = $paddingChar; + + return $this; + } + + /** + * Sets horizontal border character. + * + * @param string $horizontalBorderChar + * + * @return TableHelper + */ + public function setHorizontalBorderChar($horizontalBorderChar) + { + $this->horizontalBorderChar = $horizontalBorderChar; + + return $this; + } + + /** + * Sets vertical border character. + * + * @param string $verticalBorderChar + * + * @return TableHelper + */ + public function setVerticalBorderChar($verticalBorderChar) + { + $this->verticalBorderChar = $verticalBorderChar; + + return $this; + } + + /** + * Sets crossing character. + * + * @param string $crossingChar + * + * @return TableHelper + */ + public function setCrossingChar($crossingChar) + { + $this->crossingChar = $crossingChar; + + return $this; + } + + /** + * Sets header cell format. + * + * @param string $cellHeaderFormat + * + * @return TableHelper + */ + public function setCellHeaderFormat($cellHeaderFormat) + { + $this->cellHeaderFormat = $cellHeaderFormat; + + return $this; + } + + /** + * Sets row cell format. + * + * @param string $cellRowFormat + * + * @return TableHelper + */ + public function setCellRowFormat($cellRowFormat) + { + $this->cellRowFormat = $cellRowFormat; + + return $this; + } + + /** + * Sets table border format. + * + * @param string $borderFormat + * + * @return TableHelper + */ + public function setBorderFormat($borderFormat) + { + $this->borderFormat = $borderFormat; + + return $this; + } + + /** + * Sets cell padding type. + * + * @param integer $padType STR_PAD_* + * + * @return TableHelper + */ + public function setPadType($padType) + { + $this->padType = $padType; + + 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 | + * +---------------+-----------------------+------------------+ + * + * @param OutputInterface $output + */ + public function render(OutputInterface $output) + { + $this->output = $output; + + $this->renderRowSeparator(); + $this->renderRow($this->headers, $this->cellHeaderFormat); + if (!empty($this->headers)) { + $this->renderRowSeparator(); + } + foreach ($this->rows as $row) { + $this->renderRow($row, $this->cellRowFormat); + } + if (!empty($this->rows)) { + $this->renderRowSeparator(); + } + + $this->cleanup(); + } + + /** + * Renders horizontal header separator. + * + * Example: +-----+-----------+-------+ + */ + private function renderRowSeparator() + { + if (0 === $count = $this->getNumberOfColumns()) { + return; + } + + $markup = $this->crossingChar; + for ($column = 0; $column < $count; $column++) { + $markup .= str_repeat($this->horizontalBorderChar, $this->getColumnWidth($column)) + .$this->crossingChar + ; + } + + $this->output->writeln(sprintf($this->borderFormat, $markup)); + } + + /** + * Renders vertical column separator. + */ + private function renderColumnSeparator() + { + $this->output->write(sprintf($this->borderFormat, $this->verticalBorderChar)); + } + + /** + * 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(); + for ($column = 0, $count = $this->getNumberOfColumns(); $column < $count; $column++) { + $this->renderCell($row, $column, $cellFormat); + $this->renderColumnSeparator(); + } + $this->output->writeln(''); + } + + /** + * Renders table cell with padding. + * + * @param array $row + * @param integer $column + * @param string $cellFormat + */ + private function renderCell(array $row, $column, $cellFormat) + { + $cell = isset($row[$column]) ? $row[$column] : ''; + + $this->output->write(sprintf( + $cellFormat, + str_pad( + $this->paddingChar.$cell.$this->paddingChar, + $this->getColumnWidth($column), + $this->paddingChar, + $this->padType + ) + )); + } + + /** + * Gets number of columns for this table. + * + * @return int + */ + private function getNumberOfColumns() + { + if (null !== $this->numberOfColumns) { + return $this->numberOfColumns; + } + + $columns = array(0); + $columns[] = count($this->headers); + foreach ($this->rows as $row) { + $columns[] = count($row); + } + + return $this->numberOfColumns = max($columns); + } + + /** + * Gets column width. + * + * @param integer $column + * + * @return int + */ + private function getColumnWidth($column) + { + if (isset($this->columnWidths[$column])) { + return $this->columnWidths[$column]; + } + + $lengths = array(0); + $lengths[] = $this->getCellWidth($this->headers, $column); + foreach ($this->rows as $row) { + $lengths[] = $this->getCellWidth($row, $column); + } + + return $this->columnWidths[$column] = max($lengths) + 2; + } + + /** + * Gets cell width. + * + * @param array $row + * @param integer $column + * + * @return int + */ + private function getCellWidth(array $row, $column) + { + if ($column < 0) { + return 0; + } + + if (isset($row[$column])) { + return $this->strlen($row[$column]); + } + + return $this->getCellWidth($row, $column - 1); + } + + /** + * Called after rendering to cleanup cache data. + */ + private function cleanup() + { + $this->columnWidths = array(); + $this->numberOfColumns = null; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'table'; + } +} 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..6a4d1d5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/ArgvInput.php @@ -0,0 +1,345 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * 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 + * + * @api + */ +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 + * + * @api + */ + 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]) { + $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(), true); + } + } + } + + /** + * 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 false values (from a previous call to substr()) to null + if (false === $value) { + $value = null; + } + + 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) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->tokens as $v) { + 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 + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + + $tokens = $this->tokens; + while ($token = array_shift($tokens)) { + foreach ($values as $value) { + if (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() + { + $self = $this; + $tokens = array_map(function ($token) use ($self) { + if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) { + return $match[1] . $self->escapeToken($match[2]); + } + + if ($token && $token[0] !== '-') { + return $self->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..ad9f2ca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/ArrayInput.php @@ -0,0 +1,209 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * ArrayInput represents an input provided as an array. + * + * Usage: + * + * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar')); + * + * @author Fabien Potencier + * + * @api + */ +class ArrayInput extends Input +{ + private $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * @param InputDefinition $definition A InputDefinition instance + * + * @api + */ + 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) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (!is_int($k)) { + $v = $k; + } + + 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 + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (is_int($k) && 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 (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 \InvalidArgumentException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new \InvalidArgumentException(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 \InvalidArgumentException When option given doesn't exist + * @throws \InvalidArgumentException When a required value is missing + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isValueRequired()) { + throw new \InvalidArgumentException(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..fb84cf8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/Input.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\Console\Input; + +/** + * 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 +{ + protected $definition; + protected $options; + protected $arguments; + protected $interactive = true; + + /** + * Constructor. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(InputDefinition $definition = null) + { + if (null === $definition) { + $this->arguments = array(); + $this->options = array(); + $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() + { + if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) { + throw new \RuntimeException('Not enough arguments.'); + } + } + + /** + * Checks if the input is interactive. + * + * @return Boolean Returns true if the input is interactive + */ + public function isInteractive() + { + return $this->interactive; + } + + /** + * Sets the input interactivity. + * + * @param Boolean $interactive If the input should be interactive + */ + public function setInteractive($interactive) + { + $this->interactive = (Boolean) $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|integer $name The InputArgument name or position + * + * @return Boolean 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 $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 Boolean 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..ffe9772 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputArgument.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\Input; + +/** + * Represents a command line argument. + * + * @author Fabien Potencier + * + * @api + */ +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 integer $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 + * + * @api + */ + 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 Boolean 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 Boolean 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/InputDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputDefinition.php new file mode 100644 index 0000000..257d057 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputDefinition.php @@ -0,0 +1,443 @@ + + * + * 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\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; + +/** + * 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 + * + * @api + */ +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 + * + * @api + */ + public function __construct(array $definition = array()) + { + $this->setDefinition($definition); + } + + /** + * Sets the definition of the input. + * + * @param array $definition The definition array + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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|integer $name The InputArgument name or position + * + * @return InputArgument An InputArgument object + * + * @throws \InvalidArgumentException When argument given doesn't exist + * + * @api + */ + 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|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + * + * @api + */ + 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 + * + * @api + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + * + * @return integer The number of InputArguments + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + * + * @return integer 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 Boolean true if the InputOption object exists, false otherwise + * + * @api + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return InputOption[] An array of InputOption objects + * + * @api + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + * + * @param string $name The InputOption shortcut + * + * @return Boolean 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. + * + * @return string The synopsis + */ + public function getSynopsis() + { + $elements = array(); + foreach ($this->getOptions() as $option) { + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('['.($option->isValueRequired() ? '%s--%s="..."' : ($option->isValueOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName()); + } + + foreach ($this->getArguments() as $argument) { + $elements[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : '')); + + if ($argument->isArray()) { + $elements[] = sprintf('... [%sN]', $argument->getName()); + } + } + + return implode(' ', $elements); + } + + /** + * Returns a textual representation of the InputDefinition. + * + * @return string A string representing the InputDefinition + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. + */ + public function asText() + { + $descriptor = new TextDescriptor(); + + return $descriptor->describe($this); + } + + /** + * Returns an XML representation of the InputDefinition. + * + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|\DOMDocument An XML string representing the InputDefinition + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. + */ + public function asXml($asDom = false) + { + $descriptor = new XmlDescriptor(); + + return $descriptor->describe($this, array('as_dom' => $asDom)); + } +} 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..c39429d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputInterface.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\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) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values); + + /** + * 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 + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = 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|integer $name The InputArgument name or position + * + * @return Boolean 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 $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 Boolean true if the InputOption object exists, false otherwise + */ + public function hasOption($name); + + /** + * Is this input means interactive? + * + * @return Boolean + */ + public function isInteractive(); + + /** + * Sets the input interactivity. + * + * @param Boolean $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..e1fdea0 --- /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; + +/** + * Represents a command line option. + * + * @author Fabien Potencier + * + * @api + */ +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 integer $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 + * + * @api + */ + 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 Boolean 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 Boolean 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 Boolean 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 Boolean 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 Boolean + */ + 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..7f87f71 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/StringInput.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\Console\Input; + +/** + * StringInput represents an input provided as a string. + * + * Usage: + * + * $input = new StringInput('foo --bar="foobar"'); + * + * @author Fabien Potencier + * + * @api + */ +class StringInput extends ArgvInput +{ + const REGEX_STRING = '([^\s]+?)(?:\s|(?setTokens($this->tokenize($input)); + + if (null !== $definition) { + $this->bind($definition); + } + } + + /** + * 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 + // @codeCoverageIgnoreStart + throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + // @codeCoverageIgnoreEnd + } + + $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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/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..6dad744 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/ConsoleOutput.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\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; + +/** + * 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 + * + * @api + */ +class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface +{ + private $stderr; + + /** + * Constructor. + * + * @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param Boolean|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + * + * @api + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + $outputStream = 'php://stdout'; + if (!$this->hasStdoutSupport()) { + $outputStream = 'php://output'; + } + + parent::__construct(fopen($outputStream, 'w'), $verbosity, $decorated, $formatter); + + $this->stderr = new StreamOutput(fopen('php://stderr', 'w'), $verbosity, $decorated, $formatter); + } + + /** + * {@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. + * + * IBM iSeries (OS400) exhibits character-encoding issues when writing to + * STDOUT and doesn't properly convert ASCII to EBCDIC, resulting in garbage + * output. + * + * @return boolean + */ + protected function hasStdoutSupport() + { + return ('OS400' != php_uname('s')); + } +} 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..c63bd15 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/ConsoleOutputInterface.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\Console\Output; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * 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..c75cfca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/NullOutput.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\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 + * + * @api + */ +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; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = 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..0daedc3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/Output.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\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 + * + * @api + */ +abstract class Output implements OutputInterface +{ + private $verbosity; + private $formatter; + + /** + * Constructor. + * + * @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param Boolean $decorated Whether to decorate messages + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + * + * @api + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null) + { + $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; + $this->formatter = null === $formatter ? new OutputFormatter() : $formatter; + $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; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + $this->write($messages, true, $type); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + if (self::VERBOSITY_QUIET === $this->verbosity) { + return; + } + + $messages = (array) $messages; + + 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; + default: + throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type)); + } + + $this->doWrite($message, $newline); + } + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $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..83d5013 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/OutputInterface.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\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * OutputInterface is the interface implemented by all Output classes. + * + * @author Fabien Potencier + * + * @api + */ +interface OutputInterface +{ + const VERBOSITY_QUIET = 0; + const VERBOSITY_NORMAL = 1; + const VERBOSITY_VERBOSE = 2; + const VERBOSITY_VERY_VERBOSE = 3; + const VERBOSITY_DEBUG = 4; + + const OUTPUT_NORMAL = 0; + const OUTPUT_RAW = 1; + const OUTPUT_PLAIN = 2; + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines or a single string + * @param Boolean $newline Whether to add a newline + * @param integer $type The type of output (one of the OUTPUT constants) + * + * @throws \InvalidArgumentException When unknown output type is given + * + * @api + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL); + + /** + * 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 integer $type The type of output (one of the OUTPUT constants) + * + * @throws \InvalidArgumentException When unknown output type is given + * + * @api + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL); + + /** + * Sets the verbosity of the output. + * + * @param integer $level The level of verbosity (one of the VERBOSITY constants) + * + * @api + */ + public function setVerbosity($level); + + /** + * Gets the current verbosity of the output. + * + * @return integer The current level of verbosity (one of the VERBOSITY constants) + * + * @api + */ + public function getVerbosity(); + + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages + * + * @api + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + public function isDecorated(); + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + * + * @api + */ + public function setFormatter(OutputFormatterInterface $formatter); + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + * + * @api + */ + 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..09a5ca3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/StreamOutput.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\Output; + +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 + * + * @api + */ +class StreamOutput extends Output +{ + private $stream; + + /** + * Constructor. + * + * @param mixed $stream A stream resource + * @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param Boolean|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 + * + * @api + */ + 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 : ''))) { + // @codeCoverageIgnoreStart + // should never happen + throw new \RuntimeException('Unable to write output.'); + // @codeCoverageIgnoreEnd + } + + fflush($this->stream); + } + + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * - windows without ansicon and ConEmu + * - non tty consoles + * + * @return Boolean true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport() + { + // @codeCoverageIgnoreStart + if (DIRECTORY_SEPARATOR == '\\') { + return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); + } + + return function_exists('posix_isatty') && @posix_isatty($this->stream); + // @codeCoverageIgnoreEnd + } +} 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..fbf05fc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/README.md @@ -0,0 +1,63 @@ +Console Component +================= + +Console eases the creation of beautiful and testable command line interfaces. + +The Application object manages the CLI application: + + 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: + + 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.phar install --dev + $ 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](http://symfony.com/doc/current/components/console.html) + +[How to create a Console Command](http://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/Shell.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Shell.php new file mode 100644 index 0000000..ff8bef5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Shell.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\Console; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Process\ProcessBuilder; +use Symfony\Component\Process\PhpExecutableFinder; + +/** + * A Shell wraps an Application to add shell capabilities to it. + * + * Support for history and completion only works with a PHP compiled + * with readline support (either --with-readline or --with-libedit) + * + * @author Fabien Potencier + * @author Martin Hasoň + */ +class Shell +{ + private $application; + private $history; + private $output; + private $hasReadline; + private $processIsolation; + + /** + * Constructor. + * + * If there is no readline support for the current PHP executable + * a \RuntimeException exception is thrown. + * + * @param Application $application An application instance + */ + public function __construct(Application $application) + { + $this->hasReadline = function_exists('readline'); + $this->application = $application; + $this->history = getenv('HOME').'/.history_'.$application->getName(); + $this->output = new ConsoleOutput(); + $this->processIsolation = false; + } + + /** + * Runs the shell. + */ + public function run() + { + $this->application->setAutoExit(false); + $this->application->setCatchExceptions(true); + + if ($this->hasReadline) { + readline_read_history($this->history); + readline_completion_function(array($this, 'autocompleter')); + } + + $this->output->writeln($this->getHeader()); + $php = null; + if ($this->processIsolation) { + $finder = new PhpExecutableFinder(); + $php = $finder->find(); + $this->output->writeln(<<Running with process isolation, you should consider this: + * each command is executed as separate process, + * commands don't support interactivity, all params must be passed explicitly, + * commands output is not colorized. + +EOF + ); + } + + while (true) { + $command = $this->readline(); + + if (false === $command) { + $this->output->writeln("\n"); + + break; + } + + if ($this->hasReadline) { + readline_add_history($command); + readline_write_history($this->history); + } + + if ($this->processIsolation) { + $pb = new ProcessBuilder(); + + $process = $pb + ->add($php) + ->add($_SERVER['argv'][0]) + ->add($command) + ->inheritEnvironmentVariables(true) + ->getProcess() + ; + + $output = $this->output; + $process->run(function($type, $data) use ($output) { + $output->writeln($data); + }); + + $ret = $process->getExitCode(); + } else { + $ret = $this->application->run(new StringInput($command), $this->output); + } + + if (0 !== $ret) { + $this->output->writeln(sprintf('The command terminated with an error status (%s)', $ret)); + } + } + } + + /** + * Returns the shell header. + * + * @return string The header string + */ + protected function getHeader() + { + return <<{$this->application->getName()} shell ({$this->application->getVersion()}). + +At the prompt, type help for some help, +or list to get a list of available commands. + +To exit the shell, type ^D. + +EOF; + } + + /** + * Renders a prompt. + * + * @return string The prompt + */ + protected function getPrompt() + { + // using the formatter here is required when using readline + return $this->output->getFormatter()->format($this->application->getName().' > '); + } + + protected function getOutput() + { + return $this->output; + } + + protected function getApplication() + { + return $this->application; + } + + /** + * Tries to return autocompletion for the current entered text. + * + * @param string $text The last segment of the entered text + * + * @return Boolean|array A list of guessed strings or true + */ + private function autocompleter($text) + { + $info = readline_info(); + $text = substr($info['line_buffer'], 0, $info['end']); + + if ($info['point'] !== $info['end']) { + return true; + } + + // task name? + if (false === strpos($text, ' ') || !$text) { + return array_keys($this->application->all()); + } + + // options and arguments? + try { + $command = $this->application->find(substr($text, 0, strpos($text, ' '))); + } catch (\Exception $e) { + return true; + } + + $list = array('--help'); + foreach ($command->getDefinition()->getOptions() as $option) { + $list[] = '--'.$option->getName(); + } + + return $list; + } + + /** + * Reads a single line from standard input. + * + * @return string The single line from standard input + */ + private function readline() + { + if ($this->hasReadline) { + $line = readline($this->getPrompt()); + } else { + $this->output->write($this->getPrompt()); + $line = fgets(STDIN, 1024); + $line = (!$line && strlen($line) == 0) ? false : rtrim($line); + } + + return $line; + } + + public function getProcessIsolation() + { + return $this->processIsolation; + } + + public function setProcessIsolation($processIsolation) + { + $this->processIsolation = (Boolean) $processIsolation; + + if ($this->processIsolation && !class_exists('Symfony\\Component\\Process\\Process')) { + throw new \RuntimeException('Unable to isolate processes as the Symfony Process Component is not installed.'); + } + } +} 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..2e57e1a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tester/ApplicationTester.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\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; + + /** + * 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 integer 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->application->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the application. + * + * @param Boolean $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; + } +} 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..23dae24 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tester/CommandTester.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\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * Eases the testing of console commands. + * + * @author Fabien Potencier + */ +class CommandTester +{ + private $command; + private $input; + private $output; + + /** + * Constructor. + * + * @param Command $command A Command instance to test. + */ + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Executes the command. + * + * 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 integer The command exit code + */ + public function execute(array $input, array $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->command->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the command. + * + * @param Boolean $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; + } +} 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..bc1f038 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -0,0 +1,837 @@ + + * + * 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\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\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'; + } + + 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 first 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()), '->setHelp() returns a help message'); + } + + public function testAll() + { + $application = new Application(); + $commands = $application->all(); + $this->assertEquals('Symfony\\Component\\Console\\Command\\HelpCommand', get_class($commands['help']), '->all() returns the registered commands'); + + $application->add(new \FooCommand()); + $commands = $application->all('foo'); + $this->assertEquals(1, count($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'); + } + + 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 \InvalidArgumentException + * @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'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The namespace "f" is ambiguous (foo, foo1). + */ + public function testFindAmbiguousNamespace() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo2Command()); + $application->findNamespace('f'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage There are no commands defined in the "bar" namespace. + */ + public function testFindInvalidNamespace() + { + $application = new Application(); + $application->findNamespace('bar'); + } + + 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('InvalidArgumentException', $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).') + ); + } + + 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'); + } + + /** + * @dataProvider provideInvalidCommandNamesSingle + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Did you mean this + */ + public function testFindAlternativeExceptionMessageSingle($name) + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->find($name); + } + + public function provideInvalidCommandNamesSingle() + { + return array( + array('foo:baR'), + array('foO: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 an \InvalidArgumentException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + } + + // Namespace + plural + try { + $application->find('foo2:bar'); + $this->fail('->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + } + + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + // Subnamespace + plural + try { + $a = $application->find('foo3:'); + $this->fail('->find() should throw an \InvalidArgumentException if a command is ambiguous because of a subnamespace, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $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 an \InvalidArgumentException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist'); + $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, without alternatives'); + } + + try { + $application->find($commandName = 'foo'); + $this->fail('->find() throws an \InvalidArgumentException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist'); + $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + $this->assertRegExp('/foo:bar/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternative : "foo:bar"'); + $this->assertRegExp('/foo1:bar/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternative : "foo1:bar"'); + $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternative : "foo:bar1"'); + } + + // Test if "foo1" command throw an "\InvalidArgumentException" and does not contain + // "foo:bar" as alternative because "foo1" is too far from "foo:bar" + try { + $application->find($commandName = 'foo1'); + $this->fail('->find() throws an \InvalidArgumentException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist'); + $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + $this->assertFalse(strpos($e->getMessage(), 'foo:bar'), '->find() throws an \InvalidArgumentException if command does not exist, without "foo:bar" alternative'); + } + } + + 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 an \InvalidArgumentException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if namespace does not exist'); + $this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, without alternatives'); + } + + try { + $application->find('foo2:command'); + $this->fail('->find() throws an \InvalidArgumentException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if namespace does not exist'); + $this->assertRegExp('/There are no commands defined in the "foo2" namespace./', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, with alternative'); + $this->assertRegExp('/foo/', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, with alternative : "foo"'); + $this->assertRegExp('/foo1/', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, with alternative : "foo1"'); + $this->assertRegExp('/foo3/', $e->getMessage(), '->find() throws an \InvalidArgumentException 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')); + } + + 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 testAsText() + { + $application = new Application(); + $application->add(new \FooCommand); + $this->ensureStaticCommandHelp($application); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_astext1.txt', $this->normalizeLineBreaks($application->asText()), '->asText() returns a text representation of the application'); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_astext2.txt', $this->normalizeLineBreaks($application->asText('foo')), '->asText() returns a text representation of the application'); + } + + public function testAsXml() + { + $application = new Application(); + $application->add(new \FooCommand); + $this->ensureStaticCommandHelp($application); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/application_asxml1.txt', $application->asXml(), '->asXml() returns an XML representation of the application'); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/application_asxml2.txt', $application->asXml('foo'), '->asXml() returns an XML representation of the application'); + } + + 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'); + + $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 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->assertSame('Symfony\Component\Console\Input\ArgvInput', get_class($command->input), '->run() creates an ArgvInput by default if none is given'); + $this->assertSame('Symfony\Component\Console\Output\ConsoleOutput', get_class($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'); + } + + 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')); + $this->assertTrue($helperSet->has('dialog')); + $this->assertTrue($helperSet->has('progress')); + } + + 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() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $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.', $tester->getDisplay()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage caught + */ + public function testRunWithExceptionAndDispatcher() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $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() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $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.after.caught.', $tester->getDisplay()); + } + + protected function getDispatcher() + { + $dispatcher = new EventDispatcher; + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) { + $event->getOutput()->write('before.'); + }); + $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) { + $event->getOutput()->write('after.'); + + $event->setExitCode(128); + }); + $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) { + $event->getOutput()->writeln('caught.'); + + $event->setException(new \LogicException('caught.', $event->getExitCode(), $event->getException())); + }); + + return $dispatcher; + } +} + +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())); + } +} 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..872bb7f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/CommandTest.php @@ -0,0 +1,338 @@ + + * + * 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 name cannot be empty. + */ + 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'); + } + + 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%'); + } + + 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('foo'); + $this->assertEquals('namespace:name [--foo] [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 testGet() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $formatterHelper = new FormatterHelper(); + $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->__get() 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 commmand 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 testExecuteMethodNeedsToBeOverriden() + { + $command = new Command('foo'); + $command->run(new StringInput(''), new NullOutput()); + } + + /** + * @expectedException \InvalidArgumentException + * @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 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 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()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid callable provided to Command::setCode. + */ + public function testSetCodeWithNonCallable() + { + $command = new \TestCommand(); + $command->setCode(array($this, 'nonExistentMethod')); + } + + public function callableMethodCommand(InputInterface $input, OutputInterface $output) + { + $output->writeln('from the code...'); + } + + public function testAsText() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $tester = new CommandTester($command); + $tester->execute(array('command' => $command->getName())); + $this->assertStringEqualsFile(self::$fixturesPath.'/command_astext.txt', $command->asText(), '->asText() returns a text representation of the command'); + } + + public function testAsXml() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $tester = new CommandTester($command); + $tester->execute(array('command' => $command->getName())); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/command_asxml.txt', $command->asXml(), '->asXml() returns an XML representation of 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..d60f3e4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/HelpCommandTest.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\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')); + $this->assertRegExp('/list \[--xml\] \[--raw\] \[--format="\.\.\."\] \[namespace\]/', $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()); + $this->assertRegExp('/list \[--xml\] \[--raw\] \[--format="\.\.\."\] \[namespace\]/', $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->assertRegExp('/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->assertRegExp('/list \[--xml\] \[--raw\] \[--format="\.\.\."\] \[namespace\]/', $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->assertRegExp('/list \[--xml\] \[--raw\] \[--format="\.\.\."\] \[namespace\]/', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertRegExp('/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..1df06f5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/ListCommandTest.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\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 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)); + } +} 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..296efed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/AbstractDescriptorTest.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\Console\Tests\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Descriptor\DescriptorInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getDescribeInputArgumentTestData */ + public function testDescribeInputArgument(InputArgument $argument, $expectedDescription) + { + $this->assertEquals(trim($expectedDescription), trim($this->getDescriptor()->describe($argument))); + } + + /** @dataProvider getDescribeInputOptionTestData */ + public function testDescribeInputOption(InputOption $option, $expectedDescription) + { + $this->assertEquals(trim($expectedDescription), trim($this->getDescriptor()->describe($option))); + } + + /** @dataProvider getDescribeInputDefinitionTestData */ + public function testDescribeInputDefinition(InputDefinition $definition, $expectedDescription) + { + $this->assertEquals(trim($expectedDescription), trim($this->getDescriptor()->describe($definition))); + } + + /** @dataProvider getDescribeCommandTestData */ + public function testDescribeCommand(Command $command, $expectedDescription) + { + $this->assertEquals(trim($expectedDescription), trim($this->getDescriptor()->describe($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->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $this->getDescriptor()->describe($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; + } +} 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..943ea29 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/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\Component\Console\Tests\Descriptor; + +use Symfony\Component\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/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..a367321 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/ObjectsProvider.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\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; +use Symfony\Component\Finder\Shell\Command; + +/** + * @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'), + ); + } + + 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()), + ); + } + + 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/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..bc04ca9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorCommand2.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\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') + ->addArgument('argument_name', InputArgument::REQUIRED) + ->addOption('option_name', 'o', InputOption::VALUE_NONE) + ; + } +} 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..7349bc3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo3Command.php @@ -0,0 +1,25 @@ +setName('foo3:bar') + ->setDescription('The foo3:bar command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + throw new \Exception("First exception"); + } catch (\Exception $e) { + throw new \Exception("Second exception", 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/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/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..84b587e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":"help [--xml] [--format=\"...\"] [--raw] [command_name]","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.","aliases":[],"definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output help as XML","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"To output help in other formats","default":null},"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 [--xml] [--raw] [--format=\"...\"] [namespace]","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>","aliases":[],"definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output list as XML","default":false},"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":"To output list in other formats","default":null}}}}],"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..ae815a8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.md @@ -0,0 +1,199 @@ +UNKNOWN +======= + +* help +* list + +help +---- + +* Description: Displays help for a command +* Usage: `help [--xml] [--format="..."] [--raw] [command_name]` +* Aliases: + +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: + +**xml:** + +* Name: `--xml` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output help as XML +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: To output help in other formats +* Default: `NULL` + +**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 [--xml] [--raw] [--format="..."] [namespace]` +* Aliases: + +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: + +**xml:** + +* Name: `--xml` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output list as XML +* Default: `false` + +**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: To output list in other formats +* Default: `NULL` 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..24ab4a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + [options] command [arguments] + +Options: + --help -h Display this help message. + --quiet -q Do not output any message. + --verbose -v|vv|vvv Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + --version -V Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction -n Do not ask any interactive question. + +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..d5c57be --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.xml @@ -0,0 +1,104 @@ + + + + + help [--xml] [--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 [--xml] [--raw] [--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..e0c4b9a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":"help [--xml] [--format=\"...\"] [--raw] [command_name]","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.","aliases":[],"definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output help as XML","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"To output help in other formats","default":null},"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 [--xml] [--raw] [--format=\"...\"] [namespace]","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>","aliases":[],"definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"xml":{"name":"--xml","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output list as XML","default":false},"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":"To output list in other formats","default":null}}}},{"name":"descriptor:command1","usage":"descriptor:command1","description":"command 1 description","help":"command 1 help","aliases":["alias1","alias2"],"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] argument_name","description":"command 2 description","help":"command 2 help","aliases":[],"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"]}]} 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..bb242b7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.md @@ -0,0 +1,388 @@ +My Symfony application +====================== + +* alias1 +* alias2 +* help +* list + +**descriptor:** + +* descriptor:command1 +* descriptor:command2 + +help +---- + +* Description: Displays help for a command +* Usage: `help [--xml] [--format="..."] [--raw] [command_name]` +* Aliases: + +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: + +**xml:** + +* Name: `--xml` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output help as XML +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: To output help in other formats +* Default: `NULL` + +**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 [--xml] [--raw] [--format="..."] [namespace]` +* Aliases: + +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: + +**xml:** + +* Name: `--xml` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output list as XML +* Default: `false` + +**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: To output list in other formats +* Default: `NULL` + +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: `descriptor:command1` +* Aliases: `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] argument_name` +* Aliases: + +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..78580d0 --- /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: + [options] command [arguments] + +Options: + --help -h Display this help message. + --quiet -q Do not output any message. + --verbose -v|vv|vvv Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + --version -V Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction -n Do not ask any interactive question. + +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..9aa77bb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.xml @@ -0,0 +1,181 @@ + + + + + help [--xml] [--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 [--xml] [--raw] [--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 + command 1 description + command 1 help + + alias1 + alias2 + + + + + + + + + + + + + + descriptor:command2 [-o|--option_name] 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..8e5a162 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_astext1.txt @@ -0,0 +1,20 @@ +Console Tool + +Usage: + [options] command [arguments] + +Options: + --help -h Display this help message. + --quiet -q Do not output any message. + --verbose -v|vv|vvv Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + --version -V Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction -n Do not ask any interactive question. + +Available commands: + afoobar The foo:bar command + help Displays help for a command + list Lists commands +foo + foo:bar The foo:bar command \ No newline at end of file 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..d27af91 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_astext2.txt @@ -0,0 +1,16 @@ +Console Tool + +Usage: + [options] command [arguments] + +Options: + --help -h Display this help message. + --quiet -q Do not output any message. + --verbose -v|vv|vvv Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + --version -V Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction -n Do not ask any interactive question. + +Available commands for the "foo" namespace: + foo:bar The foo:bar command \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_asxml1.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_asxml1.txt new file mode 100644 index 0000000..2eb3c30 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_asxml1.txt @@ -0,0 +1,140 @@ + + + + + help [--xml] [--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 [--xml] [--raw] [--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 + + + + + + + + + + + foo:bar + The foo:bar command + + + afoobar + + + + + + + + + + + + + + + + afoobar + help + list + + + foo:bar + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_asxml2.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_asxml2.txt new file mode 100644 index 0000000..5d61d2a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_asxml2.txt @@ -0,0 +1,37 @@ + + + + + foo:bar + The foo:bar command + + + afoobar + + + + + + + + + + + + + + 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..79c36f9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_gethelp.txt @@ -0,0 +1,13 @@ +Console Tool + +Usage: + [options] command [arguments] + +Options: + --help -h Display this help message. + --quiet -q Do not output any message. + --verbose -v|vv|vvv Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + --version -V Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction -n Do not ask any interactive question. \ 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..4629345 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception1.txt @@ -0,0 +1,8 @@ + + + + [InvalidArgumentException] + 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..c758129 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception2.txt @@ -0,0 +1,11 @@ + + + + [InvalidArgumentException] + The "--foo" option does not exist. + + + +list [--xml] [--raw] [--format="..."] [namespace] + + 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..c639924 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt @@ -0,0 +1,19 @@ + + + + [Exception] + Second exception + + + + + + + [Exception] + First exception + + + +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..19f893b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception4.txt @@ -0,0 +1,9 @@ + + + + [InvalidArgumentException] + Command "foo" is not define + d. + + + 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..5e9554f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + [options] command [arguments] + +Options: + --help -h Display this help message. + --quiet -q Do not output any message. + --verbose -v|vv|vvv Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + --version -V Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction -n Do not ask any interactive question. + +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..b9dab9a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run2.txt @@ -0,0 +1,30 @@ +Usage: + help [--xml] [--format="..."] [--raw] [command_name] + +Arguments: + command The command to execute + command_name The command name (default: "help") + +Options: + --xml To output help as XML + --format To output help in other formats + --raw To output raw command help + --help (-h) Display this help message. + --quiet (-q) Do not output any message. + --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + --version (-V) Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction (-n) Do not ask any interactive question. + +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..68bf516 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run3.txt @@ -0,0 +1,28 @@ +Usage: + list [--xml] [--raw] [--format="..."] [namespace] + +Arguments: + namespace The namespace name + +Options: + --xml To output list as XML + --raw To output raw command list + --format To output list in other formats + +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..0c1675d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.json @@ -0,0 +1 @@ +{"name":"descriptor:command1","usage":"descriptor:command1","description":"command 1 description","help":"command 1 help","aliases":["alias1","alias2"],"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..2cef9a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.md @@ -0,0 +1,8 @@ +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: `descriptor:command1` +* Aliases: `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..2375ac0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.txt @@ -0,0 +1,7 @@ +Usage: + descriptor:command1 + +Aliases: 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..dcfa6fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.xml @@ -0,0 +1,12 @@ + + + descriptor:command1 + command 1 description + command 1 help + + alias1 + alias2 + + + + 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..493b584 --- /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] argument_name","description":"command 2 description","help":"command 2 help","aliases":[],"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..5257c0d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.md @@ -0,0 +1,30 @@ +descriptor:command2 +------------------- + +* Description: command 2 description +* Usage: `descriptor:command2 [-o|--option_name] argument_name` +* Aliases: + +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..1da9f3d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.txt @@ -0,0 +1,11 @@ +Usage: + descriptor:command2 [-o|--option_name] argument_name + +Arguments: + argument_name + +Options: + --option_name (-o) + +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..c411c36 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.xml @@ -0,0 +1,18 @@ + + + descriptor:command2 [-o|--option_name] 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..8b6c74c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_astext.txt @@ -0,0 +1,18 @@ +Usage: + namespace:name + +Aliases: name +Arguments: + command The command to execute + +Options: + --help (-h) Display this help message. + --quiet (-q) Do not output any message. + --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + --version (-V) Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction (-n) Do not ask any interactive question. + +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..4b0012d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_asxml.txt @@ -0,0 +1,38 @@ + + + namespace:name + description + help + + name + + + + 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..a7d7e0d --- /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: + --foo (-f) The foo option + --baz The baz option (default: false) + --bar (-b) The bar option (default: "bar") + --qux The qux option (default: ["http://foo.com/","bar"]) (multiple values allowed) + --qux2 The qux2 option (default: {"foo":"bar"}) (multiple values allowed) 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..111e515 --- /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..9497b1c --- /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..c421fc9 --- /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_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..0db9f66 --- /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..c6fb2cc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.txt @@ -0,0 +1,2 @@ +Options: + --option_name (-o) 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..e17c61c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.txt @@ -0,0 +1,5 @@ +Arguments: + argument_name + +Options: + --option_name (-o) 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..daf83d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.txt @@ -0,0 +1 @@ + --option_name (-o) 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..627e3c1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.txt @@ -0,0 +1 @@ + --option_name (-o) 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..b88b12d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.txt @@ -0,0 +1 @@ + --option_name (-o) 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..5dba5e6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.txt @@ -0,0 +1 @@ + --option_name (-o) 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/Formatter/OutputFormatterStyleStackTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleStackTest.php new file mode 100644 index 0000000..e99ebc5 --- /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..6890a9b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleTest.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\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[0m", $style->apply('foo')); + + $style = new OutputFormatterStyle('red', null, array('blink')); + $this->assertEquals("\033[31;5mfoo\033[0m", $style->apply('foo')); + + $style = new OutputFormatterStyle(null, 'white'); + $this->assertEquals("\033[47mfoo\033[0m", $style->apply('foo')); + } + + public function testForeground() + { + $style = new OutputFormatterStyle(); + + $style->setForeground('black'); + $this->assertEquals("\033[30mfoo\033[0m", $style->apply('foo')); + + $style->setForeground('blue'); + $this->assertEquals("\033[34mfoo\033[0m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setForeground('undefined-color'); + } + + public function testBackground() + { + $style = new OutputFormatterStyle(); + + $style->setBackground('black'); + $this->assertEquals("\033[40mfoo\033[0m", $style->apply('foo')); + + $style->setBackground('yellow'); + $this->assertEquals("\033[43mfoo\033[0m", $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[0m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[7;8;1mfoo\033[0m", $style->apply('foo')); + + $style->unsetOption('reverse'); + $this->assertEquals("\033[8;1mfoo\033[0m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[8;1mfoo\033[0m", $style->apply('foo')); + + $style->setOptions(array('bold')); + $this->assertEquals("\033[1mfoo\033[0m", $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..497630e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php @@ -0,0 +1,231 @@ + + * + * 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 FormatterStyleTest 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[0m", + $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[0m", + $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[0m", + $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[0m", + $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[0m", + $formatter->format('some question') + ); + } + + public function testNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome \033[0m\033[32msome info\033[0m\033[37;41m error\033[0m", + $formatter->format('some some info error') + ); + } + + public function testStyleMatchingNotGreedy() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32m>=2.0,<2.3\033[0m)", + $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,)') + ); + } + + public function testDeepNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41merror\033[0m\033[32minfo\033[0m\033[33mcomment\033[0m\033[37;41merror\033[0m", + $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')); + + $this->assertEquals("\033[34;47msome custom msg\033[0m", $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[0m", $formatter->format('some custom msg')); + } + + public function testInlineStyle() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[34;41msome text\033[0m", $formatter->format('some text')); + $this->assertEquals("\033[34;41msome text\033[0m", $formatter->format('some text')); + } + + public function testNonStyleTag() + { + $formatter = new OutputFormatter(true); + $this->assertEquals("\033[32msome \033[0m\033[32m styled\033[0m", $formatter->format('some styled')); + } + + 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[0m", $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[0m", $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[0m", $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[0m", $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 + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/DialogHelperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/DialogHelperTest.php new file mode 100644 index 0000000..108f709 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/DialogHelperTest.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\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\DialogHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Output\StreamOutput; + +class DialogHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testSelect() + { + $dialog = new DialogHelper(); + + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $dialog->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); + $this->assertEquals('2', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '2')); + $this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes)); + $this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes)); + $this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', false)); + + rewind($output->getStream()); + $this->assertContains('Input "Fabien" is not a superhero!', stream_get_contents($output->getStream())); + + try { + $this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, 1)); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); + } + + $this->assertEquals(array('1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '2'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '2'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '0,1', false, 'Input "%s" is not a superhero!', true)); + $this->assertEquals(array('0', '1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, ' 0 , 1 ', false, 'Input "%s" is not a superhero!', true)); + } + + public function testAsk() + { + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("\n8AM\n")); + + $this->assertEquals('2PM', $dialog->ask($this->getOutputStream(), 'What time is it?', '2PM')); + $this->assertEquals('8AM', $dialog->ask($output = $this->getOutputStream(), 'What time is it?', '2PM')); + + 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 DialogHelper(); + $dialog->setInputStream($inputStream); + + $bundles = array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AsseticBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('FrameworkBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('SecurityBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('FooBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('FooBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + } + + public function testAskHiddenResponse() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('This test is not supported on Windows'); + } + + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("8AM\n")); + + $this->assertEquals('8AM', $dialog->askHiddenResponse($this->getOutputStream(), 'What time is it?')); + } + + public function testAskConfirmation() + { + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("\n\n")); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?')); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + + $dialog->setInputStream($this->getInputStream("y\nyes\n")); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + + $dialog->setInputStream($this->getInputStream("n\nno\n")); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true)); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true)); + } + + public function testAskAndValidate() + { + $dialog = new DialogHelper(); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question ='What color was the white horse of Henry IV?'; + $error = 'This is not a color!'; + $validator = function ($color) use ($error) { + if (!in_array($color, array('white', 'black'))) { + throw new \InvalidArgumentException($error); + } + + return $color; + }; + + $dialog->setInputStream($this->getInputStream("\nblack\n")); + $this->assertEquals('white', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white')); + $this->assertEquals('black', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white')); + + $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); + try { + $this->assertEquals('white', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white')); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals($error, $e->getMessage()); + } + } + + protected function getInputStream($input) + { + $stream = fopen('php://memory', 'r+', false); + fputs($stream, $input); + rewind($stream); + + return $stream; + } + + protected function getOutputStream() + { + return new StreamOutput(fopen('php://memory', 'r+', false)); + } + + private function hasSttyAvailable() + { + exec('stty 2>&1', $output, $exitcode); + + return $exitcode === 0; + } +} 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..87a45e1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.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\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() + { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('This test requires mbstring to work.'); + } + + $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 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..c983273 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/HelperSetTest.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\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Command\Command; + +class HelperSetTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers \Symfony\Component\Console\Helper\HelperSet::__construct + */ + 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'); + } + + /** + * @covers \Symfony\Component\Console\Helper\HelperSet::set + */ + 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'); + } + + /** + * @covers \Symfony\Component\Console\Helper\HelperSet::has + */ + 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'); + } + + /** + * @covers \Symfony\Component\Console\Helper\HelperSet::get + */ + 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->assertContains('The helper "foo" is not defined.', $e->getMessage(), '->get() throws \InvalidArgumentException when helper not found'); + } + } + + /** + * @covers \Symfony\Component\Console\Helper\HelperSet::setCommand + */ + 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'); + } + + /** + * @covers \Symfony\Component\Console\Helper\HelperSet::getCommand + */ + public function testGetCommand() + { + $cmd = new Command('foo'); + $helperset = new HelperSet(); + $helperset->setCommand($cmd); + $this->assertEquals($cmd, $helperset->getCommand(), '->getCommand() retrieves stored command'); + } + + /** + * 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/ProgressHelperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/ProgressHelperTest.php new file mode 100644 index 0000000..abb8d0b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/ProgressHelperTest.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\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\ProgressHelper; +use Symfony\Component\Console\Output\StreamOutput; + +class ProgressHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testAdvance() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 1 [->--------------------------]'), stream_get_contents($output->getStream())); + } + + public function testAdvanceWithStep() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(5); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream())); + } + + public function testAdvanceMultipleTimes() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(3); + $progress->advance(2); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 3 [--->------------------------]').$this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream())); + } + + public function testCustomizations() + { + $progress = new ProgressHelper(); + $progress->setBarWidth(10); + $progress->setBarCharacter('_'); + $progress->setEmptyBarCharacter(' '); + $progress->setProgressCharacter('/'); + $progress->setFormat(' %current%/%max% [%bar%] %percent%%'); + $progress->start($output = $this->getOutputStream(), 10); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 1/10 [_/ ] 10%'), stream_get_contents($output->getStream())); + } + + public function testPercent() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->display(); + $progress->advance(); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 0/50 [>---------------------------] 0%').$this->generateOutput(' 1/50 [>---------------------------] 2%').$this->generateOutput(' 2/50 [=>--------------------------] 4%'), stream_get_contents($output->getStream())); + } + + public function testOverwriteWithShorterLine() + { + $progress = new ProgressHelper(); + $progress->setFormat(' %current%/%max% [%bar%] %percent%%'); + $progress->start($output = $this->getOutputStream(), 50); + $progress->display(); + $progress->advance(); + + // set shorter format + $progress->setFormat(' %current%/%max% [%bar%]'); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%') . + $this->generateOutput(' 1/50 [>---------------------------] 2%') . + $this->generateOutput(' 2/50 [=>--------------------------] '), + stream_get_contents($output->getStream()) + ); + } + + public function testSetCurrentProgress() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->display(); + $progress->advance(); + $progress->setCurrent(15); + $progress->setCurrent(25); + + rewind($output->getStream()); + $this->assertEquals( + $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()) + ); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You must start the progress bar + */ + public function testSetCurrentBeforeStarting() + { + $progress = new ProgressHelper(); + $progress->setCurrent(15); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You can't regress the progress bar + */ + public function testRegressProgress() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->setCurrent(15); + $progress->setCurrent(10); + } + + public function testMultiByteSupport() + { + if (!function_exists('mb_strlen') || (false === $encoding = mb_detect_encoding('■'))) { + $this->markTestSkipped('The mbstring extension is needed for multi-byte support'); + } + + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->setBarCharacter('■'); + $progress->advance(3); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 3 [■■■>------------------------]'), stream_get_contents($output->getStream())); + } + + protected function getOutputStream() + { + return new StreamOutput(fopen('php://memory', 'r+', false)); + } + + protected $lastMessagesLength; + + protected function generateOutput($expected) + { + $expectedout = $expected; + + if ($this->lastMessagesLength !== null) { + $expectedout = str_pad($expected, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + + $this->lastMessagesLength = strlen($expectedout); + + return "\x0D".$expectedout; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/TableHelperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/TableHelperTest.php new file mode 100644 index 0000000..b2aa82b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/TableHelperTest.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\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\TableHelper; +use Symfony\Component\Console\Output\StreamOutput; + +class TableHelperTest 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, $layout, $expected) + { + $table = new TableHelper(); + $table + ->setHeaders($headers) + ->setRows($rows) + ->setLayout($layout) + ; + $table->render($output = $this->getOutputStream()); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRows($headers, $rows, $layout, $expected) + { + $table = new TableHelper(); + $table + ->setHeaders($headers) + ->addRows($rows) + ->setLayout($layout) + ; + $table->render($output = $this->getOutputStream()); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRowsOneByOne($headers, $rows, $layout, $expected) + { + $table = new TableHelper(); + $table + ->setHeaders($headers) + ->setLayout($layout) + ; + foreach ($rows as $row) { + $table->addRow($row); + } + $table->render($output = $this->getOutputStream()); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRenderProvider() + { + return array( + 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'), + 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'), + ), + TableHelper::LAYOUT_DEFAULT, +<<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..8e07c4c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php @@ -0,0 +1,300 @@ + + * + * 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(), + '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->assertEquals('', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); + + $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'); + } + + 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, $expected) + { + $input = new ArgvInput($argv); + $this->assertEquals($expected, $input->getParameterOption($key), '->getParameterOption() returns the expected value'); + } + + public function provideGetParameterOptionValues() + { + return array( + array(array('app/console', 'foo:bar', '-e', 'dev'), '-e', 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), '--env', 'dev'), + array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), 'dev'), + ); + } +} 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..73f2e33 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.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\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'); + } + + 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' + ) + ); + } + + /** + * @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..3bfc796 --- /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..5cf5011 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputDefinitionTest.php @@ -0,0 +1,420 @@ + + * + * 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' => null, + 'foo2' => null, + 'foo3' => 'default', + 'foo4' => null, + 'foo5' => 'default', + 'foo6' => array(), + 'foo7' => array(1, 2), + ); + $this->assertEquals($defaults, $definition->getOptionDefaults(), '->getOptionDefaults() returns the default values for all options'); + } + + public function testGetSynopsis() + { + $definition = new InputDefinition(array(new InputOption('foo'))); + $this->assertEquals('[--foo]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputOption('foo', 'f'))); + $this->assertEquals('[-f|--foo]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))); + $this->assertEquals('[-f|--foo="..."]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL))); + $this->assertEquals('[-f|--foo[="..."]]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + + $definition = new InputDefinition(array(new InputArgument('foo'))); + $this->assertEquals('[foo]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED))); + $this->assertEquals('foo', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputArgument('foo', InputArgument::IS_ARRAY))); + $this->assertEquals('[foo1] ... [fooN]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED | InputArgument::IS_ARRAY))); + $this->assertEquals('foo1 ... [fooN]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + } + + public function testAsText() + { + $definition = new InputDefinition(array( + new InputArgument('foo', InputArgument::OPTIONAL, 'The foo argument'), + new InputArgument('baz', InputArgument::OPTIONAL, 'The baz argument', true), + new InputArgument('bar', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The bar argument', array('http://foo.com/')), + new InputOption('foo', 'f', InputOption::VALUE_REQUIRED, 'The foo option'), + new InputOption('baz', null, InputOption::VALUE_OPTIONAL, 'The baz option', false), + new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL, 'The bar option', 'bar'), + new InputOption('qux', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The qux option', array('http://foo.com/', 'bar')), + new InputOption('qux2', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The qux2 option', array('foo' => 'bar')), + )); + $this->assertStringEqualsFile(self::$fixtures.'/definition_astext.txt', $definition->asText(), '->asText() returns a textual representation of the InputDefinition'); + } + + public function testAsXml() + { + $definition = new InputDefinition(array( + new InputArgument('foo', InputArgument::OPTIONAL, 'The foo argument'), + new InputArgument('baz', InputArgument::OPTIONAL, 'The baz argument', true), + new InputArgument('bar', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The bar argument', array('bar')), + new InputOption('foo', 'f', InputOption::VALUE_REQUIRED, 'The foo option'), + new InputOption('baz', null, InputOption::VALUE_OPTIONAL, 'The baz option', false), + new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL, 'The bar option', 'bar'), + )); + $this->assertXmlStringEqualsXmlFile(self::$fixtures.'/definition_asxml.txt', $definition->asXml(), '->asText() returns a textual representation of the InputDefinition'); + } + + 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..5817e8f --- /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..0b3e38f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputTest.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\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. + */ + public function testValidateWithMissingArguments() + { + $input = new ArrayInput(array()); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + $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..b284320 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/StringInputTest.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\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')); + + // definition in constructor + $input = new StringInput('--foo=bar', $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/Output/ConsoleOutputTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php new file mode 100644 index 0000000..7a3ede3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.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\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'); + } +} 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..be58b72 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/OutputTest.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\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'); + } + + 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[0m\n", $output->output, '->writeln() decorates the output'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Unknown output type given (24) + */ + public function testWriteWithInvalidOutputType() + { + $output = new TestOutput(); + $output->writeln('foo', 24); + } + + 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'); + } +} + +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/Tester/ApplicationTesterTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php new file mode 100644 index 0000000..6ce30ab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.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\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'); + } +} 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..da5bf84 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.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\Tests\Tester; + +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'); + } +} 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..472b4f2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/composer.json @@ -0,0 +1,37 @@ +{ + "name": "symfony/console", + "type": "library", + "description": "Symfony Console Component", + "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.3" + }, + "require-dev": { + "symfony/event-dispatcher": "~2.1" + }, + "suggest": { + "symfony/event-dispatcher": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Console\\": "" } + }, + "target-dir": "Symfony/Component/Console", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..8a7edd4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./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..be10abe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +2.1.0 +----- + + * none diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/CssSelector.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/CssSelector.php new file mode 100644 index 0000000..7a12a8b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/CssSelector.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\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; + +/** + * CssSelector is the main entry point of the component and can convert CSS + * selectors to XPath expressions. + * + * $xpath = CssSelector::toXpath('h1.foo'); + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Fabien Potencier + * + * @api + */ +class CssSelector +{ + private static $html = true; + + /** + * Translates a CSS expression to its XPath equivalent. + * Optionally, a prefix can be added to the resulting XPath + * expression with the $prefix parameter. + * + * @param mixed $cssExpr The CSS expression. + * @param string $prefix An optional prefix for the XPath expression. + * + * @return string + * + * @api + */ + public static function toXPath($cssExpr, $prefix = 'descendant-or-self::') + { + $translator = new Translator(); + + if (self::$html) { + $translator->registerExtension(new HtmlExtension($translator)); + } + + $translator + ->registerParserShortcut(new EmptyStringParser()) + ->registerParserShortcut(new ElementParser()) + ->registerParserShortcut(new ClassParser()) + ->registerParserShortcut(new HashParser()) + ; + + return $translator->cssToXPath($cssExpr, $prefix); + } + + /** + * Enables the HTML extension. + */ + public static function enableHtmlExtension() + { + self::$html = true; + } + + /** + * Disables the HTML extension. + */ + public static function disableHtmlExtension() + { + self::$html = false; + } +} 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..da01c2b --- /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 cssselector 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..151dbf0 --- /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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class ExpressionErrorException extends ParseException implements ExceptionInterface +{ +} 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..8a815fb --- /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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class InternalErrorException extends ParseException implements ExceptionInterface +{ +} 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..9c119f8 --- /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 cssselector 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..529b891 --- /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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class SyntaxErrorException extends ParseException implements ExceptionInterface +{ + /** + * @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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/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..f5324e1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/AbstractNode.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\CssSelector\Node; + +/** + * Abstract base node class. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..e2fa9a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/AttributeNode.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\CssSelector\Node; + +/** + * Represents a "[| ]" node. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..a7a59a3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/ClassNode.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\Node; + +/** + * Represents a "." node. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..4e085ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.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\CssSelector\Node; + +/** + * Represents a combined node. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..9ab13c3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/ElementNode.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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..ecd11a5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/FunctionNode.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\CssSelector\Node; + +use Symfony\Component\CssSelector\Parser\Token; + +/** + * Represents a ":()" node. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..7fb4075 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/HashNode.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\Node; + +/** + * Represents a "#" node. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..2529689 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/NegationNode.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\Node; + +/** + * Represents a ":not()" node. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..1601c33 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/NodeInterface.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\Node; + +/** + * Interface for nodes. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..4f2d538 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/PseudoNode.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\Node; + +/** + * Represents a ":" node. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..49f417f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/SelectorNode.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\Node; + +/** + * Represents a "(::|:)" node. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..96bbd11 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/Specificity.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\CssSelector\Node; + +/** + * Represents a node specificity. + * + * This component is a port of the Python cssselector 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 + */ +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; + } +} 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..97c3f8d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.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\TokenStream; + +/** + * CSS selector comment handler. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..4ac11a1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +interface HandlerInterface +{ + /** + * @param Reader $reader + * @param TokenStream $stream + * + * @return boolean + */ + 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..2227ea6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..346532e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..208f83c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..2663fe8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..806cfbb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..edf8ac0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Parser.php @@ -0,0 +1,394 @@ + + * + * 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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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 boolean $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->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..b27f79f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/ParserInterface.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\Parser; + +use Symfony\Component\CssSelector\Node\SelectorNode; + +/** + * CSS selector parser interface. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..8bd8ed6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Reader.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\Parser; + +/** + * CSS selector reader. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class Reader +{ + /** + * @var string + */ + private $source; + + /** + * @var int + */ + private $length; + + /** + * @var int + */ + private $position; + + /** + * @param string $source + */ + public function __construct($source) + { + $this->source = $source; + $this->length = strlen($source); + $this->position = 0; + } + + /** + * @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..db35233 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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=5) + // 0 => string 'test:input.ab6bd_field' (length=22) + // 1 => string 'test:' (length=5) + // 2 => string 'test' (length=4) + // 3 => string 'input' (length=5) + // 4 => 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[2] ?: null, $matches[3] ?: null), $matches[4])) + ); + } + + 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..eae18b9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.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 element parser shortcut. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class ElementParser implements ParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($source) + { + // Matches an optional namespace, required element or `*` + // $source = 'testns|testel'; + // $matches = array (size=4) + // 0 => string 'testns:testel' (length=13) + // 1 => string 'testns:' (length=7) + // 2 => string 'testns' (length=6) + // 3 => string 'testel' (length=6) + if (preg_match('/^(([a-z]+)\|)?([\w-]+|\*)$/i', trim($source), $matches)) { + return array(new SelectorNode(new ElementNode($matches[2] ?: null, $matches[3]))); + } + + 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..0031f7c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..95d7d5f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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=5) + // 0 => string 'test:input#ab6bd_field' (length=22) + // 1 => string 'test:' (length=5) + // 2 => string 'test' (length=4) + // 3 => string 'input' (length=5) + // 4 => 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[2] ?: null, $matches[3] ?: null), $matches[4])) + ); + } + + 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..0abe190 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Token.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\CssSelector\Parser; + +/** + * CSS selector token. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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 boolean + */ + public function isFileEnd() + { + return self::TYPE_FILE_END === $this->type; + } + + /** + * @param array $values + * + * @return boolean + */ + 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 boolean + */ + public function isWhitespace() + { + return self::TYPE_WHITESPACE === $this->type; + } + + /** + * @return boolean + */ + public function isIdentifier() + { + return self::TYPE_IDENTIFIER === $this->type; + } + + /** + * @return boolean + */ + public function isHash() + { + return self::TYPE_HASH === $this->type; + } + + /** + * @return boolean + */ + public function isNumber() + { + return self::TYPE_NUMBER === $this->type; + } + + /** + * @return boolean + */ + 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..40f1152 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/TokenStream.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class TokenStream +{ + /** + * @var Token[] + */ + private $tokens = array(); + + /** + * @var boolean + */ + private $frozen = false; + + /** + * @var Token[] + */ + private $used = array(); + + /** + * @var int + */ + private $cursor = 0; + + /** + * @var Token|null + */ + private $peeked = null; + + /** + * @var boolean + */ + 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 null; + } + + 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..c850276 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/Tokenizer.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..c90fc10 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.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\CssSelector\Parser\Tokenizer; + +/** + * CSS selector tokenizer escaping applier. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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 (array $match) { + $code = $match[1]; + + if (bin2hex($code) > 0xFFFD) { + $code = '\\FFFD'; + } + + return mb_convert_encoding(pack('H*', $code), 'UTF-8', 'UCS-2BE'); + }, $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..6fc98b7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.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\CssSelector\Parser\Tokenizer; + +/** + * CSS selector tokenizer patterns builder. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..c4409fe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/README.md @@ -0,0 +1,25 @@ +CssSelector Component +===================== + +CssSelector converts CSS selectors to XPath expressions. + +The component only goal is to convert CSS selectors to their XPath +equivalents: + + use Symfony\Component\CssSelector\CssSelector; + + print CssSelector::toXPath('div.item > h4 > a'); + +Resources +--------- + +This component is a port of the Python lxml library, which is copyright Infrae +and distributed under the BSD license. + +Current code is a port of https://github.com/SimonSapin/cssselect@v0.7.1 + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/CssSelector/ + $ composer.phar install --dev + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/CssSelectorTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/CssSelectorTest.php new file mode 100644 index 0000000..50daecc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/CssSelectorTest.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\CssSelector\Tests; + +use Symfony\Component\CssSelector\CssSelector; + +class CssSelectorTest extends \PHPUnit_Framework_TestCase +{ + public function testCssToXPath() + { + $this->assertEquals('descendant-or-self::*', CssSelector::toXPath('')); + $this->assertEquals('descendant-or-self::h1', CssSelector::toXPath('h1')); + $this->assertEquals("descendant-or-self::h1[@id = 'foo']", CssSelector::toXPath('h1#foo')); + $this->assertEquals("descendant-or-self::h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]", CssSelector::toXPath('h1.foo')); + $this->assertEquals('descendant-or-self::foo:h1', CssSelector::toXPath('foo|h1')); + } + + /** @dataProvider getCssToXPathWithoutPrefixTestData */ + public function testCssToXPathWithoutPrefix($css, $xpath) + { + $this->assertEquals($xpath, CssSelector::toXPath($css, ''), '->parse() parses an input string and returns a node'); + } + + public function testParseExceptions() + { + try { + CssSelector::toXPath('h1:'); + $this->fail('->parse() throws an Exception if the css selector is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Symfony\Component\CssSelector\Exception\ParseException', $e, '->parse() throws an Exception if the css selector is not valid'); + $this->assertEquals("Expected identifier, but found.", $e->getMessage(), '->parse() throws an Exception if the css selector is not valid'); + } + } + + 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..1db6a59 --- /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..1f200cf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/SpecificityTest.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\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), + ); + } +} 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..a0d80a1 --- /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\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..27e53cd --- /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\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..04f71b7 --- /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\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..84ee1d8 --- /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\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..e8782e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/NumberHandlerTest.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\Tests\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\NumberHandler; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; + +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, new TokenizerEscaping($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..32ce59a --- /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\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..0d91404 --- /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\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..d94bbbc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/ParserTest.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\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->assertEquals(1, count($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->assertEquals(1, count($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->assertEquals(1, count($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->assertEquals(1, count($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]]')), + ); + } + + 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..403c7af --- /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->assertEquals(1, count($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..2e436da --- /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->assertEquals(1, count($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..8477fa1 --- /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->assertEquals(1, count($selectors)); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals('Element[*]', (string) $selector->getTree()); + + $selectors = $parser->parse('this will produce an empty array'); + $this->assertEquals(0, count($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..41ecfc6 --- /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->assertEquals(1, count($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..7a90c3b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.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\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; + 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)); + } + } + } + + /** @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..1b147e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/AbstractExtension.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\CssSelector\XPath\Extension; + +/** + * XPath expression translator abstract extension. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..1b1f00f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..639e924 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.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\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator combination extension. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..65ab287 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.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 extension interface. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +interface ExtensionInterface +{ + /** + * Returns node translators. + * + * @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..8c8f6f1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php @@ -0,0 +1,209 @@ + + * + * 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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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 boolean $last + * @param boolean $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..aef8052 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/HtmlExtension.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..7d22d98 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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 Translator + */ + private $translator; + + /** + * @var int + */ + private $flags; + + /** + * Constructor. + * + * @param Translator $translator + * @param int $flags + */ + public function __construct(Translator $translator, $flags = 0) + { + $this->translator = $translator; + $this->flags = $flags; + } + + /** + * @param int $flag + * @param boolean $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 boolean + */ + 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 + * + * @return XPathExpr + */ + public function translateSelector(Node\SelectorNode $node) + { + return $this->translator->nodeToXPath($node->getTree()); + } + + /** + * @param Node\CombinedSelectorNode $node + * + * @return XPathExpr + */ + public function translateCombinedSelector(Node\CombinedSelectorNode $node) + { + return $this->translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector()); + } + + /** + * @param Node\NegationNode $node + * + * @return XPathExpr + */ + public function translateNegation(Node\NegationNode $node) + { + $xpath = $this->translator->nodeToXPath($node->getSelector()); + $subXpath = $this->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 + * + * @return XPathExpr + */ + public function translateFunction(Node\FunctionNode $node) + { + $xpath = $this->translator->nodeToXPath($node->getSelector()); + + return $this->translator->addFunction($xpath, $node); + } + + /** + * @param Node\PseudoNode $node + * + * @return XPathExpr + */ + public function translatePseudo(Node\PseudoNode $node) + { + $xpath = $this->translator->nodeToXPath($node->getSelector()); + + return $this->translator->addPseudoClass($xpath, $node->getIdentifier()); + } + + /** + * @param Node\AttributeNode $node + * + * @return XPathExpr + */ + public function translateAttribute(Node\AttributeNode $node) + { + $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 = $this->translator->nodeToXPath($node->getSelector()); + + if ($this->hasFlag(self::ATTRIBUTE_VALUE_IN_LOWER_CASE)) { + $value = strtolower($value); + } + + return $this->translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value); + } + + /** + * @param Node\ClassNode $node + * + * @return XPathExpr + */ + public function translateClass(Node\ClassNode $node) + { + $xpath = $this->translator->nodeToXPath($node->getSelector()); + + return $this->translator->addAttributeMatching($xpath, '~=', '@class', $node->getName()); + } + + /** + * @param Node\HashNode $node + * + * @return XPathExpr + */ + public function translateHash(Node\HashNode $node) + { + $xpath = $this->translator->nodeToXPath($node->getSelector()); + + return $this->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 boolean + */ + 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..d230dd7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.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\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 cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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..1ff3381 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Translator.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\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; +use Symfony\Component\CssSelector\XPath\Extension; + +/** + * XPath expression translator interface. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +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($this)) + ->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 $selector) { + if (null !== $selector->getPseudoElement()) { + throw new ExpressionErrorException('Pseudo-elements are not supported.'); + } + } + + $translator = $this; + + return implode(' | ', array_map(function (SelectorNode $selector) use ($translator, $prefix) { + return $translator->selectorToXPath($selector, $prefix); + }, $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); + } + + /** + * @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..b26cf5b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.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\CssSelector\XPath; + +use Symfony\Component\CssSelector\Node\SelectorNode; + +/** + * XPath expression translator interface. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +interface TranslatorInterface +{ + /** + * Translates a CSS selector to an XPath expression. + * + * @param string $cssExpr + * @param string $prefix + * + * @return XPathExpr + */ + public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::'); + + /** + * Translates a parsed selector node to an XPath expression + * + * @param SelectorNode $selector + * @param string $prefix + * + * @return XPathExpr + */ + 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..6f5162e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/XPathExpr.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\CssSelector\XPath; + +/** + * XPath expression translator interface. + * + * This component is a port of the Python cssselector library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class XPathExpr +{ + /** + * @var string + */ + private $path; + + /** + * @var string + */ + private $element; + + /** + * @var string + */ + private $condition; + + /** + * @param string $path + * @param string $element + * @param string $condition + * @param boolean $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..12d8c8e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/composer.json @@ -0,0 +1,35 @@ +{ + "name": "symfony/css-selector", + "type": "library", + "description": "Symfony CssSelector Component", + "keywords": [], + "homepage": "http://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": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\CssSelector\\": "" } + }, + "target-dir": "Symfony/Component/CssSelector", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..a19dc00 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + 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..2ad5ce6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +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..2e36805 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Debug.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\Debug; + +use Symfony\Component\ClassLoader\DebugClassLoader; + +/** + * 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 integer $errorReportingLevel The level of error reporting you want + * @param Boolean $displayErrors Whether to display errors (for development) or just log them (for production) + */ + public static function enable($errorReportingLevel = null, $displayErrors = true) + { + if (static::$enabled) { + return; + } + + static::$enabled = true; + + error_reporting(-1); + + ErrorHandler::register($errorReportingLevel, $displayErrors); + if ('cli' !== php_sapi_name()) { + ExceptionHandler::register(); + // CLI - display errors only if they're not already logged to STDERR + } elseif ($displayErrors && (!ini_get('log_errors') || ini_get('error_log'))) { + ini_set('display_errors', 1); + } + + if (class_exists('Symfony\Component\ClassLoader\DebugClassLoader')) { + DebugClassLoader::enable(); + } + } +} 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..07ee7c1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/ErrorHandler.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\Debug; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\Exception\ContextErrorException; +use Psr\Log\LoggerInterface; + +/** + * ErrorHandler. + * + * @author Fabien Potencier + * @author Konstantin Myakshin + */ +class ErrorHandler +{ + const TYPE_DEPRECATION = -100; + + private $levels = array( + E_WARNING => 'Warning', + E_NOTICE => 'Notice', + E_USER_ERROR => 'User Error', + E_USER_WARNING => 'User Warning', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Runtime Notice', + E_RECOVERABLE_ERROR => 'Catchable Fatal Error', + E_DEPRECATED => 'Deprecated', + E_USER_DEPRECATED => 'User Deprecated', + E_ERROR => 'Error', + E_CORE_ERROR => 'Core Error', + E_COMPILE_ERROR => 'Compile Error', + E_PARSE => 'Parse', + ); + + private $level; + + private $reservedMemory; + + private $displayErrors; + + /** + * @var LoggerInterface[] Loggers for channels + */ + private static $loggers = array(); + + /** + * Registers the error handler. + * + * @param integer $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable) + * @param Boolean $displayErrors Display errors (for dev environment) or just log they (production usage) + * + * @return The registered error handler + */ + public static function register($level = null, $displayErrors = true) + { + $handler = new static(); + $handler->setLevel($level); + $handler->setDisplayErrors($displayErrors); + + ini_set('display_errors', 0); + set_error_handler(array($handler, 'handle')); + register_shutdown_function(array($handler, 'handleFatal')); + $handler->reservedMemory = str_repeat('x', 10240); + + return $handler; + } + + public function setLevel($level) + { + $this->level = null === $level ? error_reporting() : $level; + } + + public function setDisplayErrors($displayErrors) + { + $this->displayErrors = $displayErrors; + } + + public static function setLogger(LoggerInterface $logger, $channel = 'deprecation') + { + self::$loggers[$channel] = $logger; + } + + /** + * @throws ContextErrorException When error_reporting returns error + */ + public function handle($level, $message, $file = 'unknown', $line = 0, $context = array()) + { + if (0 === $this->level) { + return false; + } + + if ($level & (E_USER_DEPRECATED | E_DEPRECATED)) { + if (isset(self::$loggers['deprecation'])) { + if (version_compare(PHP_VERSION, '5.4', '<')) { + $stack = array_map( + function ($row) { + unset($row['args']); + return $row; + }, + array_slice(debug_backtrace(false), 0, 10) + ); + } else { + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10); + } + + self::$loggers['deprecation']->warning($message, array('type' => self::TYPE_DEPRECATION, 'stack' => $stack)); + } + + return true; + } + + if ($this->displayErrors && error_reporting() & $level && $this->level & $level) { + throw new ContextErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line), 0, $level, $file, $line, $context); + } + + return false; + } + + public function handleFatal() + { + if (null === $error = error_get_last()) { + return; + } + + unset($this->reservedMemory); + $type = $error['type']; + if (0 === $this->level || !in_array($type, array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE))) { + return; + } + + if (isset(self::$loggers['emergency'])) { + $fatal = array( + 'type' => $type, + 'file' => $error['file'], + 'line' => $error['line'], + ); + + self::$loggers['emergency']->emerg($error['message'], $fatal); + } + + if (!$this->displayErrors) { + return; + } + + // get current exception handler + $exceptionHandler = set_exception_handler(function() {}); + restore_exception_handler(); + + if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandler) { + $level = isset($this->levels[$type]) ? $this->levels[$type] : $type; + $message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']); + $exception = new FatalErrorException($message, 0, $type, $error['file'], $error['line']); + $exceptionHandler[0]->handle($exception); + } + } +} 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..2e0115f --- /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 occured + */ + 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..bf37ef8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/FatalErrorException.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; + +/** + * Fatal Error Exception. + * + * @author Konstanton Myakshin + */ +class FatalErrorException extends \ErrorException +{ +} 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..4f0e815 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/FlattenException.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\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()); + if ($exception->getPrevious()) { + $e->setPrevious(static::create($exception->getPrevious())); + } + + 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) + { + $trace = $exception->getTrace(); + + if ($exception instanceof FatalErrorException) { + if (function_exists('xdebug_get_function_stack')) { + $trace = array_slice(array_reverse(xdebug_get_function_stack()), 4); + + foreach ($trace as $i => $frame) { + // XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695 + if (!isset($frame['type'])) { + $trace[$i]['type'] = '??'; + } + + if ('dynamic' === $trace[$i]['type']) { + $trace[$i]['type'] = '->'; + } elseif ('static' === $trace[$i]['type']) { + $trace[$i]['type'] = '::'; + } + + // XDebug also has a different name for the parameters array + if (isset($frame['params']) && !isset($frame['args'])) { + $trace[$i]['args'] = $frame['params']; + unset($trace[$i]['params']); + } + } + } else { + $trace = array_slice(array_reverse($trace), 1); + } + } + + $this->setTrace($trace, $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) + { + $result = array(); + foreach ($args as $key => $value) { + 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)); + } + } 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/ExceptionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/ExceptionHandler.php new file mode 100644 index 0000000..cd781b5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/ExceptionHandler.php @@ -0,0 +1,316 @@ + + * + * 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\HttpFoundation\Response; +use Symfony\Component\Debug\Exception\FlattenException; + +if (!defined('ENT_SUBSTITUTE')) { + define('ENT_SUBSTITUTE', 8); +} + +/** + * 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 + */ +class ExceptionHandler +{ + private $debug; + private $charset; + + public function __construct($debug = true, $charset = 'UTF-8') + { + $this->debug = $debug; + $this->charset = $charset; + } + + /** + * Registers the exception handler. + * + * @param Boolean $debug + * + * @return ExceptionHandler The registered exception handler + */ + public static function register($debug = true) + { + $handler = new static($debug); + + set_exception_handler(array($handler, 'handle')); + + return $handler; + } + + /** + * Sends a response for the given Exception. + * + * If you have the Symfony HttpFoundation component installed, + * this method will use it to create and send the response. If not, + * it will fallback to plain PHP functions. + * + * @param \Exception $exception An \Exception instance + * + * @see sendPhpResponse + * @see createResponse + */ + public function handle(\Exception $exception) + { + if (class_exists('Symfony\Component\HttpFoundation\Response')) { + $this->createResponse($exception)->send(); + } else { + $this->sendPhpResponse($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 instance + */ + public function sendPhpResponse($exception) + { + if (!$exception instanceof FlattenException) { + $exception = FlattenException::create($exception); + } + + header(sprintf('HTTP/1.0 %s', $exception->getStatusCode())); + foreach ($exception->getHeaders() as $name => $value) { + header($name.': '.$value, false); + } + + echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); + } + + /** + * Creates the error Response associated with the given Exception. + * + * @param \Exception|FlattenException $exception An \Exception instance + * + * @return Response A Response instance + */ + public function createResponse($exception) + { + if (!$exception instanceof FlattenException) { + $exception = FlattenException::create($exception); + } + + return new Response($this->decorate($this->getContent($exception), $this->getStylesheet($exception)), $exception->getStatusCode(), $exception->getHeaders()); + } + + /** + * 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->abbrClass($e['class']); + $message = nl2br($e['message']); + $content .= sprintf(<< +

    %d/%d %s: %s

    + +
    +
      + +EOF + , $ind, $total, $class, $message); + foreach ($e['trace'] as $trace) { + $content .= '
    1. '; + if ($trace['function']) { + $content .= sprintf('at %s%s%s(%s)', $this->abbrClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); + } + if (isset($trace['file']) && isset($trace['line'])) { + if ($linkFormat = ini_get('xdebug.file_link_format')) { + $link = str_replace(array('%f', '%l'), array($trace['file'], $trace['line']), $linkFormat); + $content .= sprintf(' in %s line %s', $link, $trace['file'], $trace['line']); + } else { + $content .= sprintf(' in %s line %s', $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($exception), $exception->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 abbrClass($class) + { + $parts = explode('\\', $class); + + return sprintf("%s", $class, array_pop($parts)); + } + + /** + * 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->abbrClass($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'", htmlspecialchars($item[1], ENT_QUOTES | ENT_SUBSTITUTE, $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 | ENT_SUBSTITUTE, $this->charset), true)); + } + + $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue); + } + + return implode(', ', $result); + } +} 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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Component/Debug/README.md b/vendor/symfony/symfony/src/Symfony/Component/Debug/README.md new file mode 100644 index 0000000..dd5bdca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/README.md @@ -0,0 +1,40 @@ +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: + + use Symfony\Component\Debug\Debug; + + Debug::enable(); + +You can also use the tools individually: + + use Symfony\Component\Debug\ErrorHandler; + use Symfony\Component\Debug\ExceptionHandler; + + error_reporting(-1); + + ErrorHandler::register($errorReportingLevel); + if ('cli' !== php_sapi_name()) { + ExceptionHandler::register(); + } elseif (!ini_get('log_errors') || ini_get('error_log')) { + ini_set('display_errors', 1); + } + +Note that the `Debug::enable()` call also registers the debug class loader +from the Symfony ClassLoader component when available. + +This component can optionally take advantage of the features of the HttpKernel +and HttpFoundation components. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Debug/ + $ composer.phar install --dev + $ phpunit 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..db06f61 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.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\Debug\Tests; + +use Symfony\Component\Debug\ErrorHandler; + +/** + * ErrorHandlerTest + * + * @author Robert Schönthal + */ +class ErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + + public function testConstruct() + { + $handler = ErrorHandler::register(3); + + $level = new \ReflectionProperty($handler, 'level'); + $level->setAccessible(true); + + $this->assertEquals(3, $level->getValue($handler)); + + restore_error_handler(); + } + + public function testHandle() + { + $handler = ErrorHandler::register(0); + $this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, 'foo')); + + restore_error_handler(); + + $handler = ErrorHandler::register(3); + $this->assertFalse($handler->handle(4, 'foo', 'foo.php', 12, 'foo')); + + restore_error_handler(); + + $handler = ErrorHandler::register(3); + try { + $handler->handle(111, 'foo', 'foo.php', 12, 'foo'); + } catch (\ErrorException $e) { + $this->assertSame('111: foo in foo.php line 12', $e->getMessage()); + $this->assertSame(111, $e->getSeverity()); + $this->assertSame('foo.php', $e->getFile()); + $this->assertSame(12, $e->getLine()); + } + + restore_error_handler(); + + $handler = ErrorHandler::register(E_USER_DEPRECATED); + $this->assertTrue($handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, 'foo')); + + restore_error_handler(); + + $handler = ErrorHandler::register(E_DEPRECATED); + $this->assertTrue($handler->handle(E_DEPRECATED, 'foo', 'foo.php', 12, 'foo')); + + restore_error_handler(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $that = $this; + $warnArgCheck = function($message, $context) use ($that) { + $that->assertEquals('foo', $message); + $that->assertArrayHasKey('type', $context); + $that->assertEquals($context['type'], ErrorHandler::TYPE_DEPRECATION); + $that->assertArrayHasKey('stack', $context); + $that->assertInternalType('array', $context['stack']); + }; + + $logger + ->expects($this->once()) + ->method('warning') + ->will($this->returnCallback($warnArgCheck)) + ; + + $handler = ErrorHandler::register(E_USER_DEPRECATED); + $handler->setLogger($logger); + $handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, 'foo'); + + restore_error_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..4a1d99e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.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\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->assertEquals(get_class($exception), $flattened->getClass(), '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()); + } + + /** + * @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)); + } + + 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..f187e2d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.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\Debug\Tests; + +use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; + +class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testDebug() + { + $handler = new ExceptionHandler(false); + $response = $handler->createResponse(new \RuntimeException('Foo')); + + $this->assertContains('

    Whoops, looks like something went wrong.

    ', $response->getContent()); + $this->assertNotContains('
    ', $response->getContent()); + + $handler = new ExceptionHandler(true); + $response = $handler->createResponse(new \RuntimeException('Foo')); + + $this->assertContains('

    Whoops, looks like something went wrong.

    ', $response->getContent()); + $this->assertContains('
    ', $response->getContent()); + } + + public function testStatusCode() + { + $handler = new ExceptionHandler(false); + + $response = $handler->createResponse(new \RuntimeException('Foo')); + $this->assertEquals('500', $response->getStatusCode()); + $this->assertContains('Whoops, looks like something went wrong.', $response->getContent()); + + $response = $handler->createResponse(new NotFoundHttpException('Foo')); + $this->assertEquals('404', $response->getStatusCode()); + $this->assertContains('Sorry, the page you are looking for could not be found.', $response->getContent()); + } + + public function testHeaders() + { + $handler = new ExceptionHandler(false); + + $response = $handler->createResponse(new MethodNotAllowedHttpException(array('POST'))); + $this->assertEquals('405', $response->getStatusCode()); + $this->assertEquals('POST', $response->headers->get('Allow')); + } + + public function testNestedExceptions() + { + $handler = new ExceptionHandler(true); + $response = $handler->createResponse(new \RuntimeException('Foo', null, new \RuntimeException('Bar'))); + } +} 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..35b170a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/composer.json @@ -0,0 +1,40 @@ +{ + "name": "symfony/debug", + "type": "library", + "description": "Symfony Debug Component", + "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.3" + }, + "require-dev": { + "symfony/http-kernel": "~2.1", + "symfony/http-foundation": "~2.1" + }, + "suggest": { + "symfony/http-foundation": "", + "symfony/http-kernel": "", + "symfony/class-loader": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Debug\\": "" } + }, + "target-dir": "Symfony/Component/Debug", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..8bab165 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./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..fd75578 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Alias.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\DependencyInjection; + +/** + * @api + */ +class Alias +{ + private $id; + private $public; + + /** + * Constructor. + * + * @param string $id Alias identifier + * @param Boolean $public If this alias is public + * + * @api + */ + public function __construct($id, $public = true) + { + $this->id = strtolower($id); + $this->public = $public; + } + + /** + * Checks if this DI Alias should be public or not. + * + * @return Boolean + * + * @api + */ + public function isPublic() + { + return $this->public; + } + + /** + * Sets if this Alias is public. + * + * @param Boolean $boolean If this Alias should be public + * + * @api + */ + public function setPublic($boolean) + { + $this->public = (Boolean) $boolean; + } + + /** + * Returns the Id of this alias. + * + * @return string The alias id + * + * @api + */ + 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..4839bd8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -0,0 +1,23 @@ +CHANGELOG +========= + +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..f7aa471 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.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\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 Boolean $onlyConstructorArguments Sets this Service Reference pass to ignore method calls + */ + public function __construct($onlyConstructorArguments = false) + { + $this->onlyConstructorArguments = (Boolean) $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 (!$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()); + } + } + } + + /** + * 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 null; + } + + return $id; + } +} 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..03528fe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.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\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 $currentId; + private $currentPath; + + /** + * Checks the ContainerBuilder object for circular references. + * + * @param ContainerBuilder $container The ContainerBuilder instances + */ + public function process(ContainerBuilder $container) + { + $graph = $container->getCompiler()->getServiceReferenceGraph(); + + foreach ($graph->getNodes() as $id => $node) { + $this->currentId = $id; + $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(); + $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()); + 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..e536331 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.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\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; +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 + * - synthetic services are always of non-prototype scope + * + * @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 + )); + } + + // synthetic service has non-prototype scope + if ($definition->isSynthetic() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope()) { + throw new RuntimeException(sprintf( + 'A synthetic service ("%s") cannot be of scope "prototype".', + $id + )); + } + + // non-synthetic, non-abstract service has class + if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) { + if ($definition->getFactoryClass() || $definition->getFactoryService()) { + 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 + )); + } + } + } +} 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..2cd5480 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.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\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..9351b11 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php @@ -0,0 +1,167 @@ + + * + * 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\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\ScopeCrossingInjectionException; +use Symfony\Component\DependencyInjection\Exception\ScopeWideningInjectionException; + +/** + * Checks the validity of references + * + * The following checks are performed by this pass: + * - target definitions are not abstract + * - target definitions are of equal or wider scope + * - target definitions are in the same scope hierarchy + * + * @author Johannes M. Schmitt + */ +class CheckReferenceValidityPass implements CompilerPassInterface +{ + private $container; + private $currentId; + private $currentDefinition; + private $currentScope; + private $currentScopeAncestors; + private $currentScopeChildren; + + /** + * Processes the ContainerBuilder to validate References. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + + $children = $this->container->getScopeChildren(); + $ancestors = array(); + + $scopes = $this->container->getScopes(); + foreach ($scopes as $name => $parent) { + $ancestors[$name] = array($parent); + + while (isset($scopes[$parent])) { + $ancestors[$name][] = $parent = $scopes[$parent]; + } + } + + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isSynthetic() || $definition->isAbstract()) { + continue; + } + + $this->currentId = $id; + $this->currentDefinition = $definition; + $this->currentScope = $scope = $definition->getScope(); + + if (ContainerInterface::SCOPE_CONTAINER === $scope) { + $this->currentScopeChildren = array_keys($scopes); + $this->currentScopeAncestors = array(); + } elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope) { + $this->currentScopeChildren = isset($children[$scope]) ? $children[$scope] : array(); + $this->currentScopeAncestors = isset($ancestors[$scope]) ? $ancestors[$scope] : array(); + } + + $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 + )); + } + + $this->validateScope($argument, $targetDefinition); + } + } + } + + /** + * Validates the scope of a single Reference. + * + * @param Reference $reference + * @param Definition $definition + * + * @throws ScopeWideningInjectionException when the definition references a service of a narrower scope + * @throws ScopeCrossingInjectionException when the definition references a service of another scope hierarchy + */ + private function validateScope(Reference $reference, Definition $definition = null) + { + if (ContainerInterface::SCOPE_PROTOTYPE === $this->currentScope) { + return; + } + + if (!$reference->isStrict()) { + return; + } + + if (null === $definition) { + return; + } + + if ($this->currentScope === $scope = $definition->getScope()) { + return; + } + + $id = (string) $reference; + + if (in_array($scope, $this->currentScopeChildren, true)) { + throw new ScopeWideningInjectionException($this->currentId, $this->currentScope, $id, $scope); + } + + if (!in_array($scope, $this->currentScopeAncestors, true)) { + throw new ScopeCrossingInjectionException($this->currentId, $this->currentScope, $id, $scope); + } + } + + /** + * Returns the Definition given an id. + * + * @param string $id Definition identifier + * + * @return Definition + */ + private function getDefinition($id) + { + if (!$this->container->hasDefinition($id)) { + return null; + } + + 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..01f224b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/Compiler.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\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; + +/** + * This class is used to remove circular dependencies between individual passes. + * + * @author Johannes M. Schmitt + * + * @api + */ +class Compiler +{ + private $passConfig; + private $log; + private $loggingFormatter; + private $serviceReferenceGraph; + + /** + * Constructor. + */ + public function __construct() + { + $this->passConfig = new PassConfig(); + $this->serviceReferenceGraph = new ServiceReferenceGraph(); + $this->loggingFormatter = new LoggingFormatter(); + $this->log = array(); + } + + /** + * Returns the PassConfig. + * + * @return PassConfig The PassConfig instance + * + * @api + */ + public function getPassConfig() + { + return $this->passConfig; + } + + /** + * Returns the ServiceReferenceGraph. + * + * @return ServiceReferenceGraph The ServiceReferenceGraph instance + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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..beacda9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CompilerPassInterface.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\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Interface that must be implemented by compilation passes + * + * @author Johannes M. Schmitt + * + * @api + */ +interface CompilerPassInterface +{ + /** + * You can modify the container here before it is dumped to PHP code. + * + * @param ContainerBuilder $container + * + * @api + */ + public function process(ContainerBuilder $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..ab8895a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.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\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(); + + foreach ($container->getDefinitions() as $id => $definition) { + $this->currentId = $id; + + $definition->setArguments( + $this->inlineArguments($container, $definition->getArguments()) + ); + + $definition->setMethodCalls( + $this->inlineArguments($container, $definition->getMethodCalls()) + ); + + $definition->setProperties( + $this->inlineArguments($container, $definition->getProperties()) + ); + } + } + + /** + * Processes inline arguments. + * + * @param ContainerBuilder $container The ContainerBuilder + * @param array $arguments An array of arguments + * + * @return array + */ + private function inlineArguments(ContainerBuilder $container, array $arguments) + { + foreach ($arguments as $k => $argument) { + 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 (ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope()) { + $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())); + } + } + + return $arguments; + } + + /** + * Checks if the definition is inlineable. + * + * @param ContainerBuilder $container + * @param string $id + * @param Definition $definition + * + * @return Boolean If the definition is inlineable + */ + private function isInlineableDefinition(ContainerBuilder $container, $id, Definition $definition) + { + if (ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope()) { + return true; + } + + if ($definition->isPublic()) { + return false; + } + + if (!$this->graph->hasNode($id)) { + return true; + } + + $ids = array(); + foreach ($this->graph->getNode($id)->getInEdges() as $edge) { + $ids[] = $edge->getSourceNode()->getId(); + } + + if (count(array_unique($ids)) > 1) { + return false; + } + + return $container->getDefinition(reset($ids))->getScope() === $definition->getScope(); + } +} 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..6bd6161 --- /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..7573084 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.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\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(); + + 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); + + $extension->load($config, $tmpContainer); + + $container->merge($tmpContainer); + } + + $container->addDefinitions($definitions); + $container->addAliases($aliases); + $container->getParameterBag()->add($parameters); + } +} 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..e863f75 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -0,0 +1,259 @@ + + * + * 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 + * + * @api + */ +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; + private $beforeOptimizationPasses; + private $beforeRemovingPasses; + private $optimizationPasses; + private $removingPasses; + + /** + * Constructor. + */ + public function __construct() + { + $this->mergePass = new MergeExtensionConfigurationPass(); + + $this->afterRemovingPasses = array(); + $this->beforeOptimizationPasses = array(); + $this->beforeRemovingPasses = array(); + + $this->optimizationPasses = array( + new ResolveDefinitionTemplatesPass(), + new ResolveParameterPlaceHoldersPass(), + new CheckDefinitionValidityPass(), + new ResolveReferencesToAliasesPass(), + new ResolveInvalidReferencesPass(), + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + public function getAfterRemovingPasses() + { + return $this->afterRemovingPasses; + } + + /** + * Gets all passes for the BeforeOptimization pass. + * + * @return array An array of passes + * + * @api + */ + public function getBeforeOptimizationPasses() + { + return $this->beforeOptimizationPasses; + } + + /** + * Gets all passes for the BeforeRemoving pass. + * + * @return array An array of passes + * + * @api + */ + public function getBeforeRemovingPasses() + { + return $this->beforeRemovingPasses; + } + + /** + * Gets all passes for the Optimization pass. + * + * @return array An array of passes + * + * @api + */ + public function getOptimizationPasses() + { + return $this->optimizationPasses; + } + + /** + * Gets all passes for the Removing pass. + * + * @return array An array of passes + * + * @api + */ + public function getRemovingPasses() + { + return $this->removingPasses; + } + + /** + * Gets all passes for the Merge pass. + * + * @return array An array of passes + * + * @api + */ + public function getMergePass() + { + return $this->mergePass; + } + + /** + * Sets the Merge Pass. + * + * @param CompilerPassInterface $pass The merge pass + * + * @api + */ + public function setMergePass(CompilerPassInterface $pass) + { + $this->mergePass = $pass; + } + + /** + * Sets the AfterRemoving passes. + * + * @param array $passes An array of passes + * + * @api + */ + public function setAfterRemovingPasses(array $passes) + { + $this->afterRemovingPasses = $passes; + } + + /** + * Sets the BeforeOptimization passes. + * + * @param array $passes An array of passes + * + * @api + */ + public function setBeforeOptimizationPasses(array $passes) + { + $this->beforeOptimizationPasses = $passes; + } + + /** + * Sets the BeforeRemoving passes. + * + * @param array $passes An array of passes + * + * @api + */ + public function setBeforeRemovingPasses(array $passes) + { + $this->beforeRemovingPasses = $passes; + } + + /** + * Sets the Optimization passes. + * + * @param array $passes An array of passes + * + * @api + */ + public function setOptimizationPasses(array $passes) + { + $this->optimizationPasses = $passes; + } + + /** + * Sets the Removing passes. + * + * @param array $passes An array of passes + * + * @api + */ + 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..d97d923 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemoveAbstractDefinitionsPass.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\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..4842337 --- /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..0c7be66 --- /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..6073abe --- /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 Boolean + */ + 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..972d708 --- /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 "%s".', $alias, $id), 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..4fb5b0d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.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\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 + */ +class ResolveDefinitionTemplatesPass implements CompilerPassInterface +{ + private $container; + private $compiler; + private $formatter; + + /** + * Process the ContainerBuilder to replace DefinitionDecorator instances with their real Definition instances. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + $this->compiler = $container->getCompiler(); + $this->formatter = $this->compiler->getLoggingFormatter(); + + foreach (array_keys($container->getDefinitions()) as $id) { + // yes, we are specifically fetching the definition from the + // container to ensure we are not operating on stale data + $definition = $container->getDefinition($id); + if (!$definition instanceof DefinitionDecorator || $definition->isAbstract()) { + continue; + } + + $this->resolveDefinition($id, $definition); + } + } + + /** + * Resolves the definition + * + * @param string $id The definition identifier + * @param DefinitionDecorator $definition + * + * @return Definition + * + * @throws \RuntimeException When the definition is invalid + */ + private function resolveDefinition($id, DefinitionDecorator $definition) + { + if (!$this->container->hasDefinition($parent = $definition->getParent())) { + throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $id)); + } + + $parentDef = $this->container->getDefinition($parent); + if ($parentDef instanceof DefinitionDecorator) { + $parentDef = $this->resolveDefinition($parent, $parentDef); + } + + $this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $id, $parent)); + $def = new Definition(); + + // merge in parent definition + // purposely ignored attributes: scope, abstract, tags + $def->setClass($parentDef->getClass()); + $def->setArguments($parentDef->getArguments()); + $def->setMethodCalls($parentDef->getMethodCalls()); + $def->setProperties($parentDef->getProperties()); + $def->setFactoryClass($parentDef->getFactoryClass()); + $def->setFactoryMethod($parentDef->getFactoryMethod()); + $def->setFactoryService($parentDef->getFactoryService()); + $def->setConfigurator($parentDef->getConfigurator()); + $def->setFile($parentDef->getFile()); + $def->setPublic($parentDef->isPublic()); + + // overwrite with values specified in the decorator + $changes = $definition->getChanges(); + if (isset($changes['class'])) { + $def->setClass($definition->getClass()); + } + if (isset($changes['factory_class'])) { + $def->setFactoryClass($definition->getFactoryClass()); + } + if (isset($changes['factory_method'])) { + $def->setFactoryMethod($definition->getFactoryMethod()); + } + if (isset($changes['factory_service'])) { + $def->setFactoryService($definition->getFactoryService()); + } + if (isset($changes['configurator'])) { + $def->setConfigurator($definition->getConfigurator()); + } + if (isset($changes['file'])) { + $def->setFile($definition->getFile()); + } + if (isset($changes['public'])) { + $def->setPublic($definition->isPublic()); + } + + // 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 = (integer) 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)); + } + + // these attributes are always taken from the child + $def->setAbstract($definition->isAbstract()); + $def->setScope($definition->getScope()); + $def->setTags($definition->getTags()); + + // set new definition on container + $this->container->setDefinition($id, $def); + + 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..93d5806 --- /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 $ignore) { + // 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 $ignore) { + // ignore property + } + } + $definition->setProperties($properties); + } + } + + /** + * Processes arguments to determine invalid references. + * + * @param array $arguments An array of Reference objects + * @param Boolean $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..6fc0a87 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.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\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())); + + $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..015bdeb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +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(), $argument->isStrict()); + } + } + } + + 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) + { + while ($this->container->hasAlias($id)) { + $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..fbd33ee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.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\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; + + /** + * Constructor. + */ + public function __construct() + { + $this->nodes = array(); + } + + /** + * Checks if the graph has a specific node. + * + * @param string $id Id to check + * + * @return Boolean + */ + 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..19da234 --- /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..3fd5077 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.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\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; + private $outEdges; + 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; + $this->inEdges = array(); + $this->outEdges = array(); + } + + /** + * 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 Boolean 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 Boolean 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..ef561e1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php @@ -0,0 +1,526 @@ + + * + * 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\InactiveScopeException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +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 + * + * @api + */ +class Container implements IntrospectableContainerInterface +{ + /** + * @var ParameterBagInterface + */ + protected $parameterBag; + + protected $services; + protected $methodMap; + protected $aliases; + protected $scopes; + protected $scopeChildren; + protected $scopedServices; + protected $scopeStacks; + protected $loading = array(); + + /** + * Constructor. + * + * @param ParameterBagInterface $parameterBag A ParameterBagInterface instance + * + * @api + */ + public function __construct(ParameterBagInterface $parameterBag = null) + { + $this->parameterBag = null === $parameterBag ? new ParameterBag() : $parameterBag; + + $this->services = array(); + $this->scopes = array(); + $this->scopeChildren = array(); + $this->scopedServices = array(); + $this->scopeStacks = array(); + + $this->set('service_container', $this); + } + + /** + * Compiles the container. + * + * This method does two things: + * + * * Parameter values are resolved; + * * The parameter bag is frozen. + * + * @api + */ + public function compile() + { + $this->parameterBag->resolve(); + + $this->parameterBag = new FrozenParameterBag($this->parameterBag->all()); + } + + /** + * Returns true if the container parameter bag are frozen. + * + * @return Boolean true if the container parameter bag are frozen, false otherwise + * + * @api + */ + public function isFrozen() + { + return $this->parameterBag instanceof FrozenParameterBag; + } + + /** + * Gets the service container parameter bag. + * + * @return ParameterBagInterface A ParameterBagInterface instance + * + * @api + */ + 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 + * + * @api + */ + public function getParameter($name) + { + return $this->parameterBag->get($name); + } + + /** + * Checks if a parameter exists. + * + * @param string $name The parameter name + * + * @return Boolean The presence of parameter in container + * + * @api + */ + public function hasParameter($name) + { + return $this->parameterBag->has($name); + } + + /** + * Sets a parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + * + * @api + */ + public function setParameter($name, $value) + { + $this->parameterBag->set($name, $value); + } + + /** + * Sets a service. + * + * @param string $id The service identifier + * @param object $service The service instance + * @param string $scope The scope of the service + * + * @throws RuntimeException When trying to set a service in an inactive scope + * @throws InvalidArgumentException When trying to set a service in the prototype scope + * + * @api + */ + public function set($id, $service, $scope = self::SCOPE_CONTAINER) + { + if (self::SCOPE_PROTOTYPE === $scope) { + throw new InvalidArgumentException(sprintf('You cannot set service "%s" of scope "prototype".', $id)); + } + + $id = strtolower($id); + + if (self::SCOPE_CONTAINER !== $scope) { + if (!isset($this->scopedServices[$scope])) { + throw new RuntimeException(sprintf('You cannot set service "%s" of inactive scope.', $id)); + } + + $this->scopedServices[$scope][$id] = $service; + } + + $this->services[$id] = $service; + + if (method_exists($this, $method = 'synchronize'.strtr($id, array('_' => '', '.' => '_')).'Service')) { + $this->$method(); + } + } + + /** + * Returns true if the given service is defined. + * + * @param string $id The service identifier + * + * @return Boolean true if the service is defined, false otherwise + * + * @api + */ + public function has($id) + { + $id = strtolower($id); + + return array_key_exists($id, $this->services) || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_')).'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 integer $invalidBehavior The behavior when the service does not exist + * + * @return object The associated service + * + * @throws InvalidArgumentException if the service is not defined + * @throws ServiceCircularReferenceException When a circular reference is detected + * @throws ServiceNotFoundException When the service is not defined + * + * @see Reference + * + * @api + */ + public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) + { + $id = strtolower($id); + + // resolve aliases + if (isset($this->aliases[$id])) { + $id = $this->aliases[$id]; + } + + // re-use shared service instance if it exists + if (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 (method_exists($this, $method = 'get'.strtr($id, array('_' => '', '.' => '_')).'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 (array_keys($this->services) as $key) { + $lev = levenshtein($id, $key); + if ($lev <= strlen($id) / 3 || false !== strpos($key, $id)) { + $alternatives[] = $key; + } + } + + throw new ServiceNotFoundException($id, null, null, $alternatives); + } + + return null; + } + + $this->loading[$id] = true; + + try { + $service = $this->$method(); + } catch (\Exception $e) { + unset($this->loading[$id]); + + if (array_key_exists($id, $this->services)) { + unset($this->services[$id]); + } + + if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { + return null; + } + + throw $e; + } + + unset($this->loading[$id]); + + return $service; + } + + /** + * Returns true if the given service has actually been initialized + * + * @param string $id The service identifier + * + * @return Boolean true if service has already been initialized, false otherwise + */ + public function initialized($id) + { + return array_key_exists(strtolower($id), $this->services); + } + + /** + * 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]); + } + } + + return array_unique(array_merge($ids, array_keys($this->services))); + } + + /** + * This is called when you enter a scope + * + * @param string $name + * + * @throws RuntimeException When the parent scope is inactive + * @throws InvalidArgumentException When the scope does not exist + * + * @api + */ + public function enterScope($name) + { + if (!isset($this->scopes[$name])) { + throw new InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name)); + } + + if (self::SCOPE_CONTAINER !== $this->scopes[$name] && !isset($this->scopedServices[$this->scopes[$name]])) { + throw new RuntimeException(sprintf('The parent scope "%s" must be active when entering this scope.', $this->scopes[$name])); + } + + // check if a scope of this name is already active, if so we need to + // remove all services of this scope, and those of any of its child + // scopes from the global services map + if (isset($this->scopedServices[$name])) { + $services = array($this->services, $name => $this->scopedServices[$name]); + unset($this->scopedServices[$name]); + + foreach ($this->scopeChildren[$name] as $child) { + if (isset($this->scopedServices[$child])) { + $services[$child] = $this->scopedServices[$child]; + unset($this->scopedServices[$child]); + } + } + + // update global map + $this->services = call_user_func_array('array_diff_key', $services); + array_shift($services); + + // add stack entry for this scope so we can restore the removed services later + if (!isset($this->scopeStacks[$name])) { + $this->scopeStacks[$name] = new \SplStack(); + } + $this->scopeStacks[$name]->push($services); + } + + $this->scopedServices[$name] = array(); + } + + /** + * This is called to leave the current scope, and move back to the parent + * scope. + * + * @param string $name The name of the scope to leave + * + * @throws InvalidArgumentException if the scope is not active + * + * @api + */ + public function leaveScope($name) + { + if (!isset($this->scopedServices[$name])) { + throw new InvalidArgumentException(sprintf('The scope "%s" is not active.', $name)); + } + + // remove all services of this scope, or any of its child scopes from + // the global service map + $services = array($this->services, $this->scopedServices[$name]); + unset($this->scopedServices[$name]); + foreach ($this->scopeChildren[$name] as $child) { + if (!isset($this->scopedServices[$child])) { + continue; + } + + $services[] = $this->scopedServices[$child]; + unset($this->scopedServices[$child]); + } + $this->services = call_user_func_array('array_diff_key', $services); + + // check if we need to restore services of a previous scope of this type + if (isset($this->scopeStacks[$name]) && count($this->scopeStacks[$name]) > 0) { + $services = $this->scopeStacks[$name]->pop(); + $this->scopedServices += $services; + + foreach ($services as $array) { + foreach ($array as $id => $service) { + $this->set($id, $service, $name); + } + } + } + } + + /** + * Adds a scope to the container. + * + * @param ScopeInterface $scope + * + * @throws InvalidArgumentException + * + * @api + */ + public function addScope(ScopeInterface $scope) + { + $name = $scope->getName(); + $parentScope = $scope->getParentName(); + + if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) { + throw new InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name)); + } + if (isset($this->scopes[$name])) { + throw new InvalidArgumentException(sprintf('A scope with name "%s" already exists.', $name)); + } + if (self::SCOPE_CONTAINER !== $parentScope && !isset($this->scopes[$parentScope])) { + throw new InvalidArgumentException(sprintf('The parent scope "%s" does not exist, or is invalid.', $parentScope)); + } + + $this->scopes[$name] = $parentScope; + $this->scopeChildren[$name] = array(); + + // normalize the child relations + while ($parentScope !== self::SCOPE_CONTAINER) { + $this->scopeChildren[$parentScope][] = $name; + $parentScope = $this->scopes[$parentScope]; + } + } + + /** + * Returns whether this container has a certain scope + * + * @param string $name The name of the scope + * + * @return Boolean + * + * @api + */ + public function hasScope($name) + { + return isset($this->scopes[$name]); + } + + /** + * Returns whether this scope is currently active + * + * This does not actually check if the passed scope actually exists. + * + * @param string $name + * + * @return Boolean + * + * @api + */ + public function isScopeActive($name) + { + return isset($this->scopedServices[$name]); + } + + /** + * Camelizes a string. + * + * @param string $id A string to camelize + * + * @return string The camelized string + */ + public static function camelize($id) + { + return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) { return ('.' === $match[1] ? '_' : '').strtoupper($match[2]); }, $id); + } + + /** + * 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, '_', '.'))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerAware.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerAware.php new file mode 100644 index 0000000..4096915 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerAware.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; + +/** + * A simple implementation of ContainerAwareInterface. + * + * @author Fabien Potencier + * + * @api + */ +abstract class ContainerAware implements ContainerAwareInterface +{ + /** + * @var ContainerInterface + * + * @api + */ + protected $container; + + /** + * Sets the Container associated with this Controller. + * + * @param ContainerInterface $container A ContainerInterface instance + * + * @api + */ + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } +} 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..e85bb53 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerAwareInterface.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; + +/** + * ContainerAwareInterface should be implemented by classes that depends on a Container. + * + * @author Fabien Potencier + * + * @api + */ +interface ContainerAwareInterface +{ + /** + * Sets the Container. + * + * @param ContainerInterface|null $container A ContainerInterface instance or null + * + * @api + */ + public function setContainer(ContainerInterface $container = null); +} 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..733d016 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -0,0 +1,1162 @@ + + * + * 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\InactiveScopeException; +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; + +/** + * ContainerBuilder is a DI container that provides an API to easily describe services. + * + * @author Fabien Potencier + * + * @api + */ +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; + + /** + * 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 Boolean $track true if you want to track resources, false otherwise + */ + public function setResourceTracking($track) + { + $this->trackResources = (Boolean) $track; + } + + /** + * Checks if resources are tracked. + * + * @return Boolean 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + public function getExtensions() + { + return $this->extensions; + } + + /** + * Checks if we have an extension. + * + * @param string $name The name of the extension + * + * @return Boolean If the extension exists + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION) + { + if (null === $this->compiler) { + $this->compiler = new Compiler(); + } + + $this->compiler->addPass($pass, $type); + + $this->addObjectResource($pass); + + return $this; + } + + /** + * Returns the compiler pass config which can then be modified. + * + * @return PassConfig The compiler pass config + * + * @api + */ + public function getCompilerPassConfig() + { + if (null === $this->compiler) { + $this->compiler = new Compiler(); + } + + return $this->compiler->getPassConfig(); + } + + /** + * Returns the compiler. + * + * @return Compiler The compiler + * + * @api + */ + public function getCompiler() + { + if (null === $this->compiler) { + $this->compiler = new Compiler(); + } + + return $this->compiler; + } + + /** + * Returns all Scopes. + * + * @return array An array of scopes + * + * @api + */ + public function getScopes() + { + return $this->scopes; + } + + /** + * Returns all Scope children. + * + * @return array An array of scope children. + * + * @api + */ + public function getScopeChildren() + { + return $this->scopeChildren; + } + + /** + * Sets a service. + * + * @param string $id The service identifier + * @param object $service The service instance + * @param string $scope The scope + * + * @throws BadMethodCallException When this ContainerBuilder is frozen + * + * @api + */ + public function set($id, $service, $scope = self::SCOPE_CONTAINER) + { + $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, $scope); + + if (isset($this->obsoleteDefinitions[$id]) && $this->obsoleteDefinitions[$id]->isSynchronized()) { + $this->synchronize($id); + } + } + + /** + * Removes a service definition. + * + * @param string $id The service identifier + * + * @api + */ + public function removeDefinition($id) + { + unset($this->definitions[strtolower($id)]); + } + + /** + * Returns true if the given service is defined. + * + * @param string $id The service identifier + * + * @return Boolean true if the service is defined, false otherwise + * + * @api + */ + 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 integer $invalidBehavior The behavior when the service does not exist + * + * @return object The associated service + * + * @throws InvalidArgumentException when no definitions are available + * @throws InactiveScopeException when the current scope is not active + * @throws LogicException when a circular dependency is detected + * @throws \Exception + * + * @see Reference + * + * @api + */ + public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + { + $id = strtolower($id); + + try { + return parent::get($id, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); + } catch (InactiveScopeException $e) { + if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { + return null; + } + + throw $e; + } catch (InvalidArgumentException $e) { + if (isset($this->loading[$id])) { + throw new LogicException(sprintf('The service "%s" has a circular reference to itself.', $id), 0, $e); + } + + if (!$this->hasDefinition($id) && 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 null; + } + + throw $e; + } + + $this->loading[$id] = true; + + try { + $service = $this->createService($definition, $id); + } catch (\Exception $e) { + unset($this->loading[$id]); + + if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { + return null; + } + + throw $e; + } + + 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 + * + * @api + */ + 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 + * + * @api + */ + 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. + * + * @api + */ + public function compile() + { + if (null === $this->compiler) { + $this->compiler = new Compiler(); + } + + if ($this->trackResources) { + foreach ($this->compiler->getPassConfig()->getPasses() as $pass) { + $this->addObjectResource($pass); + } + + foreach ($this->definitions as $definition) { + if ($definition->isLazy() && ($class = $definition->getClass()) && class_exists($class)) { + $this->addClassResource(new \ReflectionClass($class)); + } + } + } + + $this->compiler->compile($this); + + $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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 === strtolower($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 + * + * @api + */ + 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 Boolean true if the alias exists, false otherwise + * + * @api + */ + public function hasAlias($id) + { + return isset($this->aliasDefinitions[strtolower($id)]); + } + + /** + * Gets all defined aliases. + * + * @return Alias[] An array of aliases + * + * @api + */ + 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 + * + * @api + */ + public function getAlias($id) + { + $id = strtolower($id); + + if (!$this->hasAlias($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 + * + * @api + */ + public function register($id, $class = null) + { + return $this->setDefinition(strtolower($id), new Definition($class)); + } + + /** + * Adds the service definitions. + * + * @param Definition[] $definitions An array of service definitions + * + * @api + */ + 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 + * + * @api + */ + public function setDefinitions(array $definitions) + { + $this->definitions = array(); + $this->addDefinitions($definitions); + } + + /** + * Gets all service definitions. + * + * @return Definition[] An array of Definition instances + * + * @api + */ + 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 + * + * @api + */ + 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 Boolean true if the service definition exists, false otherwise + * + * @api + */ + 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 + * + * @api + */ + public function getDefinition($id) + { + $id = strtolower($id); + + if (!$this->hasDefinition($id)) { + 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 + * + * @api + */ + public function findDefinition($id) + { + while ($this->hasAlias($id)) { + $id = (string) $this->getAlias($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 Boolean $tryProxy Whether to try proxying the service with a lazy proxy + * + * @return object The service described by the service definition + * + * @throws RuntimeException When the scope is inactive + * @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 + * + * @internal this method is public because of PHP 5.3 limitations, do not use it explicitly in your code + */ + public 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 ($tryProxy && $definition->isLazy()) { + $container = $this; + + $proxy = $this + ->getProxyInstantiator() + ->instantiateProxy( + $container, + $definition, + $id, function () use ($definition, $id, $container) { + return $container->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 !== $definition->getFactoryMethod()) { + if (null !== $definition->getFactoryClass()) { + $factory = $parameterBag->resolveValue($definition->getFactoryClass()); + } elseif (null !== $definition->getFactoryService()) { + $factory = $this->get($parameterBag->resolveValue($definition->getFactoryService())); + } else { + throw new RuntimeException(sprintf('Cannot create service "%s" from factory method without a factory service or factory class.', $id)); + } + + $service = call_user_func_array(array($factory, $definition->getFactoryMethod()), $arguments); + } else { + $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass())); + + $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); + } + + 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->resolveValue($definition->getProperties())); + foreach ($properties as $name => $value) { + $service->$name = $value; + } + + if ($callable = $definition->getConfigurator()) { + if (is_array($callable)) { + $callable[0] = $callable[0] instanceof Reference ? $this->get((string) $callable[0]) : $parameterBag->resolveValue($callable[0]); + } + + 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. + * + * @param mixed $value A value + * + * @return mixed The same value with all service references replaced by the real service instances + */ + public function resolveServices($value) + { + if (is_array($value)) { + foreach ($value as &$v) { + $v = $this->resolveServices($v); + } + } elseif ($value instanceof Reference) { + $value = $this->get((string) $value, $value->getInvalidBehavior()); + } elseif ($value instanceof Definition) { + $value = $this->createService($value, null); + } + + 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. + * + * @api + */ + public function findTaggedServiceIds($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 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; + } + + /** + * Synchronizes a service change. + * + * This method updates all services that depend on the given + * service by calling all methods referencing it. + * + * @param string $id A service id + */ + private function synchronize($id) + { + foreach ($this->definitions as $definitionId => $definition) { + // only check initialized services + if (!$this->initialized($definitionId)) { + continue; + } + + foreach ($definition->getMethodCalls() as $call) { + foreach ($call[1] as $argument) { + if ($argument instanceof Reference && $id == (string) $argument) { + $this->callMethod($this->get($definitionId), $call); + } + } + } + } + } + + 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()->resolveValue($call[1]))); + } + + /** + * Shares a given service in the container + * + * @param Definition $definition + * @param mixed $service + * @param string $id + * + * @throws InactiveScopeException + */ + private function shareService(Definition $definition, $service, $id) + { + if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { + if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) { + throw new InactiveScopeException($id, $scope); + } + + $this->services[$lowerId = strtolower($id)] = $service; + + if (self::SCOPE_CONTAINER !== $scope) { + $this->scopedServices[$scope][$lowerId] = $service; + } + } + } +} 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..1951ae7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerInterface.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\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 + * + * @api + */ +interface ContainerInterface +{ + const EXCEPTION_ON_INVALID_REFERENCE = 1; + const NULL_ON_INVALID_REFERENCE = 2; + const IGNORE_ON_INVALID_REFERENCE = 3; + const SCOPE_CONTAINER = 'container'; + const SCOPE_PROTOTYPE = 'prototype'; + + /** + * Sets a service. + * + * @param string $id The service identifier + * @param object $service The service instance + * @param string $scope The scope of the service + * + * @api + */ + public function set($id, $service, $scope = self::SCOPE_CONTAINER); + + /** + * 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 if the service is not defined + * @throws ServiceCircularReferenceException When a circular reference is detected + * @throws ServiceNotFoundException When the service is not defined + * + * @see Reference + * + * @api + */ + 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 Boolean true if the service is defined, false otherwise + * + * @api + */ + public function has($id); + + /** + * Gets a parameter. + * + * @param string $name The parameter name + * + * @return mixed The parameter value + * + * @throws InvalidArgumentException if the parameter is not defined + * + * @api + */ + public function getParameter($name); + + /** + * Checks if a parameter exists. + * + * @param string $name The parameter name + * + * @return Boolean The presence of parameter in container + * + * @api + */ + public function hasParameter($name); + + /** + * Sets a parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + * + * @api + */ + public function setParameter($name, $value); + + /** + * Enters the given scope + * + * @param string $name + * + * @api + */ + public function enterScope($name); + + /** + * Leaves the current scope, and re-enters the parent scope + * + * @param string $name + * + * @api + */ + public function leaveScope($name); + + /** + * Adds a scope to the container + * + * @param ScopeInterface $scope + * + * @api + */ + public function addScope(ScopeInterface $scope); + + /** + * Whether this container has the given scope + * + * @param string $name + * + * @return Boolean + * + * @api + */ + public function hasScope($name); + + /** + * Determines whether the given scope is currently active. + * + * It does however not check if the scope actually exists. + * + * @param string $name + * + * @return Boolean + * + * @api + */ + public function isScopeActive($name); +} 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..1168444 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Definition.php @@ -0,0 +1,715 @@ + + * + * 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 + * + * @api + */ +class Definition +{ + private $class; + private $file; + private $factoryClass; + private $factoryMethod; + private $factoryService; + private $scope; + private $properties; + private $calls; + private $configurator; + private $tags; + private $public; + private $synthetic; + private $abstract; + private $synchronized; + private $lazy; + + protected $arguments; + + /** + * Constructor. + * + * @param string $class The service class + * @param array $arguments An array of arguments to pass to the service constructor + * + * @api + */ + public function __construct($class = null, array $arguments = array()) + { + $this->class = $class; + $this->arguments = $arguments; + $this->calls = array(); + $this->scope = ContainerInterface::SCOPE_CONTAINER; + $this->tags = array(); + $this->public = true; + $this->synthetic = false; + $this->synchronized = false; + $this->lazy = false; + $this->abstract = false; + $this->properties = array(); + } + + /** + * Sets the name of the class that acts as a factory using the factory method, + * which will be invoked statically. + * + * @param string $factoryClass The factory class name + * + * @return Definition The current instance + * + * @api + */ + public function setFactoryClass($factoryClass) + { + $this->factoryClass = $factoryClass; + + return $this; + } + + /** + * Gets the factory class. + * + * @return string The factory class name + * + * @api + */ + public function getFactoryClass() + { + return $this->factoryClass; + } + + /** + * Sets the factory method able to create an instance of this class. + * + * @param string $factoryMethod The factory method name + * + * @return Definition The current instance + * + * @api + */ + public function setFactoryMethod($factoryMethod) + { + $this->factoryMethod = $factoryMethod; + + return $this; + } + + /** + * Gets the factory method. + * + * @return string The factory method name + * + * @api + */ + public function getFactoryMethod() + { + return $this->factoryMethod; + } + + /** + * Sets the name of the service that acts as a factory using the factory method. + * + * @param string $factoryService The factory service id + * + * @return Definition The current instance + * + * @api + */ + public function setFactoryService($factoryService) + { + $this->factoryService = $factoryService; + + return $this; + } + + /** + * Gets the factory service id. + * + * @return string The factory service id + * + * @api + */ + public function getFactoryService() + { + return $this->factoryService; + } + + /** + * Sets the service class. + * + * @param string $class The service class + * + * @return Definition The current instance + * + * @api + */ + public function setClass($class) + { + $this->class = $class; + + return $this; + } + + /** + * Gets the service class. + * + * @return string The service class + * + * @api + */ + 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 + * + * @api + */ + public function setArguments(array $arguments) + { + $this->arguments = $arguments; + + return $this; + } + + /** + * @api + */ + public function setProperties(array $properties) + { + $this->properties = $properties; + + return $this; + } + + /** + * @api + */ + public function getProperties() + { + return $this->properties; + } + + /** + * @api + */ + 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 + * + * @api + */ + public function addArgument($argument) + { + $this->arguments[] = $argument; + + return $this; + } + + /** + * Sets a specific argument + * + * @param integer $index + * @param mixed $argument + * + * @return Definition The current instance + * + * @throws OutOfBoundsException When the replaced argument does not exist + * + * @api + */ + 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 + * + * @api + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Gets an argument to pass to the service constructor/factory method. + * + * @param integer $index + * + * @return mixed The argument value + * + * @throws OutOfBoundsException When the argument does not exist + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 Boolean + * + * @api + */ + 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 + * + * @api + */ + public function getMethodCalls() + { + return $this->calls; + } + + /** + * Sets tags for this definition + * + * @param array $tags + * + * @return Definition the current instance + * + * @api + */ + public function setTags(array $tags) + { + $this->tags = $tags; + + return $this; + } + + /** + * Returns all tags. + * + * @return array An array of tags + * + * @api + */ + public function getTags() + { + return $this->tags; + } + + /** + * Gets a tag by name. + * + * @param string $name The tag name + * + * @return array An array of attributes + * + * @api + */ + 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 + * + * @api + */ + 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 Boolean + * + * @api + */ + 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) + { + if (isset($this->tags[$name])) { + unset($this->tags[$name]); + } + + return $this; + } + + /** + * Clears the tags for this definition. + * + * @return Definition The current instance + * + * @api + */ + 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 + * + * @api + */ + public function setFile($file) + { + $this->file = $file; + + return $this; + } + + /** + * Gets the file to require before creating the service. + * + * @return string The full pathname to include + * + * @api + */ + public function getFile() + { + return $this->file; + } + + /** + * Sets the scope of the service + * + * @param string $scope Whether the service must be shared or not + * + * @return Definition The current instance + * + * @api + */ + public function setScope($scope) + { + $this->scope = $scope; + + return $this; + } + + /** + * Returns the scope of the service + * + * @return string + * + * @api + */ + public function getScope() + { + return $this->scope; + } + + /** + * Sets the visibility of this service. + * + * @param Boolean $boolean + * + * @return Definition The current instance + * + * @api + */ + public function setPublic($boolean) + { + $this->public = (Boolean) $boolean; + + return $this; + } + + /** + * Whether this service is public facing + * + * @return Boolean + * + * @api + */ + public function isPublic() + { + return $this->public; + } + + /** + * Sets the synchronized flag of this service. + * + * @param Boolean $boolean + * + * @return Definition The current instance + * + * @api + */ + public function setSynchronized($boolean) + { + $this->synchronized = (Boolean) $boolean; + + return $this; + } + + /** + * Whether this service is synchronized. + * + * @return Boolean + * + * @api + */ + public function isSynchronized() + { + return $this->synchronized; + } + + /** + * Sets the lazy flag of this service. + * + * @param Boolean $lazy + * + * @return Definition The current instance + */ + public function setLazy($lazy) + { + $this->lazy = (Boolean) $lazy; + + return $this; + } + + /** + * Whether this service is lazy. + * + * @return Boolean + */ + public function isLazy() + { + return $this->lazy; + } + + /** + * Sets whether this definition is synthetic, that is not constructed by the + * container, but dynamically injected. + * + * @param Boolean $boolean + * + * @return Definition the current instance + * + * @api + */ + public function setSynthetic($boolean) + { + $this->synthetic = (Boolean) $boolean; + + return $this; + } + + /** + * Whether this definition is synthetic, that is not constructed by the + * container, but dynamically injected. + * + * @return Boolean + * + * @api + */ + public function isSynthetic() + { + return $this->synthetic; + } + + /** + * Whether this definition is abstract, that means it merely serves as a + * template for other definitions. + * + * @param Boolean $boolean + * + * @return Definition the current instance + * + * @api + */ + public function setAbstract($boolean) + { + $this->abstract = (Boolean) $boolean; + + return $this; + } + + /** + * Whether this definition is abstract, that means it merely serves as a + * template for other definitions. + * + * @return Boolean + * + * @api + */ + public function isAbstract() + { + return $this->abstract; + } + + /** + * Sets a configurator to call after the service is fully initialized. + * + * @param callable $callable A PHP callable + * + * @return Definition The current instance + * + * @api + */ + public function setConfigurator($callable) + { + $this->configurator = $callable; + + return $this; + } + + /** + * Gets the configurator to call after the service is fully initialized. + * + * @return callable The PHP callable to call + * + * @api + */ + public function getConfigurator() + { + return $this->configurator; + } +} 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..ad3c3c1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/DefinitionDecorator.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\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; + +/** + * This definition decorates another definition. + * + * @author Johannes M. Schmitt + * + * @api + */ +class DefinitionDecorator extends Definition +{ + private $parent; + private $changes; + + /** + * Constructor. + * + * @param string $parent The id of Definition instance to decorate. + * + * @api + */ + public function __construct($parent) + { + parent::__construct(); + + $this->parent = $parent; + $this->changes = array(); + } + + /** + * Returns the Definition being decorated. + * + * @return string + * + * @api + */ + public function getParent() + { + return $this->parent; + } + + /** + * Returns all changes tracked for the Definition object. + * + * @return array An array of changes for this Definition + * + * @api + */ + public function getChanges() + { + return $this->changes; + } + + /** + * {@inheritDoc} + * + * @api + */ + public function setClass($class) + { + $this->changes['class'] = true; + + return parent::setClass($class); + } + + /** + * {@inheritDoc} + * + * @api + */ + public function setFactoryClass($class) + { + $this->changes['factory_class'] = true; + + return parent::setFactoryClass($class); + } + + /** + * {@inheritDoc} + * + * @api + */ + public function setFactoryMethod($method) + { + $this->changes['factory_method'] = true; + + return parent::setFactoryMethod($method); + } + + /** + * {@inheritDoc} + * + * @api + */ + public function setFactoryService($service) + { + $this->changes['factory_service'] = true; + + return parent::setFactoryService($service); + } + + /** + * {@inheritDoc} + * + * @api + */ + public function setConfigurator($callable) + { + $this->changes['configurator'] = true; + + return parent::setConfigurator($callable); + } + + /** + * {@inheritDoc} + * + * @api + */ + public function setFile($file) + { + $this->changes['file'] = true; + + return parent::setFile($file); + } + + /** + * {@inheritDoc} + * + * @api + */ + public function setPublic($boolean) + { + $this->changes['public'] = true; + + return parent::setPublic($boolean); + } + + /** + * 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 integer $index + * + * @return mixed The argument value + * + * @throws OutOfBoundsException When the argument does not exist + * + * @api + */ + 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 integer $index + * @param mixed $value + * + * @return DefinitionDecorator the current instance + * @throws InvalidArgumentException when $index isn't an integer + * + * @api + */ + 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..9892401 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/Dumper.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\Dumper; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Dumper is the abstract class for all built-in dumpers. + * + * @author Fabien Potencier + * + * @api + */ +abstract class Dumper implements DumperInterface +{ + protected $container; + + /** + * Constructor. + * + * @param ContainerBuilder $container The service container to dump + * + * @api + */ + 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..ba146f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/DumperInterface.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\Dumper; + +/** + * DumperInterface is the interface implemented by service container dumper classes. + * + * @author Fabien Potencier + * + * @api + */ +interface DumperInterface +{ + /** + * Dumps the service container. + * + * @param array $options An array of options + * + * @return string The representation of the service container + * + * @api + */ + 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..4499e52 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.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\Component\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\Definition; +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 Boolean $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) { + $nodes[$id] = array('class' => str_replace('\\', '\\\\', $this->container->getParameterBag()->resolveValue($definition->getClass())), 'attributes' => array_merge($this->options['node.definition'], array('style' => ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope() ? 'filled' : 'dotted'))); + + $container->setDefinition($id, new Definition('stdClass')); + } + + foreach ($container->getServiceIds() as $id) { + $service = $container->get($id); + + if (in_array($id, array_keys($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->getScopes() as $scope) { + $container->addScope($scope); + } + 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..0c62274 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -0,0 +1,1296 @@ + + * + * 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; + +/** + * PhpDumper dumps a service container as a PHP class. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + * + * @api + */ +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'); + + /** + * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface + */ + private $proxyDumper; + + /** + * {@inheritDoc} + * + * @api + */ + 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 + * + * @param array $options An array of options + * + * @return string A PHP class representing of the service container + * + * @api + */ + public function dump(array $options = array()) + { + $options = array_merge(array( + 'class' => 'ProjectServiceContainer', + 'base_class' => 'Container', + ), $options); + + $code = $this->startClass($options['class'], $options['base_class']); + + if ($this->container->isFrozen()) { + $code .= $this->addFrozenConstructor(); + } else { + $code .= $this->addConstructor(); + } + + $code .= + $this->addServices(). + $this->addDefaultParametersMethod(). + $this->endClass(). + $this->addProxyClasses() + ; + + 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); + } + + $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 $proxyDefinitions 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 = $this->dumpValue($definition->getClass()); + + 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 && ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) { + $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance'); + } elseif (!$isProxyCandidate && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { + $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$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 Boolean + */ + 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 + */ + 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; + } + + $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) { + return sprintf(" %s->%s(\$%s);\n", $this->getServiceCall((string) $callable[0]), $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) + { + $name = Container::camelize($id); + $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' : $class, $class); + } elseif ($definition->getFactoryClass()) { + $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod()); + } elseif ($definition->getFactoryService()) { + $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryService(), $definition->getFactoryMethod()); + } + + $scope = $definition->getScope(); + if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) { + if ($return && 0 === strpos($return[count($return) - 1], '@return')) { + $return[] = ''; + } + $return[] = sprintf("@throws InactiveScopeException when the '%s' service is requested while the '%s' scope is not active", $id, $scope); + } + + $return = implode("\n * ", $return); + + $doc = ''; + if (ContainerInterface::SCOPE_PROTOTYPE !== $scope) { + $doc .= <<isPublic()) { + $doc .= <<isLazy()) { + $lazyInitialization = '$lazyLoad = true'; + $lazyInitializationDoc = "\n * @param boolean \$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 = <<getProxyDumper()->getProxyFactoryCode($definition, $id) : ''; + + if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) { + $code .= <<scopedServices['$scope'])) { + throw new InactiveScopeException('$id', '$scope'); + } + + +EOF; + } + + 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 { + $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 = $synchronizers = ''; + $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); + } + + $synchronizers .= $this->addServiceSynchronizer($id, $definition); + } + + return $publicServices.$synchronizers.$privateServices; + } + + /** + * Adds synchronizer methods. + * + * @param string $id A service identifier + * @param Definition $definition A Definition instance + */ + private function addServiceSynchronizer($id, Definition $definition) + { + if (!$definition->isSynchronized()) { + return; + } + + $code = ''; + foreach ($this->container->getDefinitions() as $definitionId => $definition) { + foreach ($definition->getMethodCalls() as $call) { + foreach ($call[1] as $argument) { + if ($argument instanceof Reference && $id == (string) $argument) { + $arguments = array(); + foreach ($call[1] as $value) { + $arguments[] = $this->dumpValue($value); + } + + $call = $this->wrapServiceConditionals($call[1], sprintf("\$this->get('%s')->%s(%s);", $definitionId, $call[0], implode(', ', $arguments))); + + $code .= <<initialized('$definitionId')) { + $call + } + +EOF; + } + } + } + } + + if (!$code) { + return; + } + + $name = Container::camelize($id); + + return <<dumpValue($definition->getClass()); + + $arguments = array(); + foreach ($definition->getArguments() as $value) { + $arguments[] = $this->dumpValue($value); + } + + if (null !== $definition->getFactoryMethod()) { + if (null !== $definition->getFactoryClass()) { + return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass()), $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : ''); + } + + if (null !== $definition->getFactoryService()) { + return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService()), $definition->getFactoryMethod(), implode(', ', $arguments)); + } + + throw new RuntimeException(sprintf('Factory method requires a factory service or factory class in service definition for %s', $id)); + } + + 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", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments)); + } + + /** + * Adds the class headers. + * + * @param string $class Class name + * @param string $baseClass The name of the base class + * + * @return string + */ + private function startClass($class, $baseClass) + { + $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;'; + + return <<container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null; + + $code = <<container->getScopes()) > 0) { + $code .= "\n"; + $code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n"; + $code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n"; + } + + $code .= $this->addMethodMap(); + $code .= $this->addAliases(); + + $code .= <<container->getParameterBag()->all()) { + $code .= "\n \$this->parameters = \$this->getDefaultParameters();\n"; + } + + $code .= <<services = + \$this->scopedServices = + \$this->scopeStacks = array(); + + \$this->set('service_container', \$this); + +EOF; + + $code .= "\n"; + if (count($scopes = $this->container->getScopes()) > 0) { + $code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n"; + $code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n"; + } else { + $code .= " \$this->scopes = array();\n"; + $code .= " \$this->scopeChildren = 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'.Container::camelize($id).'Service', true).",\n"; + } + + return $code . " );\n"; + } + + /** + * Adds the aliases property definition + * + * @return string + */ + private function addAliases() + { + if (!$aliases = $this->container->getAliases()) { + 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)); + } else { + $value = var_export($value, true); + } + + $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] += 1; + } + } + } + + /** + * 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->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 Boolean $deep + * @param array $visited + * + * @return Boolean + */ + private function hasReference($id, array $arguments, $deep = false, $visited = array()) + { + foreach ($arguments as $argument) { + if (is_array($argument)) { + if ($this->hasReference($id, $argument, $deep, $visited)) { + return true; + } + } elseif ($argument instanceof Reference) { + if ($id === (string) $argument) { + return true; + } + + if ($deep && !isset($visited[(string) $argument])) { + $visited[(string) $argument] = true; + + $service = $this->container->getDefinition((string) $argument); + $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties()); + + if ($this->hasReference($id, $arguments, $deep, $visited)) { + return true; + } + } + } + } + + return false; + } + + /** + * Dumps values. + * + * @param array $value + * @param Boolean $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); + } + $class = $this->dumpValue($value->getClass()); + + if (false !== strpos($class, '$')) { + throw new RuntimeException('Cannot dump definitions which have a variable class name.'); + } + + if (null !== $value->getFactoryMethod()) { + if (null !== $value->getFactoryClass()) { + return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass()), $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); + } elseif (null !== $value->getFactoryService()) { + return sprintf("%s->%s(%s)", $this->getServiceCall($value->getFactoryService()), $value->getFactoryMethod(), implode(', ', $arguments)); + } else { + throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.'); + } + } + + return sprintf("new \\%s(%s)", substr(str_replace('\\\\', '\\', $class), 1, -1), 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 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 { + $that = $this; + $replaceParameters = function ($match) use ($that) { + return "'.".$that->dumpParameter(strtolower($match[2])).".'"; + }; + + $code = str_replace('%%', '%', preg_replace_callback('/(?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); + } + } + + /** + * 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 = intval($i/$firstCharsLength); + } + + while ($i > 0) { + $i -= 1; + $name .= $nonFirstChars[$i%$nonFirstCharsLength]; + $i = intval($i/$nonFirstCharsLength); + } + + $this->variableCount += 1; + + // check that the name is not reserved + if (in_array($name, $this->reservedVariables, true)) { + continue; + } + + return $name; + } + } +} 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..a311af3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.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\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; + +/** + * XmlDumper dumps a service container as an XML string. + * + * @author Fabien Potencier + * @author Martin Hasoň + * + * @api + */ +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 + * + * @api + */ + 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 ($definition->getClass()) { + $service->setAttribute('class', $definition->getClass()); + } + if ($definition->getFactoryMethod()) { + $service->setAttribute('factory-method', $definition->getFactoryMethod()); + } + if ($definition->getFactoryService()) { + $service->setAttribute('factory-service', $definition->getFactoryService()); + } + if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope()) { + $service->setAttribute('scope', $scope); + } + if (!$definition->isPublic()) { + $service->setAttribute('public', 'false'); + } + if ($definition->isSynthetic()) { + $service->setAttribute('synthetic', 'true'); + } + if ($definition->isSynchronized()) { + $service->setAttribute('synchronized', 'true'); + } + if ($definition->isLazy()) { + $service->setAttribute('lazy', 'true'); + } + + 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->getConfigurator()) { + $configurator = $this->document->createElement('configurator'); + if (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'); + } + if (!$value->isStrict()) { + $element->setAttribute('strict', 'false'); + } + } elseif ($value instanceof Definition) { + $element->setAttribute('type', 'service'); + $this->addService($value, null, $element); + } 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..807e656 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -0,0 +1,316 @@ + + * + * 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\DependencyInjection\ContainerBuilder; + +/** + * YamlDumper dumps a service container as a YAML string. + * + * @author Fabien Potencier + * + * @api + */ +class YamlDumper extends Dumper +{ + private $dumper; + + /** + * Constructor. + * + * @param ContainerBuilder $container The service container to dump + * + * @api + */ + public function __construct(ContainerBuilder $container) + { + parent::__construct($container); + + $this->dumper = new YmlDumper(); + } + + /** + * 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 + * + * @api + */ + public function dump(array $options = array()) + { + 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 ($definition->getClass()) { + $code .= sprintf(" class: %s\n", $definition->getClass()); + } + + $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->isSynchronized()) { + $code .= sprintf(" synchronized: true\n"); + } + + if ($definition->getFactoryClass()) { + $code .= sprintf(" factory_class: %s\n", $definition->getFactoryClass()); + } + + if ($definition->isLazy()) { + $code .= sprintf(" lazy: true\n"); + } + + if ($definition->getFactoryMethod()) { + $code .= sprintf(" factory_method: %s\n", $definition->getFactoryMethod()); + } + + if ($definition->getFactoryService()) { + $code .= sprintf(" factory_service: %s\n", $definition->getFactoryService()); + } + + 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 (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope()) { + $code .= sprintf(" scope: %s\n", $scope); + } + + if ($callable = $definition->getConfigurator()) { + 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]); + } + } + + $code .= sprintf(" configurator: %s\n", $this->dumper->dump($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); + } else { + 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 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 (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); + } + + /** + * Prepares parameters. + * + * @param array $parameters + * @param Boolean $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/InactiveScopeException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/InactiveScopeException.php new file mode 100644 index 0000000..6b3dd3e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/InactiveScopeException.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 you try to create a service of an inactive scope. + * + * @author Johannes M. Schmitt + */ +class InactiveScopeException extends RuntimeException +{ + private $serviceId; + private $scope; + + public function __construct($serviceId, $scope, \Exception $previous = null) + { + parent::__construct(sprintf('You cannot create a service ("%s") of an inactive scope ("%s").', $serviceId, $scope), 0, $previous); + + $this->serviceId = $serviceId; + $this->scope = $scope; + } + + public function getServiceId() + { + return $this->serviceId; + } + + public function getScope() + { + return $this->scope; + } +} 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/ScopeCrossingInjectionException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ScopeCrossingInjectionException.php new file mode 100644 index 0000000..661fbab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ScopeCrossingInjectionException.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\DependencyInjection\Exception; + +/** + * This exception is thrown when the a scope crossing injection is detected. + * + * @author Johannes M. Schmitt + */ +class ScopeCrossingInjectionException extends RuntimeException +{ + private $sourceServiceId; + private $sourceScope; + private $destServiceId; + private $destScope; + + public function __construct($sourceServiceId, $sourceScope, $destServiceId, $destScope, \Exception $previous = null) + { + parent::__construct(sprintf( + 'Scope Crossing Injection detected: The definition "%s" references the service "%s" which belongs to another scope hierarchy. ' + .'This service might not be available consistently. Generally, it is safer to either move the definition "%s" to scope "%s", or ' + .'declare "%s" as a child scope of "%s". If you can be sure that the other scope is always active, you can set the reference to strict=false to get rid of this error.', + $sourceServiceId, + $destServiceId, + $sourceServiceId, + $destScope, + $sourceScope, + $destScope + ), 0, $previous); + + $this->sourceServiceId = $sourceServiceId; + $this->sourceScope = $sourceScope; + $this->destServiceId = $destServiceId; + $this->destScope = $destScope; + } + + public function getSourceServiceId() + { + return $this->sourceServiceId; + } + + public function getSourceScope() + { + return $this->sourceScope; + } + + public function getDestServiceId() + { + return $this->destServiceId; + } + + public function getDestScope() + { + return $this->destScope; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ScopeWideningInjectionException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ScopeWideningInjectionException.php new file mode 100644 index 0000000..86a6684 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ScopeWideningInjectionException.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\Exception; + +/** + * Thrown when a scope widening injection is detected. + * + * @author Johannes M. Schmitt + */ +class ScopeWideningInjectionException extends RuntimeException +{ + private $sourceServiceId; + private $sourceScope; + private $destServiceId; + private $destScope; + + public function __construct($sourceServiceId, $sourceScope, $destServiceId, $destScope, \Exception $previous = null) + { + parent::__construct(sprintf( + 'Scope Widening Injection detected: The definition "%s" references the service "%s" which belongs to a narrower scope. ' + .'Generally, it is safer to either move "%s" to scope "%s" or alternatively rely on the provider pattern by injecting the container itself, and requesting the service "%s" each time it is needed. ' + .'In rare, special cases however that might not be necessary, then you can set the reference to strict=false to get rid of this error.', + $sourceServiceId, + $destServiceId, + $sourceServiceId, + $destScope, + $destServiceId + ), 0, $previous); + + $this->sourceServiceId = $sourceServiceId; + $this->sourceScope = $sourceScope; + $this->destServiceId = $destServiceId; + $this->destScope = $destScope; + } + + public function getSourceServiceId() + { + return $this->sourceServiceId; + } + + public function getSourceScope() + { + return $this->sourceScope; + } + + public function getDestServiceId() + { + return $this->destServiceId; + } + + public function getDestScope() + { + return $this->destScope; + } +} 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/Extension/ConfigurationExtensionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php new file mode 100644 index 0000000..6fcd901 --- /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 $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..a39fe37 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/Extension.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\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; + } + } + + return null; + } + + final protected function processConfiguration(ConfigurationInterface $configuration, array $configs) + { + $processor = new Processor(); + + return $processor->processConfiguration($configuration, $configs); + } + + /** + * @param ContainerBuilder $container + * @param array $config + * + * @return Boolean 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 (Boolean) $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..fc015e1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.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\DependencyInjection\Extension; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * ExtensionInterface is the interface implemented by container extension classes. + * + * @author Fabien Potencier + * + * @api + */ +interface ExtensionInterface +{ + /** + * Loads a specific configuration. + * + * @param array $config An array of configuration values + * @param ContainerBuilder $container A ContainerBuilder instance + * + * @throws InvalidArgumentException When provided tag is not defined in this extension + * + * @api + */ + public function load(array $config, ContainerBuilder $container); + + /** + * Returns the namespace to be used for this extension (XML namespace). + * + * @return string The XML namespace + * + * @api + */ + public function getNamespace(); + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + * + * @api + */ + 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 + * + * @api + */ + 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/IntrospectableContainerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/IntrospectableContainerInterface.php new file mode 100644 index 0000000..34d6cad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/IntrospectableContainerInterface.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; + +/** + * IntrospectableContainerInterface defines additional introspection functionality + * for containers, allowing logic to be implemented based on a Container's state. + * + * @author Evan Villemez + * + */ +interface IntrospectableContainerInterface extends ContainerInterface +{ + /** + * Check for whether or not a service has been initialized. + * + * @param string $id + * + * @return Boolean true if the service has been initialized, false otherwise + * + */ + public function initialized($id); + +} 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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/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..6495df2 --- /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..d8d5dac --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.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\LazyProxy\PhpDumper; + +use Symfony\Component\DependencyInjection\ContainerInterface; +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..e1d4ff4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.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\DependencyInjection\LazyProxy\PhpDumper; + +use Symfony\Component\DependencyInjection\ContainerInterface; +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..775a352 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.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\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; + } + + /** + * Loads a Closure. + * + * @param \Closure $closure The resource + * @param string $type The resource type + */ + public function load($closure, $type = null) + { + call_user_func($closure, $this->container); + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean true if this class supports the given resource, false otherwise + */ + public function supports($resource, $type = null) + { + return $resource instanceof \Closure; + } +} 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..ad437b5 --- /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..e4b99f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/IniFileLoader.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\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 +{ + /** + * Loads a resource. + * + * @param mixed $file The resource + * @param string $type The resource type + * + * @throws InvalidArgumentException When ini file is not valid + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + + $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.', $file)); + } + + if (isset($result['parameters']) && is_array($result['parameters'])) { + foreach ($result['parameters'] as $key => $value) { + $this->container->setParameter($key, $value); + } + } + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean true if this class supports the given resource, false otherwise + */ + 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..f2bf441 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.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\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 form the file to change the container. + * + * @author Fabien Potencier + */ +class PhpFileLoader extends FileLoader +{ + /** + * Loads a PHP file. + * + * @param mixed $file The resource + * @param string $type The resource type + */ + public function load($file, $type = null) + { + // the container and loader variables are exposed to the included file below + $container = $this->container; + $loader = $this; + + $path = $this->locator->locate($file); + $this->setCurrentDir(dirname($path)); + $this->container->addResource(new FileResource($path)); + + include $path; + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean true if this class supports the given resource, false otherwise + */ + 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..9f25ab7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -0,0 +1,418 @@ + + * + * 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\SimpleXMLElement; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * XmlFileLoader loads XML files service definitions. + * + * @author Fabien Potencier + */ +class XmlFileLoader extends FileLoader +{ + /** + * Loads an XML file. + * + * @param mixed $file The resource + * @param string $type The resource type + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + + $xml = $this->parseFile($path); + $xml->registerXPathNamespace('container', 'http://symfony.com/schema/dic/services'); + + $this->container->addResource(new FileResource($path)); + + // anonymous services + $this->processAnonymousServices($xml, $path); + + // imports + $this->parseImports($xml, $path); + + // parameters + $this->parseParameters($xml, $path); + + // extensions + $this->loadFromExtensions($xml); + + // services + $this->parseDefinitions($xml, $path); + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean true if this class supports the given resource, false otherwise + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION); + } + + /** + * Parses parameters + * + * @param SimpleXMLElement $xml + * @param string $file + */ + private function parseParameters(SimpleXMLElement $xml, $file) + { + if (!$xml->parameters) { + return; + } + + $this->container->getParameterBag()->add($xml->parameters->getArgumentsAsPhp('parameter')); + } + + /** + * Parses imports + * + * @param SimpleXMLElement $xml + * @param string $file + */ + private function parseImports(SimpleXMLElement $xml, $file) + { + if (false === $imports = $xml->xpath('//container:imports/container:import')) { + return; + } + + foreach ($imports as $import) { + $this->setCurrentDir(dirname($file)); + $this->import((string) $import['resource'], null, (Boolean) $import->getAttributeAsPhp('ignore-errors'), $file); + } + } + + /** + * Parses multiple definitions + * + * @param SimpleXMLElement $xml + * @param string $file + */ + private function parseDefinitions(SimpleXMLElement $xml, $file) + { + if (false === $services = $xml->xpath('//container:services/container:service')) { + return; + } + + foreach ($services as $service) { + $this->parseDefinition((string) $service['id'], $service, $file); + } + } + + /** + * Parses an individual Definition + * + * @param string $id + * @param SimpleXMLElement $service + * @param string $file + */ + private function parseDefinition($id, $service, $file) + { + if ((string) $service['alias']) { + $public = true; + if (isset($service['public'])) { + $public = $service->getAttributeAsPhp('public'); + } + $this->container->setAlias($id, new Alias((string) $service['alias'], $public)); + + return; + } + + if (isset($service['parent'])) { + $definition = new DefinitionDecorator((string) $service['parent']); + } else { + $definition = new Definition(); + } + + foreach (array('class', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'synchronized', 'lazy', 'abstract') as $key) { + if (isset($service[$key])) { + $method = 'set'.str_replace('-', '', $key); + $definition->$method((string) $service->getAttributeAsPhp($key)); + } + } + + if ($service->file) { + $definition->setFile((string) $service->file); + } + + $definition->setArguments($service->getArgumentsAsPhp('argument')); + $definition->setProperties($service->getArgumentsAsPhp('property')); + + if (isset($service->configurator)) { + if (isset($service->configurator['function'])) { + $definition->setConfigurator((string) $service->configurator['function']); + } else { + if (isset($service->configurator['service'])) { + $class = new Reference((string) $service->configurator['service'], ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false); + } else { + $class = (string) $service->configurator['class']; + } + + $definition->setConfigurator(array($class, (string) $service->configurator['method'])); + } + } + + foreach ($service->call as $call) { + $definition->addMethodCall((string) $call['method'], $call->getArgumentsAsPhp('argument')); + } + + foreach ($service->tag as $tag) { + $parameters = array(); + foreach ($tag->attributes() as $name => $value) { + if ('name' === $name) { + continue; + } + + $parameters[$name] = SimpleXMLElement::phpize($value); + } + + $definition->addTag((string) $tag['name'], $parameters); + } + + $this->container->setDefinition($id, $definition); + } + + /** + * Parses a XML file. + * + * @param string $file Path to a file + * + * @return SimpleXMLElement + * + * @throws InvalidArgumentException When loading of XML file returns error + */ + protected function parseFile($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 simplexml_import_dom($dom, 'Symfony\\Component\\DependencyInjection\\SimpleXMLElement'); + } + + /** + * Processes anonymous services + * + * @param SimpleXMLElement $xml + * @param string $file + */ + private function processAnonymousServices(SimpleXMLElement $xml, $file) + { + $definitions = array(); + $count = 0; + + // anonymous services as arguments/properties + if (false !== $nodes = $xml->xpath('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) { + foreach ($nodes as $node) { + // give it a unique name + $node['id'] = sprintf('%s_%d', md5($file), ++$count); + + $definitions[(string) $node['id']] = array($node->service, $file, false); + $node->service['id'] = (string) $node['id']; + } + } + + // anonymous services "in the wild" + if (false !== $nodes = $xml->xpath('//container:services/container:service[not(@id)]')) { + foreach ($nodes as $node) { + // give it a unique name + $node['id'] = sprintf('%s_%d', md5($file), ++$count); + + $definitions[(string) $node['id']] = array($node, $file, true); + $node->service['id'] = (string) $node['id']; + } + } + + // resolve definitions + krsort($definitions); + foreach ($definitions as $id => $def) { + // anonymous services are always private + $def[0]['public'] = false; + + $this->parseDefinition($id, $def[0], $def[1]); + + $oNode = dom_import_simplexml($def[0]); + if (true === $def[2]) { + $nNode = new \DOMElement('_services'); + $oNode->parentNode->replaceChild($nNode, $oNode); + $nNode->setAttribute('id', $id); + } else { + $oNode->parentNode->removeChild($oNode); + } + } + } + + /** + * Validates a documents XML schema. + * + * @param \DOMDocument $dom + * + * @return Boolean + * + * @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 SimpleXMLElement $xml + */ + private function loadFromExtensions(SimpleXMLElement $xml) + { + foreach (dom_import_simplexml($xml)->childNodes as $node) { + if (!$node instanceof \DOMElement || $node->namespaceURI === 'http://symfony.com/schema/dic/services') { + 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..ce135fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -0,0 +1,360 @@ + + * + * 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\Config\Resource\FileResource; +use Symfony\Component\Yaml\Parser as YamlParser; + +/** + * 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; + + /** + * Loads a Yaml file. + * + * @param mixed $file The resource + * @param string $type The resource type + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + + $content = $this->loadFile($path); + + $this->container->addResource(new FileResource($path)); + + // empty file + if (null === $content) { + return; + } + + // imports + $this->parseImports($content, $file); + + // parameters + if (isset($content['parameters'])) { + foreach ($content['parameters'] as $key => $value) { + $this->container->setParameter($key, $this->resolveServices($value)); + } + } + + // extensions + $this->loadFromExtensions($content); + + // services + $this->parseDefinitions($content, $file); + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean true if this class supports the given resource, false otherwise + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'yml' === pathinfo($resource, PATHINFO_EXTENSION); + } + + /** + * Parses all imports + * + * @param array $content + * @param string $file + */ + private function parseImports($content, $file) + { + if (!isset($content['imports'])) { + return; + } + + foreach ($content['imports'] as $import) { + $this->setCurrentDir(dirname($file)); + $this->import($import['resource'], null, isset($import['ignore_errors']) ? (Boolean) $import['ignore_errors'] : false, $file); + } + } + + /** + * Parses definitions + * + * @param array $content + * @param string $file + */ + private function parseDefinitions($content, $file) + { + if (!isset($content['services'])) { + return; + } + + 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; + } elseif (isset($service['alias'])) { + $public = !array_key_exists('public', $service) || (Boolean) $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['scope'])) { + $definition->setScope($service['scope']); + } + + if (isset($service['synthetic'])) { + $definition->setSynthetic($service['synthetic']); + } + + if (isset($service['synchronized'])) { + $definition->setSynchronized($service['synchronized']); + } + + 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 (isset($service['factory_class'])) { + $definition->setFactoryClass($service['factory_class']); + } + + if (isset($service['factory_method'])) { + $definition->setFactoryMethod($service['factory_method']); + } + + if (isset($service['factory_service'])) { + $definition->setFactoryService($service['factory_service']); + } + + 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'])) { + foreach ($service['calls'] as $call) { + $args = isset($call[1]) ? $this->resolveServices($call[1]) : array(); + $definition->addMethodCall($call[0], $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.', $id, $file)); + } + + foreach ($service['tags'] as $tag) { + 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)) { + throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s" in %s.', $id, $name, $file)); + } + } + + $definition->addTag($name, $tag); + } + } + + $this->container->setDefinition($id, $definition); + } + + /** + * Loads a YAML file. + * + * @param string $file + * + * @return array The file content + */ + protected function loadFile($file) + { + 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(); + } + + return $this->validate($this->yamlParser->parse(file_get_contents($file)), $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.', $file)); + } + + foreach (array_keys($content) as $namespace) { + 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 $value + * + * @return Reference + */ + private function resolveServices($value) + { + if (is_array($value)) { + $value = array_map(array($this, 'resolveServices'), $value); + } 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..f1c2003 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..7ba8c3a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Parameter.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; + +/** + * Parameter represents a parameter reference. + * + * @author Fabien Potencier + * + * @api + */ +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..9664b13 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.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\DependencyInjection\ParameterBag; + +use Symfony\Component\DependencyInjection\Exception\LogicException; + +/** + * Holds read-only parameters. + * + * @author Fabien Potencier + * + * @api + */ +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 + * + * @api + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $parameters; + $this->resolved = true; + } + + /** + * {@inheritDoc} + * + * @api + */ + public function clear() + { + throw new LogicException('Impossible to call clear() on a frozen ParameterBag.'); + } + + /** + * {@inheritDoc} + * + * @api + */ + public function add(array $parameters) + { + throw new LogicException('Impossible to call add() on a frozen ParameterBag.'); + } + + /** + * {@inheritDoc} + * + * @api + */ + public function set($name, $value) + { + throw new LogicException('Impossible to call set() 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..494d231 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php @@ -0,0 +1,306 @@ + + * + * 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 + * + * @api + */ +class ParameterBag implements ParameterBagInterface +{ + protected $parameters; + protected $resolved; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * + * @api + */ + public function __construct(array $parameters = array()) + { + $this->parameters = array(); + $this->add($parameters); + $this->resolved = false; + } + + /** + * Clears all parameters. + * + * @api + */ + public function clear() + { + $this->parameters = array(); + } + + /** + * Adds parameters to the service container parameters. + * + * @param array $parameters An array of parameters + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + public function get($name) + { + $name = strtolower($name); + + if (!array_key_exists($name, $this->parameters)) { + if (!$name) { + throw new ParameterNotFoundException($name); + } + + $alternatives = array(); + foreach (array_keys($this->parameters) as $key) { + $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 + * + * @api + */ + 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 Boolean true if the parameter name is defined, false otherwise + * + * @api + */ + public function has($name) + { + return array_key_exists(strtolower($name), $this->parameters); + } + + /** + * Removes a parameter. + * + * @param string $name The parameter name + * + * @api + */ + 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); + } + + $self = $this; + + return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($self, $resolving, $value) { + // skip %% + if (!isset($match[1])) { + return '%%'; + } + + $key = strtolower($match[1]); + if (isset($resolving[$key])) { + throw new ParameterCircularReferenceException(array_keys($resolving)); + } + + $resolved = $self->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 $self->isResolved() ? $resolved : $self->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..a26d6ae --- /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\ParameterNotFoundException; + +/** + * ParameterBagInterface. + * + * @author Fabien Potencier + * + * @api + */ +interface ParameterBagInterface +{ + /** + * Clears all parameters. + * + * @api + */ + public function clear(); + + /** + * Adds parameters to the service container parameters. + * + * @param array $parameters An array of parameters + * + * @api + */ + public function add(array $parameters); + + /** + * Gets the service container parameters. + * + * @return array An array of parameters + * + * @api + */ + 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 + * + * @api + */ + public function get($name); + + /** + * Sets a service container parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + * + * @api + */ + public function set($name, $value); + + /** + * Returns true if a parameter name is defined. + * + * @param string $name The parameter name + * + * @return Boolean true if the parameter name is defined, false otherwise + * + * @api + */ + 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..ab67595 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/README.md @@ -0,0 +1,73 @@ +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: + + 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): + + $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: + + $sc = new ContainerBuilder(); + + $sc + ->register('bar', '%bar.class%') + ->setFactoryClass('%bar.class%') + ->setFactoryMethod('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. + + $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.phar install --dev + $ 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..1517da2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Reference.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\DependencyInjection; + +/** + * Reference represents a service reference. + * + * @author Fabien Potencier + * + * @api + */ +class Reference +{ + private $id; + private $invalidBehavior; + private $strict; + + /** + * Constructor. + * + * @param string $id The service identifier + * @param int $invalidBehavior The behavior when the service does not exist + * @param Boolean $strict Sets how this reference is validated + * + * @see Container + */ + public function __construct($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $strict = true) + { + $this->id = strtolower($id); + $this->invalidBehavior = $invalidBehavior; + $this->strict = $strict; + } + + /** + * __toString. + * + * @return string The service identifier + */ + public function __toString() + { + return (string) $this->id; + } + + /** + * Returns the behavior to be used when the service does not exist. + * + * @return int + */ + public function getInvalidBehavior() + { + return $this->invalidBehavior; + } + + /** + * Returns true when this Reference is strict + * + * @return Boolean + */ + public function isStrict() + { + return $this->strict; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Scope.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Scope.php new file mode 100644 index 0000000..161229e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Scope.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; + +/** + * Scope class. + * + * @author Johannes M. Schmitt + * + * @api + */ +class Scope implements ScopeInterface +{ + private $name; + private $parentName; + + /** + * @api + */ + public function __construct($name, $parentName = ContainerInterface::SCOPE_CONTAINER) + { + $this->name = $name; + $this->parentName = $parentName; + } + + /** + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * @api + */ + public function getParentName() + { + return $this->parentName; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ScopeInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ScopeInterface.php new file mode 100644 index 0000000..81ac67c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ScopeInterface.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\DependencyInjection; + +/** + * Scope Interface. + * + * @author Johannes M. Schmitt + * + * @api + */ +interface ScopeInterface +{ + /** + * @api + */ + public function getName(); + + /** + * @api + */ + public function getParentName(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/SimpleXMLElement.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/SimpleXMLElement.php new file mode 100644 index 0000000..cc5e311 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/SimpleXMLElement.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\DependencyInjection; + +use Symfony\Component\Config\Util\XmlUtils; + +/** + * SimpleXMLElement class. + * + * @author Fabien Potencier + */ +class SimpleXMLElement extends \SimpleXMLElement +{ + /** + * Converts an attribute as a php type. + * + * @param string $name + * + * @return mixed + */ + public function getAttributeAsPhp($name) + { + return self::phpize($this[$name]); + } + + /** + * Returns arguments as valid php types. + * + * @param string $name + * @param Boolean $lowercase + * + * @return mixed + */ + public function getArgumentsAsPhp($name, $lowercase = true) + { + $arguments = array(); + foreach ($this->$name as $arg) { + if (isset($arg['name'])) { + $arg['key'] = (string) $arg['name']; + } + $key = isset($arg['key']) ? (string) $arg['key'] : (!$arguments ? 0 : max(array_keys($arguments)) + 1); + + // 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 (isset($arg['index'])) { + $key = 'index_'.$arg['index']; + } + + switch ($arg['type']) { + case 'service': + $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + if (isset($arg['on-invalid']) && 'ignore' == $arg['on-invalid']) { + $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + } elseif (isset($arg['on-invalid']) && 'null' == $arg['on-invalid']) { + $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; + } + + if (isset($arg['strict'])) { + $strict = self::phpize($arg['strict']); + } else { + $strict = true; + } + + $arguments[$key] = new Reference((string) $arg['id'], $invalidBehavior, $strict); + break; + case 'collection': + $arguments[$key] = $arg->getArgumentsAsPhp($name, false); + break; + case 'string': + $arguments[$key] = (string) $arg; + break; + case 'constant': + $arguments[$key] = constant((string) $arg); + break; + default: + $arguments[$key] = self::phpize($arg); + } + } + + return $arguments; + } + + /** + * Converts an xml value to a php type. + * + * @param mixed $value + * + * @return mixed + */ + public static function phpize($value) + { + return XmlUtils::phpize($value); + } +} 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..3b48817 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/TaggedContainerInterface.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; + +/** + * TaggedContainerInterface is the interface implemented when a container knows how to deals with tags. + * + * @author Fabien Potencier + * + * @api + */ +interface TaggedContainerInterface extends ContainerInterface +{ + /** + * Returns service ids for a given tag. + * + * @param string $name The tag name + * + * @return array An array of tags + * + * @api + */ + 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..c99659e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/AnalyzeServiceReferencesPassTest.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\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Compiler\Compiler; +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 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()); + } + + 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/CheckCircularReferencesPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckCircularReferencesPassTest.php new file mode 100644 index 0000000..085bc51 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckCircularReferencesPassTest.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\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 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 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..06845a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.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\Compiler\CheckDefinitionValidityPass; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class CheckDefinitionValidityPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \RuntimeException + */ + public function testProcessDetectsSyntheticNonPublicDefinitions() + { + $container = new ContainerBuilder(); + $container->register('a')->setSynthetic(true)->setPublic(false); + + $this->process($container); + } + + /** + * @expectedException \RuntimeException + */ + public function testProcessDetectsSyntheticPrototypeDefinitions() + { + $container = new ContainerBuilder(); + $container->register('a')->setSynthetic(true)->setScope(ContainerInterface::SCOPE_PROTOTYPE); + + $this->process($container); + } + + /** + * @expectedException \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); + } + + 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..e71835e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.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\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..ee18e5c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.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\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Scope; + +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 +{ + public function testProcessIgnoresScopeWideningIfNonStrictReference() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false)); + $container->register('b')->setScope('prototype'); + + $this->process($container); + } + + /** + * @expectedException \RuntimeException + */ + public function testProcessDetectsScopeWidening() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b')->setScope('prototype'); + + $this->process($container); + } + + public function testProcessIgnoresCrossScopeHierarchyReferenceIfNotStrict() + { + $container = new ContainerBuilder(); + $container->addScope(new Scope('a')); + $container->addScope(new Scope('b')); + + $container->register('a')->setScope('a')->addArgument(new Reference('b', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false)); + $container->register('b')->setScope('b'); + + $this->process($container); + } + + /** + * @expectedException \RuntimeException + */ + public function testProcessDetectsCrossScopeHierarchyReference() + { + $container = new ContainerBuilder(); + $container->addScope(new Scope('a')); + $container->addScope(new Scope('b')); + + $container->register('a')->setScope('a')->addArgument(new Reference('b')); + $container->register('b')->setScope('b'); + + $this->process($container); + } + + /** + * @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/InlineServiceDefinitionsPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php new file mode 100644 index 0000000..f22f0da --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.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\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Scope; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\Compiler; +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 testProcessDoesNotInlineWhenAliasedServiceIsNotOfPrototypeScope() + { + $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 testProcessDoesInlineServiceOfPrototypeScope() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setScope('prototype') + ; + $container + ->register('bar') + ->setPublic(false) + ->setScope('prototype') + ; + $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 testProcessInlinesOnlyIfSameScope() + { + $container = new ContainerBuilder(); + + $container->addScope(new Scope('foo')); + $a = $container->register('a')->setPublic(false)->setScope('foo'); + $b = $container->register('b')->addArgument(new Reference('a')); + + $this->process($container); + $arguments = $b->getArguments(); + $this->assertEquals(new Reference('a'), $arguments[0]); + $this->assertTrue($container->hasDefinition('a')); + } + + 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..1d4ea07 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.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\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 the following dependencies are correctly processed: + * + * 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/RemoveUnusedDefinitionsPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.php new file mode 100644 index 0000000..d7e5521 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.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\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\Compiler; +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')); + } + + 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..f3c5b15 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.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\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 testProcessDoesNotCopyScope() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->setScope('foo') + ; + + $container + ->setDefinition('child', new DefinitionDecorator('parent')) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertEquals(ContainerInterface::SCOPE_CONTAINER, $def->getScope()); + } + + 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 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()); + } + + 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..7205886 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInvalidReferencesPassTest.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\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()); + } + + public function testStrictFlagIsPreserved() + { + $container = new ContainerBuilder(); + $container->register('bar'); + $def = $container + ->register('foo') + ->addArgument(new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE, false)) + ; + + $this->process($container); + + $this->assertFalse($def->getArgument(0)->isStrict()); + } + + protected function process(ContainerBuilder $container) + { + $pass = new ResolveInvalidReferencesPass(); + $pass->process($container); + } +} 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..ca089a5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveReferencesToAliasesPassTest.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\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]); + } + + 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..a5e7531 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -0,0 +1,778 @@ + + * + * 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\Compiler\CompilerPassInterface; +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\Reference; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Scope; +use Symfony\Component\Config\Resource\FileResource; + +class ContainerBuilderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::setDefinitions + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getDefinitions + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::setDefinition + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getDefinition + */ + public function testDefinitions() + { + $builder = new ContainerBuilder(); + $definitions = array( + 'foo' => new Definition('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'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::register + */ + public function testRegister() + { + $builder = new ContainerBuilder(); + $builder->register('foo', '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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::has + */ + public function testHas() + { + $builder = new ContainerBuilder(); + $this->assertFalse($builder->has('foo'), '->has() returns false if the service does not exist'); + $builder->register('foo', '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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::get + */ + 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'); + } + + $builder->register('foobar', 'stdClass')->setScope('container'); + $this->assertTrue($builder->get('bar') === $builder->get('bar'), '->get() always returns the same instance if the service is shared'); + } + + /** + * @covers \Symfony\Component\DependencyInjection\ContainerBuilder::get + * @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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::get + */ + public function testGetReturnsNullOnInactiveScope() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'stdClass')->setScope('request'); + + $this->assertNull($builder->get('foo', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getServiceIds + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::setAlias + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::hasAlias + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getAlias + */ + 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->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'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getAliases + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::setAliases + */ + 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'])); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::addAliases + */ + 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'])); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::addCompilerPass + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getCompilerPassConfig + */ + public function testAddGetCompilerPass() + { + $builder = new ContainerBuilder(); + $builder->setResourceTracking(false); + $builderCompilerPasses = $builder->getCompiler()->getPassConfig()->getPasses(); + $builder->addCompilerPass($this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface')); + $this->assertEquals(sizeof($builderCompilerPasses) + 1, sizeof($builder->getCompiler()->getPassConfig()->getPasses())); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + public function testCreateService() + { + $builder = new ContainerBuilder(); + $builder->register('foo1', 'FooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php'); + $this->assertInstanceOf('\FooClass', $builder->get('foo1'), '->createService() requires the file defined by the service definition'); + $builder->register('foo2', 'FooClass')->setFile(__DIR__.'/Fixtures/includes/%file%.php'); + $builder->setParameter('file', 'foo'); + $this->assertInstanceOf('\FooClass', $builder->get('foo2'), '->createService() replaces parameters in the file provided by the service definition'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + public function testCreateProxyWithRealServiceInstantiator() + { + $builder = new ContainerBuilder(); + + $builder->register('foo1', '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('FooClass', get_class($foo1)); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + public function testCreateServiceArguments() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', '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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + public function testCreateServiceFactoryMethod() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', 'FooClass')->setFactoryClass('FooClass')->setFactoryMethod('getInstance')->addArgument(array('foo' => '%value%', '%value%' => 'foo', new Reference('bar'))); + $builder->setParameter('value', 'bar'); + $this->assertTrue($builder->get('foo1')->called, '->createService() calls the factory method to create the service instance'); + $this->assertEquals(array('foo' => 'bar', 'bar' => 'foo', $builder->get('bar')), $builder->get('foo1')->arguments, '->createService() passes the arguments to the factory method'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + public function testCreateServiceFactoryService() + { + $builder = new ContainerBuilder(); + $builder->register('baz_service')->setFactoryService('baz_factory')->setFactoryMethod('getInstance'); + $builder->register('baz_factory', 'BazClass'); + + $this->assertInstanceOf('BazClass', $builder->get('baz_service')); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + public function testCreateServiceMethodCalls() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', '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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + public function testCreateServiceConfigurator() + { + $builder = new ContainerBuilder(); + $builder->register('foo1', 'FooClass')->setConfigurator('sc_configure'); + $this->assertTrue($builder->get('foo1')->configured, '->createService() calls the configurator'); + + $builder->register('foo2', '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', 'FooClass')->setConfigurator(array(new Reference('baz'), 'configure')); + $this->assertTrue($builder->get('foo3')->configured, '->createService() calls the configurator'); + + $builder->register('foo4', 'FooClass')->setConfigurator('foo'); + try { + $builder->get('foo4'); + $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 "FooClass" is not a callable.', $e->getMessage(), '->createService() throws an InvalidArgumentException if the configure callable is not a valid callable'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + * @expectedException \RuntimeException + */ + public function testCreateSyntheticService() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'FooClass')->setSynthetic(true); + $builder->get('foo'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::resolveServices + */ + public function testResolveServices() + { + $builder = new ContainerBuilder(); + $builder->register('foo', '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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::merge + */ + 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); +////// FIXME + $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); +////// FIXME + $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', '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', 'FooClass'); + $config->setDefinition('foo', new Definition('BazClass')); + $container->merge($config); + $this->assertEquals('BazClass', $container->getDefinition('foo')->getClass(), '->merge() overrides already defined services'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::merge + * @expectedException LogicException + */ + public function testMergeLogicException() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->compile(); + $container->merge(new ContainerBuilder()); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::findTaggedServiceIds + */ + public function testfindTaggedServiceIds() + { + $builder = new ContainerBuilder(); + $builder + ->register('foo', '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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::findDefinition + */ + public function testFindDefinition() + { + $container = new ContainerBuilder(); + $container->setDefinition('foo', $definition = new Definition('FooClass')); + $container->setAlias('bar', 'foo'); + $container->setAlias('foobar', 'bar'); + $this->assertEquals($definition, $container->findDefinition('foobar'), '->findDefinition() returns a Definition'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::addObjectResource + */ + public function testAddObjectResource() + { + if (!class_exists('Symfony\Component\Config\Resource\FileResource')) { + $this->markTestSkipped('The "Config" component is not available'); + } + + $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())); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::addClassResource + */ + public function testAddClassResource() + { + if (!class_exists('Symfony\Component\Config\Resource\FileResource')) { + $this->markTestSkipped('The "Config" component is not available'); + } + + $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())); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::compile + */ + public function testCompilesClassDefinitionsOfLazyServices() + { + if (!class_exists('Symfony\Component\Config\Resource\FileResource')) { + $this->markTestSkipped('The "Config" component is not available'); + } + + $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); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getResources + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::addResource + */ + public function testResources() + { + if (!class_exists('Symfony\Component\Config\Resource\FileResource')) { + $this->markTestSkipped('The "Config" component is not available'); + } + + $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()); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::registerExtension + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getExtension + */ + 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() + { + if (!class_exists('Symfony\Component\Config\Resource\FileResource')) { + $this->markTestSkipped('The "Config" component is not available'); + } + + $container = new ContainerBuilder(); + $container->compile(); + $container->set('a', new \stdClass()); + } + + public function testNoExceptionWhenSetSyntheticServiceOnAFrozenContainer() + { + if (!class_exists('Symfony\Component\Config\Resource\FileResource')) { + $this->markTestSkipped('The "Config" component is not available'); + } + + $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')); + } + + public function testSetOnSynchronizedService() + { + $container = new ContainerBuilder(); + $container->register('baz', 'BazClass') + ->setSynchronized(true) + ; + $container->register('bar', 'BarClass') + ->addMethodCall('setBaz', array(new Reference('baz'))) + ; + + $container->set('baz', $baz = new \BazClass()); + $this->assertSame($baz, $container->get('bar')->getBaz()); + + $container->set('baz', $baz = new \BazClass()); + $this->assertSame($baz, $container->get('bar')->getBaz()); + } + + public function testSynchronizedServiceWithScopes() + { + $container = new ContainerBuilder(); + $container->addScope(new Scope('foo')); + $container->register('baz', 'BazClass') + ->setSynthetic(true) + ->setSynchronized(true) + ->setScope('foo') + ; + $container->register('bar', 'BarClass') + ->addMethodCall('setBaz', array(new Reference('baz', ContainerInterface::NULL_ON_INVALID_REFERENCE, false))) + ; + $container->compile(); + + $container->enterScope('foo'); + $container->set('baz', $outerBaz = new \BazClass(), 'foo'); + $this->assertSame($outerBaz, $container->get('bar')->getBaz()); + + $container->enterScope('foo'); + $container->set('baz', $innerBaz = new \BazClass(), 'foo'); + $this->assertSame($innerBaz, $container->get('bar')->getBaz()); + $container->leaveScope('foo'); + + $this->assertNotSame($innerBaz, $container->get('bar')->getBaz()); + $this->assertSame($outerBaz, $container->get('bar')->getBaz()); + + $container->leaveScope('foo'); + } + + /** + * @expectedException BadMethodCallException + */ + public function testThrowsExceptionWhenSetDefinitionOnAFrozenContainer() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->compile(); + $container->setDefinition('a', new Definition()); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getExtensionConfig + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::prependExtensionConfig + */ + 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); + } +} + +class FooClass {} 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..f49a567 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php @@ -0,0 +1,549 @@ + + * + * 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\Scope; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Exception\InactiveScopeException; + +class ContainerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\DependencyInjection\Container::__construct + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Container::compile + */ + public function testCompile() + { + $sc = new Container(new ParameterBag(array('foo' => 'bar'))); + $sc->compile(); + $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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Container::isFrozen + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Container::getParameterBag + */ + public function testGetParameterBag() + { + $sc = new Container(); + $this->assertEquals(array(), $sc->getParameterBag()->all(), '->getParameterBag() returns an empty array if no parameter has been defined'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Container::setParameter + * @covers Symfony\Component\DependencyInjection\Container::getParameter + */ + 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'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\Container::getServiceIds + */ + 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(); + $this->assertEquals(array('scoped', 'scoped_foo', 'inactive', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'service_container'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by getXXXService() methods'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Container::set + */ + public function testSet() + { + $sc = new Container(); + $sc->set('foo', $foo = new \stdClass()); + $this->assertEquals($foo, $sc->get('foo'), '->set() sets a service'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSetDoesNotAllowPrototypeScope() + { + $c = new Container(); + $c->set('foo', new \stdClass(), 'prototype'); + } + + /** + * @expectedException \RuntimeException + */ + public function testSetDoesNotAllowInactiveScope() + { + $c = new Container(); + $c->addScope(new Scope('foo')); + $c->set('foo', new \stdClass(), 'foo'); + } + + public function testSetAlsoSetsScopedService() + { + $c = new Container(); + $c->addScope(new Scope('foo')); + $c->enterScope('foo'); + $c->set('foo', $foo = new \stdClass(), 'foo'); + + $services = $this->getField($c, 'scopedServices'); + $this->assertTrue(isset($services['foo']['foo'])); + $this->assertSame($foo, $services['foo']['foo']); + } + + /** + * @covers Symfony\Component\DependencyInjection\Container::get + */ + 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($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'); + + $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)); + } + + 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'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\Container::get + */ + public function testGetReturnsNullOnInactiveScope() + { + $sc = new ProjectServiceContainer(); + $this->assertNull($sc->get('inactive', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * @covers Symfony\Component\DependencyInjection\Container::has + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Container::initialized + */ + 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'); + } + + public function testEnterLeaveCurrentScope() + { + $container = new ProjectServiceContainer(); + $container->addScope(new Scope('foo')); + + $container->enterScope('foo'); + $scoped1 = $container->get('scoped'); + $scopedFoo1 = $container->get('scoped_foo'); + + $container->enterScope('foo'); + $scoped2 = $container->get('scoped'); + $scoped3 = $container->get('scoped'); + $scopedFoo2 = $container->get('scoped_foo'); + + $container->leaveScope('foo'); + $scoped4 = $container->get('scoped'); + $scopedFoo3 = $container->get('scoped_foo'); + + $this->assertNotSame($scoped1, $scoped2); + $this->assertSame($scoped2, $scoped3); + $this->assertSame($scoped1, $scoped4); + $this->assertNotSame($scopedFoo1, $scopedFoo2); + $this->assertSame($scopedFoo1, $scopedFoo3); + } + + public function testEnterLeaveScopeWithChildScopes() + { + $container = new Container(); + $container->addScope(new Scope('foo')); + $container->addScope(new Scope('bar', 'foo')); + + $this->assertFalse($container->isScopeActive('foo')); + + $container->enterScope('foo'); + $container->enterScope('bar'); + + $this->assertTrue($container->isScopeActive('foo')); + $this->assertFalse($container->has('a')); + + $a = new \stdClass(); + $container->set('a', $a, 'bar'); + + $services = $this->getField($container, 'scopedServices'); + $this->assertTrue(isset($services['bar']['a'])); + $this->assertSame($a, $services['bar']['a']); + + $this->assertTrue($container->has('a')); + $container->leaveScope('foo'); + + $services = $this->getField($container, 'scopedServices'); + $this->assertFalse(isset($services['bar'])); + + $this->assertFalse($container->isScopeActive('foo')); + $this->assertFalse($container->has('a')); + } + + public function testEnterScopeRecursivelyWithInactiveChildScopes() + { + $container = new Container(); + $container->addScope(new Scope('foo')); + $container->addScope(new Scope('bar', 'foo')); + + $this->assertFalse($container->isScopeActive('foo')); + + $container->enterScope('foo'); + + $this->assertTrue($container->isScopeActive('foo')); + $this->assertFalse($container->isScopeActive('bar')); + $this->assertFalse($container->has('a')); + + $a = new \stdClass(); + $container->set('a', $a, 'foo'); + + $services = $this->getField($container, 'scopedServices'); + $this->assertTrue(isset($services['foo']['a'])); + $this->assertSame($a, $services['foo']['a']); + + $this->assertTrue($container->has('a')); + $container->enterScope('foo'); + + $services = $this->getField($container, 'scopedServices'); + $this->assertFalse(isset($services['a'])); + + $this->assertTrue($container->isScopeActive('foo')); + $this->assertFalse($container->isScopeActive('bar')); + $this->assertFalse($container->has('a')); + } + + public function testLeaveScopeNotActive() + { + $container = new Container(); + $container->addScope(new Scope('foo')); + + try { + $container->leaveScope('foo'); + $this->fail('->leaveScope() throws a \LogicException if the scope is not active yet'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->leaveScope() throws a \LogicException if the scope is not active yet'); + $this->assertEquals('The scope "foo" is not active.', $e->getMessage(), '->leaveScope() throws a \LogicException if the scope is not active yet'); + } + + try { + $container->leaveScope('bar'); + $this->fail('->leaveScope() throws a \LogicException if the scope does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->leaveScope() throws a \LogicException if the scope does not exist'); + $this->assertEquals('The scope "bar" is not active.', $e->getMessage(), '->leaveScope() throws a \LogicException if the scope does not exist'); + } + } + + /** + * @expectedException \InvalidArgumentException + * @dataProvider getBuiltInScopes + */ + public function testAddScopeDoesNotAllowBuiltInScopes($scope) + { + $container = new Container(); + $container->addScope(new Scope($scope)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testAddScopeDoesNotAllowExistingScope() + { + $container = new Container(); + $container->addScope(new Scope('foo')); + $container->addScope(new Scope('foo')); + } + + /** + * @expectedException \InvalidArgumentException + * @dataProvider getInvalidParentScopes + */ + public function testAddScopeDoesNotAllowInvalidParentScope($scope) + { + $c = new Container(); + $c->addScope(new Scope('foo', $scope)); + } + + public function testAddScope() + { + $c = new Container(); + $c->addScope(new Scope('foo')); + $c->addScope(new Scope('bar', 'foo')); + + $this->assertSame(array('foo' => 'container', 'bar' => 'foo'), $this->getField($c, 'scopes')); + $this->assertSame(array('foo' => array('bar'), 'bar' => array()), $this->getField($c, 'scopeChildren')); + } + + public function testHasScope() + { + $c = new Container(); + + $this->assertFalse($c->hasScope('foo')); + $c->addScope(new Scope('foo')); + $this->assertTrue($c->hasScope('foo')); + } + + public function testIsScopeActive() + { + $c = new Container(); + + $this->assertFalse($c->isScopeActive('foo')); + $c->addScope(new Scope('foo')); + + $this->assertFalse($c->isScopeActive('foo')); + $c->enterScope('foo'); + + $this->assertTrue($c->isScopeActive('foo')); + $c->leaveScope('foo'); + + $this->assertFalse($c->isScopeActive('foo')); + } + + public function testGetThrowsException() + { + $c = new ProjectServiceContainer(); + + try { + $c->get('throw_exception'); + $this->fail(); + } catch (\Exception $e) { + $this->assertEquals('Something went terribly wrong!', $e->getMessage()); + } + + try { + $c->get('throw_exception'); + $this->fail(); + } catch (\Exception $e) { + $this->assertEquals('Something went terribly wrong!', $e->getMessage()); + } + } + + public function testGetThrowsExceptionOnServiceConfiguration() + { + $c = new ProjectServiceContainer(); + + try { + $c->get('throws_exception_on_service_configuration'); + $this->fail('The container can not contain invalid service!'); + } catch (\Exception $e) { + $this->assertEquals('Something was terribly wrong while trying to configure the service!', $e->getMessage()); + } + $this->assertFalse($c->initialized('throws_exception_on_service_configuration')); + + try { + $c->get('throws_exception_on_service_configuration'); + $this->fail('The container can not contain invalid service!'); + } catch (\Exception $e) { + $this->assertEquals('Something was terribly wrong while trying to configure the service!', $e->getMessage()); + } + $this->assertFalse($c->initialized('throws_exception_on_service_configuration')); + } + + public function getInvalidParentScopes() + { + return array( + array(ContainerInterface::SCOPE_PROTOTYPE), + array('bar'), + ); + } + + public function getBuiltInScopes() + { + return array( + array(ContainerInterface::SCOPE_CONTAINER), + array(ContainerInterface::SCOPE_PROTOTYPE), + ); + } + + protected function getField($obj, $field) + { + $reflection = new \ReflectionProperty($obj, $field); + $reflection->setAccessible(true); + + return $reflection->getValue($obj); + } +} + +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(); + } + + protected function getScopedService() + { + if (!isset($this->scopedServices['foo'])) { + throw new \RuntimeException('Invalid call'); + } + + return $this->services['scoped'] = $this->scopedServices['foo']['scoped'] = new \stdClass(); + } + + protected function getScopedFooService() + { + if (!isset($this->scopedServices['foo'])) { + throw new \RuntimeException('invalid call'); + } + + return $this->services['scoped_foo'] = $this->scopedServices['foo']['scoped_foo'] = new \stdClass(); + } + + protected function getInactiveService() + { + throw new InactiveScopeException('request', 'request'); + } + + 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..3464a6a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.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\DependencyInjection\Tests; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; + +class CrossCheckTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + protected function setUp() + { + if (!class_exists('Symfony\Component\Config\Loader\Loader')) { + $this->markTestSkipped('The "Config" component is not available'); + } + } + + 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..bf1bab1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/DefinitionDecoratorTest.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\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('factoryClass', 'factory_class'), + array('factoryMethod', 'factory_method'), + array('factoryService', 'factory_service'), + 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 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..d41c6a8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -0,0 +1,316 @@ + + * + * 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; + +class DefinitionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\DependencyInjection\Definition::__construct + */ + 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 testSetGetFactoryClass() + { + $def = new Definition('stdClass'); + $this->assertNull($def->getFactoryClass()); + $this->assertSame($def, $def->setFactoryClass('stdClass2'), "->setFactoryClass() implements a fluent interface."); + $this->assertEquals('stdClass2', $def->getFactoryClass(), "->getFactoryClass() returns current class to construct this service."); + } + + public function testSetGetFactoryMethod() + { + $def = new Definition('stdClass'); + $this->assertNull($def->getFactoryMethod()); + $this->assertSame($def, $def->setFactoryMethod('foo'), '->setFactoryMethod() implements a fluent interface'); + $this->assertEquals('foo', $def->getFactoryMethod(), '->getFactoryMethod() returns the factory method name'); + } + + public function testSetGetFactoryService() + { + $def = new Definition('stdClass'); + $this->assertNull($def->getFactoryService()); + $this->assertSame($def, $def->setFactoryService('foo.bar'), "->setFactoryService() implements a fluent interface."); + $this->assertEquals('foo.bar', $def->getFactoryService(), "->getFactoryService() returns current service to construct this service."); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::setClass + * @covers Symfony\Component\DependencyInjection\Definition::getClass + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::setArguments + * @covers Symfony\Component\DependencyInjection\Definition::getArguments + * @covers Symfony\Component\DependencyInjection\Definition::addArgument + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::setMethodCalls + * @covers Symfony\Component\DependencyInjection\Definition::addMethodCall + * @covers Symfony\Component\DependencyInjection\Definition::hasMethodCall + * @covers Symfony\Component\DependencyInjection\Definition::removeMethodCall + */ + 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(''); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::setFile + * @covers Symfony\Component\DependencyInjection\Definition::getFile + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::setScope + * @covers Symfony\Component\DependencyInjection\Definition::getScope + */ + public function testSetGetScope() + { + $def = new Definition('stdClass'); + $this->assertEquals('container', $def->getScope()); + $this->assertSame($def, $def->setScope('foo')); + $this->assertEquals('foo', $def->getScope()); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::setPublic + * @covers Symfony\Component\DependencyInjection\Definition::isPublic + */ + 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.'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::setSynthetic + * @covers Symfony\Component\DependencyInjection\Definition::isSynthetic + */ + 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.'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::setSynchronized + * @covers Symfony\Component\DependencyInjection\Definition::isSynchronized + */ + public function testSetIsSynchronized() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isSynchronized(), '->isSynchronized() returns false by default'); + $this->assertSame($def, $def->setSynchronized(true), '->setSynchronized() implements a fluent interface'); + $this->assertTrue($def->isSynchronized(), '->isSynchronized() returns true if the service is synchronized.'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::setLazy + * @covers Symfony\Component\DependencyInjection\Definition::isLazy + */ + 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.'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::setAbstract + * @covers Symfony\Component\DependencyInjection\Definition::isAbstract + */ + 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.'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::setConfigurator + * @covers Symfony\Component\DependencyInjection\Definition::getConfigurator + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::clearTags + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::clearTags + */ + 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')); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::addTag + * @covers Symfony\Component\DependencyInjection\Definition::getTag + * @covers Symfony\Component\DependencyInjection\Definition::getTags + * @covers Symfony\Component\DependencyInjection\Definition::hasTag + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Definition::replaceArgument + */ + 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()); + } +} 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..0dc1ce8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/GraphvizDumperTest.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\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'); + } +} 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..0ea42d4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.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\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')), '->dump() takes a class and a base_class options'); + + $container = new ContainerBuilder(); + new PhpDumper($container); + } + + public function testDumpFrozenContainerWithNoParameter() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->register('foo', 'stdClass'); + + $container->compile(); + + $dumper = new PhpDumper($container); + + $dumpedString = $dumper->dump(); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services11.php', $dumpedString, '->dump() does not add getDefaultParameters() method call if container have no parameters.'); + $this->assertNotRegexp("/function getDefaultParameters\(/", $dumpedString, '->dump() does not add getDefaultParameters() method definition.'); + } + + 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'); + } + + /** + * @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 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 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'); + } +} 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..9ec3438 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.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\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($container = new ContainerBuilder()); + + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/xml/services1.xml', $dumper->dump(), '->dump() dumps an empty container as an empty XML file'); + + $container = new ContainerBuilder(); + $dumper = new XmlDumper($container); + } + + 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() + { + include self::$fixturesPath.'/containers/container11.php'; + $dumper = new XmlDumper($container); + $this->assertEquals(" + + + + + + + + + + + + + +", $dumper->dump()); + } + + public function testDumpEntities() + { + include self::$fixturesPath.'/containers/container12.php'; + $dumper = new XmlDumper($container); + $this->assertEquals(" + + + + + foo<>&bar + + + +", $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..ca7aec0 --- /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; + + protected function setUp() + { + if (!class_exists('Symfony\Component\Yaml\Yaml')) { + $this->markTestSkipped('The "Yaml" component is not available'); + } + } + + 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'); + } + } +} 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..e35bbd5 --- /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..0dc8679 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container12.php @@ -0,0 +1,13 @@ + + 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..593be9c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container14.php @@ -0,0 +1,11 @@ + '%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..6abe5e2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php @@ -0,0 +1,85 @@ + + register('foo', 'FooClass')-> + addTag('foo', array('foo' => 'foo'))-> + addTag('foo', array('bar' => 'bar'))-> + setFactoryClass('FooClass')-> + setFactoryMethod('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')))-> + addMethodCall('setBar', array(new Reference('bar')))-> + addMethodCall('initialize')-> + setConfigurator('sc_configure') +; +$container-> + register('bar', 'FooClass')-> + setArguments(array('foo', new Reference('foo.baz'), new Parameter('foo_bar')))-> + setScope('container')-> + setConfigurator(array(new Reference('foo.baz'), 'configure')) +; +$container-> + register('foo.baz', '%baz_class%')-> + setFactoryClass('%baz_class%')-> + setFactoryMethod('getInstance')-> + setConfigurator(array('%baz_class%', 'configureStatic1')) +; +$container-> + register('foo_bar', '%foo_class%')-> + setScope('prototype') +; +$container->getParameterBag()->clear(); +$container->getParameterBag()->add(array( + 'baz_class' => 'BazClass', + 'foo_class' => 'FooClass', + 'foo' => 'bar', +)); +$container->setAlias('alias_for_foo', 'foo'); +$container->setAlias('alias_for_alias', 'alias_for_foo'); +$container-> + register('method_call1', '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))) +; +$container-> + register('factory_service', 'Bar')-> + setFactoryService('foo.baz')-> + setFactoryMethod('getInstance') +; + +$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) + ->setSynchronized(true) +; +$container + ->register('depends_on_request', 'stdClass') + ->addMethodCall('setRequest', array(new Reference('request', ContainerInterface::NULL_ON_INVALID_REFERENCE, false))) +; + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/interfaces1.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/interfaces1.php new file mode 100644 index 0000000..27503a3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/interfaces1.php @@ -0,0 +1,25 @@ +setParameter('cla', 'Fo'); +$container->setParameter('ss', 'Class'); + +$definition = new Definition('%cla%o%ss%'); +$container->setDefinition('foo', $definition); + +return $container; + +if (!class_exists('FooClass')) { + class FooClass + { + public $bar; + + public function setBar($bar) + { + $this->bar = $bar; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/interfaces2.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/interfaces2.php new file mode 100644 index 0000000..a851901 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/interfaces2.php @@ -0,0 +1,34 @@ +setDefinition('barFactory', $factoryDefinition); + +$definition = new Definition(); +$definition->setFactoryService('barFactory'); +$definition->setFactoryMethod('createBarClass'); +$container->setDefinition('bar', $definition); + +return $container; + +class BarClass +{ + public $foo; + + public function setBar($foo) + { + $this->foo = $foo; + } +} + +class BarClassFactory +{ + public function createBarClass() + { + return new BarClass(); + } +} 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/services9.dot b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot new file mode 100644 index 0000000..cb00664 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot @@ -0,0 +1,34 @@ +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)\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_bar [label="bar\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo_baz [label="foo.baz\nBazClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo_bar [label="foo_bar\nFooClass\n", shape=record, fillcolor="#eeeeee", style="dotted"]; + node_method_call1 [label="method_call1\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_factory_service [label="factory_service\nBar\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_depends_on_request [label="depends_on_request\nstdClass\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_depends_on_request -> node_request [label="setRequest()" 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..81ff7aa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectExtension.php @@ -0,0 +1,41 @@ +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) + { + return null; + } +} 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 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..1640020 --- /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..180bb38 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/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/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..de4b642 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php @@ -0,0 +1,28 @@ +parameters = $this->getDefaultParameters(); + + $this->services = + $this->scopedServices = + $this->scopeStacks = array(); + + $this->set('service_container', $this); + + $this->scopes = array(); + $this->scopeChildren = array(); + $this->methodMap = array( + 'test' => 'getTestService', + ); + } + + /** + * 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/services11.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php new file mode 100644 index 0000000..8985603 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php @@ -0,0 +1,51 @@ +services = + $this->scopedServices = + $this->scopeStacks = array(); + + $this->set('service_container', $this); + + $this->scopes = array(); + $this->scopeChildren = array(); + $this->methodMap = array( + 'foo' => 'getFooService', + ); + } + + /** + * Gets the 'foo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return stdClass A stdClass instance. + */ + protected function getFooService() + { + return $this->services['foo'] = new \stdClass(); + } +} 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..77712e8 --- /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..9361a0e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -0,0 +1,263 @@ +getDefaultParameters())); + $this->methodMap = array( + 'bar' => 'getBarService', + 'baz' => 'getBazService', + 'depends_on_request' => 'getDependsOnRequestService', + 'factory_service' => 'getFactoryServiceService', + 'foo' => 'getFooService', + 'foo.baz' => 'getFoo_BazService', + 'foo_bar' => 'getFooBarService', + 'foo_with_inline' => 'getFooWithInlineService', + 'inlined' => 'getInlinedService', + 'method_call1' => 'getMethodCall1Service', + 'request' => 'getRequestService', + ); + $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 FooClass A FooClass instance. + */ + protected function getBarService() + { + $this->services['bar'] = $instance = new \FooClass('foo', $this->get('foo.baz'), $this->getParameter('foo_bar')); + + $this->get('foo.baz')->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 'depends_on_request' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return stdClass A stdClass instance. + */ + protected function getDependsOnRequestService() + { + $this->services['depends_on_request'] = $instance = new \stdClass(); + + $instance->setRequest($this->get('request', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + + return $instance; + } + + /** + * 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 FooClass A FooClass instance. + */ + protected function getFooService() + { + $a = $this->get('foo.baz'); + + $this->services['foo'] = $instance = call_user_func(array('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; + 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 FooClass A FooClass instance. + */ + protected function getMethodCall1Service() + { + require_once '%path%foo.php'; + + $this->services['method_call1'] = $instance = new \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)); + } + + 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.'); + } + + /** + * Updates the 'request' service. + */ + protected function synchronizeRequestService() + { + if ($this->initialized('depends_on_request')) { + $this->get('depends_on_request')->setRequest($this->get('request', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + } + + /** + * 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 default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'baz_class' => 'BazClass', + 'foo_class' => '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..b60cbff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -0,0 +1,289 @@ +parameters = $this->getDefaultParameters(); + + $this->services = + $this->scopedServices = + $this->scopeStacks = array(); + + $this->set('service_container', $this); + + $this->scopes = array(); + $this->scopeChildren = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + 'baz' => 'getBazService', + 'depends_on_request' => 'getDependsOnRequestService', + 'factory_service' => 'getFactoryServiceService', + 'foo' => 'getFooService', + 'foo.baz' => 'getFoo_BazService', + 'foo_bar' => 'getFooBarService', + 'foo_with_inline' => 'getFooWithInlineService', + 'method_call1' => 'getMethodCall1Service', + 'request' => 'getRequestService', + ); + $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 FooClass A FooClass instance. + */ + protected function getBarService() + { + $this->services['bar'] = $instance = new \FooClass('foo', $this->get('foo.baz'), $this->getParameter('foo_bar')); + + $this->get('foo.baz')->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 'depends_on_request' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return stdClass A stdClass instance. + */ + protected function getDependsOnRequestService() + { + $this->services['depends_on_request'] = $instance = new \stdClass(); + + $instance->setRequest($this->get('request', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + + return $instance; + } + + /** + * 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 FooClass A FooClass instance. + */ + protected function getFooService() + { + $a = $this->get('foo.baz'); + + $this->services['foo'] = $instance = call_user_func(array('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; + 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 = call_user_func(array('BazClass', 'getInstance')); + + call_user_func(array('BazClass', 'configureStatic1'), $instance); + + return $instance; + } + + /** + * Gets the 'foo_bar' service. + * + * @return FooClass A FooClass instance. + */ + protected function getFooBarService() + { + return new \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 FooClass A FooClass instance. + */ + protected function getMethodCall1Service() + { + require_once '%path%foo.php'; + + $this->services['method_call1'] = $instance = new \FooClass(); + + $instance->setBar($this->get('foo')); + $instance->setBar(NULL); + + 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.'); + } + + /** + * Updates the 'request' service. + */ + protected function synchronizeRequestService() + { + if ($this->initialized('depends_on_request')) { + $this->get('depends_on_request')->setRequest($this->get('request', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + } + + /** + * {@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' => '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/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/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/services2.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services2.xml new file mode 100644 index 0000000..6e8a6ce --- /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 + + + 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..abd9fbc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + %path%/foo.php + + + foo + + + true + false + + + + + + + + + + + + + + + + + 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..c5fa5fb --- /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..cb3a1f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -0,0 +1,79 @@ + + + + BazClass + FooClass + bar + + + + + + foo + + + foo is %foo% + %foo% + + true + + bar + + + + + + + + + foo + + %foo_bar% + + + + + + + + %path%foo.php + + + + + + + + + + + + + + + + + + + + + pub + + + + + + + + + + + + + + + + + + + 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/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..8137fab --- /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, foo: { foo: foo, bar: bar } } 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/services2.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services2.yml new file mode 100644 index 0000000..3c12746 --- /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/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..18f1065 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4.yml @@ -0,0 +1,6 @@ +imports: + - { resource: services2.yml } + - { resource: services3.yml } + - { 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..7ba9453 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml @@ -0,0 +1,31 @@ +services: + foo: { class: FooClass } + baz: { class: BazClass } + scope.container: { class: FooClass, scope: container } + scope.custom: { class: FooClass, scope: custom } + scope.prototype: { class: FooClass, scope: prototype } + constructor: { class: FooClass, factory_method: getInstance } + 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 ] + method_call2: + class: FooClass + calls: + - [ setBar, [ foo, @foo, [true, false] ] ] + alias_for_foo: @foo + another_alias_for_foo: + alias: foo + public: false + factory_service: { class: BazClass, factory_method: getInstance, factory_service: baz_factory } + request: + class: Request + synthetic: true + synchronized: true + lazy: true 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..6a4c3d5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -0,0 +1,72 @@ +parameters: + baz_class: BazClass + foo_class: FooClass + foo: bar + +services: + foo: + class: FooClass + tags: + - { name: foo, foo: foo } + - { name: foo, bar: bar } + factory_class: FooClass + factory_method: getInstance + arguments: [foo, '@foo.baz', { '%foo%': 'foo is %foo%', foobar: '%foo%' }, true, '@service_container'] + properties: { foo: bar, moo: '@foo.baz' } + calls: + - [setBar, ['@bar']] + - [initialize, { }] + + configurator: sc_configure + bar: + class: FooClass + arguments: [foo, '@foo.baz', '%foo_bar%'] + configurator: ['@foo.baz', configure] + foo.baz: + class: %baz_class% + factory_class: %baz_class% + factory_method: getInstance + configurator: ['%baz_class%', configureStatic1] + foo_bar: + class: %foo_class% + scope: prototype + method_call1: + class: FooClass + file: %path%foo.php + calls: + - [setBar, ['@foo']] + - [setBar, ['@?foo2']] + - [setBar, ['@?foo3']] + - [setBar, ['@?foobaz']] + + factory_service: + class: Bar + factory_method: getInstance + factory_service: foo.baz + foo_with_inline: + class: Foo + calls: + - [setBar, ['@inlined']] + + inlined: + class: Bar + properties: { pub: pub } + calls: + - [setBaz, ['@baz']] + + baz: + class: Baz + calls: + - [setFoo, ['@foo_with_inline']] + + request: + class: Request + synthetic: true + synchronized: true + depends_on_request: + class: stdClass + calls: + - [setRequest, ['@?request']] + + 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..5fb2026 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.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\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 + * + * @covers \Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator + */ +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..6466736 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/PhpDumper/NullDumperTest.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\PhpDumper; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; + +/** + * Tests for {@see \Symfony\Component\DependencyInjection\PhpDumper\NullDumper} + * + * @author Marco Pivetta + * + * @covers \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper + */ +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..33594d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/ClosureLoaderTest.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\Tests\Loader; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; + +class ClosureLoaderTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\Config\Loader\Loader')) { + $this->markTestSkipped('The "Config" component is not available'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\Loader\ClosureLoader::supports + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Loader\ClosureLoader::load + */ + 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/IniFileLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/IniFileLoaderTest.php new file mode 100644 index 0000000..bba9c8c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/IniFileLoaderTest.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\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() + { + if (!class_exists('Symfony\Component\Config\Loader\Loader')) { + $this->markTestSkipped('The "Config" component is not available'); + } + + $this->container = new ContainerBuilder(); + $this->loader = new IniFileLoader($this->container, new FileLocator(self::$fixturesPath.'/ini')); + } + + /** + * @covers Symfony\Component\DependencyInjection\Loader\IniFileLoader::__construct + * @covers Symfony\Component\DependencyInjection\Loader\IniFileLoader::load + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Loader\IniFileLoader::__construct + * @covers Symfony\Component\DependencyInjection\Loader\IniFileLoader::load + */ + public function testExceptionIsRaisedWhenIniFileDoesNotExist() + { + try { + $this->loader->load('foo.ini'); + $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.ini" does not exist (in: ', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file does not exist'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\Loader\IniFileLoader::__construct + * @covers Symfony\Component\DependencyInjection\Loader\IniFileLoader::load + */ + public function testExceptionIsRaisedWhenIniFileCannotBeParsed() + { + try { + @$this->loader->load('nonvalid.ini'); + $this->fail('->load() throws an InvalidArgumentException if the loaded file is not parseable'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file is not parseable'); + $this->assertEquals('The "nonvalid.ini" file is not valid.', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file is not parseable'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\Loader\IniFileLoader::supports + */ + 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..3a97dc2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.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\Tests\Loader; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\Loader\Loader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\Config\FileLocator; + +class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\Config\Loader\Loader')) { + $this->markTestSkipped('The "Config" component is not available'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\Loader\PhpFileLoader::supports + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\Loader\PhpFileLoader::load + */ + 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..d8138f9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -0,0 +1,375 @@ + + * + * 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\Definition; +use Symfony\Component\Config\Loader\Loader; +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; + +class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + protected function setUp() + { + if (!class_exists('Symfony\Component\Config\Loader\Loader')) { + $this->markTestSkipped('The "Config" component is not available'); + } + } + + 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('parseFile'); + $m->setAccessible(true); + + try { + $m->invoke($loader, self::$fixturesPath.'/ini/parameters.ini'); + $this->fail('->parseFile() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->parseFile() 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(), '->parseFile() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->parseFile() 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(), '->parseFile() 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('->parseFile() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->parseFile() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'nonvalid.xml'), $e->getMessage(), '->parseFile() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->parseFile() 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(), '->parseFile() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + } + + $xml = $m->invoke($loader, self::$fixturesPath.'/xml/services1.xml'); + $this->assertEquals('Symfony\\Component\\DependencyInjection\\SimpleXMLElement', get_class($xml), '->parseFile() 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')), 'foo_bar' => new Reference('foo_bar'), 'mixedcase' => array('MixedCaseKey' => 'value')); + + $this->assertEquals($expected, $actual, '->load() converts XML values to PHP ones'); + } + + public function testLoadImports() + { + if (!class_exists('Symfony\Component\Yaml\Yaml')) { + $this->markTestSkipped('The "Yaml" component is not available'); + } + + $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(true, false), 'foo_bar' => new Reference('foo_bar'), 'mixedcase' => array('MixedCaseKey' => 'value'), '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->assertEquals(4, count($services), '->load() attributes unique ids to anonymous services'); + + // anonymous service as an argument + $args = $services['foo']->getArguments(); + $this->assertEquals(1, count($args), '->load() references anonymous services as "normal" ones'); + $this->assertEquals('Symfony\\Component\\DependencyInjection\\Reference', get_class($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->assertEquals(1, count($args), '->load() references anonymous services as "normal" ones'); + $this->assertEquals('Symfony\\Component\\DependencyInjection\\Reference', get_class($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'); + + // anonymous service as a property + $properties = $services['foo']->getProperties(); + $property = $properties['p']; + $this->assertEquals('Symfony\\Component\\DependencyInjection\\Reference', get_class($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->assertEquals('Symfony\\Component\\DependencyInjection\\Definition', get_class($services['foo']), '->load() converts element to Definition instances'); + $this->assertEquals('FooClass', $services['foo']->getClass(), '->load() parses the class attribute'); + $this->assertEquals('container', $services['scope.container']->getScope()); + $this->assertEquals('custom', $services['scope.custom']->getScope()); + $this->assertEquals('prototype', $services['scope.prototype']->getScope()); + $this->assertEquals('getInstance', $services['constructor']->getFactoryMethod(), '->load() parses the factory-method 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())), $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->assertNull($services['factory_service']->getClass()); + $this->assertEquals('getInstance', $services['factory_service']->getFactoryMethod()); + $this->assertEquals('baz_factory', $services['factory_service']->getFactoryService()); + + $this->assertTrue($services['request']->isSynthetic(), '->load() parses the synthetic flag'); + $this->assertTrue($services['request']->isSynchronized(), '->load() parses the synchronized flag'); + $this->assertTrue($services['request']->isLazy(), '->load() parses the lazy flag'); + + $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()); + } + + 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'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\Loader\XmlFileLoader::supports + */ + 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->assertEquals(2, count($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->assertEquals(4, count($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'); + } + } +} 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..e452e5d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.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\DependencyInjection\Tests\Loader; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\Config\Loader\Loader; +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; + +class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + protected function setUp() + { + if (!class_exists('Symfony\Component\Config\Loader\Loader')) { + $this->markTestSkipped('The "Config" component is not available'); + } + + if (!class_exists('Symfony\Component\Yaml\Yaml')) { + $this->markTestSkipped('The "Yaml" component is not available'); + } + } + + 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'); + } + } + } + + 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')), + $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->assertEquals('Symfony\\Component\\DependencyInjection\\Definition', get_class($services['foo']), '->load() converts service element to Definition instances'); + $this->assertEquals('FooClass', $services['foo']->getClass(), '->load() parses the class attribute'); + $this->assertEquals('container', $services['scope.container']->getScope()); + $this->assertEquals('custom', $services['scope.custom']->getScope()); + $this->assertEquals('prototype', $services['scope.prototype']->getScope()); + $this->assertEquals('getInstance', $services['constructor']->getFactoryMethod(), '->load() parses the factory_method 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())), $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('baz_factory', $services['factory_service']->getFactoryService()); + + $this->assertTrue($services['request']->isSynthetic(), '->load() parses the synthetic flag'); + $this->assertTrue($services['request']->isSynchronized(), '->load() parses the synchronized flag'); + $this->assertTrue($services['request']->isLazy(), '->load() parses the lazy flag'); + + $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()); + } + + 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'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\Loader\YamlFileLoader::supports + */ + 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->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + } + + public function testNonArrayTagThrowsException() + { + $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'); + } + } + + 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 ', $e->getMessage(), '->load() throws an InvalidArgumentException if a tag-attribute is not a scalar'); + } + } +} 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..e6e7fea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/FrozenParameterBagTest.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\Tests\ParameterBag; + +use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; + +class FrozenParameterBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag::__construct + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag::clear + * @expectedException \LogicException + */ + public function testClear() + { + $bag = new FrozenParameterBag(array()); + $bag->clear(); + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag::set + * @expectedException \LogicException + */ + public function testSet() + { + $bag = new FrozenParameterBag(array()); + $bag->set('foo', 'bar'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag::add + * @expectedException \LogicException + */ + public function testAdd() + { + $bag = new FrozenParameterBag(array()); + $bag->add(array()); + } +} 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..e1f8169 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php @@ -0,0 +1,279 @@ + + * + * 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 +{ + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\ParameterBag::__construct + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\ParameterBag::clear + */ + public function testClear() + { + $bag = new ParameterBag($parameters = array( + 'foo' => 'foo', + 'bar' => 'bar', + )); + $bag->clear(); + $this->assertEquals(array(), $bag->all(), '->clear() removes all parameters'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\ParameterBag::remove + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\ParameterBag::get + * @covers Symfony\Component\DependencyInjection\ParameterBag\ParameterBag::set + */ + 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'); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\ParameterBag::has + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\ParameterBag::resolveValue + */ + 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%')); + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\ParameterBag::resolve + */ + 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()); + } + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\ParameterBag::resolve + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\ParameterBag::escapeValue + */ + 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'); + } + + /** + * @covers Symfony\Component\DependencyInjection\ParameterBag\ParameterBag::resolve + * @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..bed188e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterTest.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\DependencyInjection\Tests; + +use Symfony\Component\DependencyInjection\Parameter; + +class ParameterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\DependencyInjection\Parameter::__construct + */ + 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..f14e99f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ReferenceTest.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\DependencyInjection\Tests; + +use Symfony\Component\DependencyInjection\Reference; + +class ReferenceTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\DependencyInjection\Reference::__construct + */ + 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..c84b8fd --- /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..2631da9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/composer.json @@ -0,0 +1,40 @@ +{ + "name": "symfony/dependency-injection", + "type": "library", + "description": "Symfony DependencyInjection Component", + "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.3" + }, + "require-dev": { + "symfony/yaml": "~2.0", + "symfony/config": "~2.2" + }, + "suggest": { + "symfony/yaml": "", + "symfony/config": "", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\DependencyInjection\\": "" } + }, + "target-dir": "Symfony/Component/DependencyInjection", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..53f5f99 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./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..2343a51 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/CHANGELOG.md @@ -0,0 +1,26 @@ +CHANGELOG +========= + +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..cc2e682 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Crawler.php @@ -0,0 +1,773 @@ + + * + * 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\CssSelector; + +/** + * Crawler eases navigation of a list of \DOMNode objects. + * + * @author Fabien Potencier + * + * @api + */ +class Crawler extends \SplObjectStorage +{ + /** + * @var string The current URI or the base href value + */ + protected $uri; + + /** + * Constructor. + * + * @param mixed $node A Node to use as the base for the crawling + * @param string $uri The current URI or the base href value + * + * @api + */ + public function __construct($node = null, $uri = null) + { + $this->uri = $uri; + + $this->add($node); + } + + /** + * Removes all the nodes. + * + * @api + */ + public function clear() + { + $this->removeAll($this); + } + + /** + * 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. + * + * @api + */ + 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. + * + * @param string $content A string to parse as HTML/XML + * @param null|string $type The content type of the string + * + * @return null|void + */ + public function addContent($content, $type = null) + { + if (empty($type)) { + $type = 'text/html'; + } + + // DOM only for HTML/XML content + if (!preg_match('/(x|ht)ml/i', $type, $matches)) { + return null; + } + + $charset = 'ISO-8859-1'; + if (false !== $pos = strpos($type, 'charset=')) { + $charset = substr($type, $pos + 8); + if (false !== $pos = strpos($charset, ';')) { + $charset = substr($charset, 0, $pos); + } + } + + if ('x' === $matches[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 + * + * @api + */ + public function addHtmlContent($content, $charset = 'UTF-8') + { + $current = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + + $dom = new \DOMDocument('1.0', $charset); + $dom->validateOnParse = true; + + if (function_exists('mb_convert_encoding') && in_array(strtolower($charset), array_map('strtolower', mb_list_encodings()))) { + $content = mb_convert_encoding($content, 'HTML-ENTITIES', $charset); + } + + @$dom->loadHTML($content); + + libxml_use_internal_errors($current); + libxml_disable_entity_loader($disableEntities); + + $this->addDocument($dom); + + $base = $this->filterXPath('descendant-or-self::base')->extract(array('href')); + + $baseHref = current($base); + if (count($base) && !empty($baseHref)) { + if ($this->uri) { + $linkNode = $dom->createElement('a'); + $linkNode->setAttribute('href', $baseHref); + $link = new Link($linkNode, $this->uri); + $this->uri = $link->getUri(); + } else { + $this->uri = $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 + * + * @api + */ + public function addXmlContent($content, $charset = 'UTF-8') + { + $current = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + + $dom = new \DOMDocument('1.0', $charset); + $dom->validateOnParse = true; + + // remove the default namespace to make XPath expressions simpler + @$dom->loadXML(str_replace('xmlns', 'ns', $content), LIBXML_NONET); + + libxml_use_internal_errors($current); + libxml_disable_entity_loader($disableEntities); + + $this->addDocument($dom); + } + + /** + * Adds a \DOMDocument to the list of nodes. + * + * @param \DOMDocument $dom A \DOMDocument instance + * + * @api + */ + 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 + * + * @api + */ + public function addNodeList(\DOMNodeList $nodes) + { + foreach ($nodes as $node) { + $this->addNode($node); + } + } + + /** + * Adds an array of \DOMNode instances to the list of nodes. + * + * @param \DOMNode[] $nodes An array of \DOMNode instances + * + * @api + */ + 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 + * + * @api + */ + public function addNode(\DOMNode $node) + { + if ($node instanceof \DOMDocument) { + $this->attach($node->documentElement); + } else { + $this->attach($node); + } + } + + /** + * Returns a node given its position in the node list. + * + * @param integer $position The position + * + * @return Crawler A new instance of the Crawler with the selected node, or an empty Crawler if it does not exist. + * + * @api + */ + public function eq($position) + { + foreach ($this as $i => $node) { + if ($i == $position) { + return new static($node, $this->uri); + } + } + + return new static(null, $this->uri); + } + + /** + * 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 + * + * @api + */ + public function each(\Closure $closure) + { + $data = array(); + foreach ($this as $i => $node) { + $data[] = $closure(new static($node, $this->uri), $i); + } + + return $data; + } + + /** + * 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. + * + * @api + */ + public function reduce(\Closure $closure) + { + $nodes = array(); + foreach ($this as $i => $node) { + if (false !== $closure(new static($node, $this->uri), $i)) { + $nodes[] = $node; + } + } + + return new static($nodes, $this->uri); + } + + /** + * Returns the first node of the current selection + * + * @return Crawler A Crawler instance with the first selected node + * + * @api + */ + 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 + * + * @api + */ + public function last() + { + return $this->eq(count($this) - 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 + * + * @api + */ + public function siblings() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return new static($this->sibling($this->getNode(0)->parentNode->firstChild), $this->uri); + } + + /** + * 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 + * + * @api + */ + public function nextAll() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return new static($this->sibling($this->getNode(0)), $this->uri); + } + + /** + * Returns the previous sibling nodes of the current selection + * + * @return Crawler A Crawler instance with the previous sibling nodes + * + * @throws \InvalidArgumentException + * + * @api + */ + public function previousAll() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return new static($this->sibling($this->getNode(0), 'previousSibling'), $this->uri); + } + + /** + * 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 + * + * @api + */ + public function parents() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + $nodes = array(); + + while ($node = $node->parentNode) { + if (1 === $node->nodeType && '_root' !== $node->nodeName) { + $nodes[] = $node; + } + } + + return new static($nodes, $this->uri); + } + + /** + * Returns the children nodes of the current selection + * + * @return Crawler A Crawler instance with the children nodes + * + * @throws \InvalidArgumentException When current node is empty + * + * @api + */ + public function children() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0)->firstChild; + + return new static($node ? $this->sibling($node) : array(), $this->uri); + } + + /** + * Returns the attribute value of the first node of the list. + * + * @param string $attribute The attribute name + * + * @return string The attribute value + * + * @throws \InvalidArgumentException When current node is empty + * + * @api + */ + public function attr($attribute) + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->getNode(0)->getAttribute($attribute); + } + + /** + * Returns the node value of the first node of the list. + * + * @return string The node value + * + * @throws \InvalidArgumentException When current node is empty + * + * @api + */ + public function text() + { + if (!count($this)) { + 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 (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $html = ''; + foreach ($this->getNode(0)->childNodes as $child) { + if (version_compare(PHP_VERSION, '5.3.6', '>=')) { + // node parameter was added to the saveHTML() method in PHP 5.3.6 + // @see http://php.net/manual/en/domdocument.savehtml.php + $html .= $child->ownerDocument->saveHTML($child); + } else { + $document = new \DOMDocument('1.0', 'UTF-8'); + $document->appendChild($document->importNode($child, true)); + $html .= rtrim($document->saveHTML()); + } + } + + 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 + * + * @api + */ + public function extract($attributes) + { + $attributes = (array) $attributes; + + $data = array(); + foreach ($this as $node) { + $elements = array(); + foreach ($attributes as $attribute) { + if ('_text' === $attribute) { + $elements[] = $node->nodeValue; + } else { + $elements[] = $node->getAttribute($attribute); + } + } + + $data[] = count($attributes) > 1 ? $elements : $elements[0]; + } + + return $data; + } + + /** + * Filters the list of nodes with an XPath expression. + * + * @param string $xpath An XPath expression + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + * + * @api + */ + public function filterXPath($xpath) + { + $document = new \DOMDocument('1.0', 'UTF-8'); + $root = $document->appendChild($document->createElement('_root')); + foreach ($this as $node) { + $root->appendChild($document->importNode($node, true)); + } + + $domxpath = new \DOMXPath($document); + + return new static($domxpath->query($xpath), $this->uri); + } + + /** + * 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 + * + * @api + */ + public function filter($selector) + { + if (!class_exists('Symfony\\Component\\CssSelector\\CssSelector')) { + // @codeCoverageIgnoreStart + throw new \RuntimeException('Unable to filter with a CSS selector as the Symfony CssSelector is not installed (you can use filterXPath instead).'); + // @codeCoverageIgnoreEnd + } + + return $this->filterXPath(CssSelector::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 + * + * @api + */ + public function selectLink($value) + { + $xpath = sprintf('//a[contains(concat(\' \', normalize-space(string(.)), \' \'), %s)] ', static::xpathLiteral(' '.$value.' ')). + sprintf('| //a/img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)]/ancestor::a', static::xpathLiteral(' '.$value.' ')); + + return $this->filterXPath($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 + * + * @api + */ + public function selectButton($value) + { + $xpath = sprintf('//input[((@type="submit" or @type="button") and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', static::xpathLiteral(' '.$value.' ')). + sprintf('or (@type="image" and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id="%s" or @name="%s"] ', static::xpathLiteral(' '.$value.' '), $value, $value). + sprintf('| //button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id="%s" or @name="%s"]', static::xpathLiteral(' '.$value.' '), $value, $value); + + return $this->filterXPath($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 + * + * @api + */ + public function link($method = 'get') + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + + return new Link($node, $this->uri, $method); + } + + /** + * Returns an array of Link objects for the nodes in the list. + * + * @return Link[] An array of Link instances + * + * @api + */ + public function links() + { + $links = array(); + foreach ($this as $node) { + $links[] = new Link($node, $this->uri, '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 + * + * @api + */ + public function form(array $values = null, $method = null) + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $form = new Form($this->getNode(0), $this->uri, $method); + + if (null !== $values) { + $form->setValues($values); + } + + return $form; + } + + /** + * 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, ', ')); + } + + protected function getNode($position) + { + foreach ($this as $i => $node) { + if ($i == $position) { + return $node; + } + // @codeCoverageIgnoreStart + } + + return null; + // @codeCoverageIgnoreEnd + } + + 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; + } +} 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..2e192cb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php @@ -0,0 +1,307 @@ + + * + * 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 + * + * @api + */ +class ChoiceFormField extends FormField +{ + /** + * @var string + */ + private $type; + /** + * @var Boolean + */ + private $multiple; + /** + * @var array + */ + private $options; + + /** + * Returns true if the field should be included in the submitted values. + * + * @return Boolean 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 Boolean + */ + 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 + * + * @api + */ + public function select($value) + { + $this->setValue($value); + } + + /** + * Ticks a checkbox. + * + * @throws \LogicException When the type provided is not correct + * + * @api + */ + 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 + * + * @api + */ + 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 \DOMNode $node A \DOMNode + * + * @throws \LogicException When choice provided is not multiple nor radio + */ + public function addChoice(\DOMNode $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->getAttribute('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 Boolean 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' != $this->node->getAttribute('type') && 'radio' != $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 = $this->node->getAttribute('type'); + $optionValue = $this->buildOptionValue($this->node); + $this->options[] = $optionValue; + + if ($this->node->getAttribute('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) { + $this->options[] = $this->buildOptionValue($option); + + if ($option->getAttribute('selected')) { + $found = true; + if ($this->multiple) { + $this->value[] = $option->getAttribute('value'); + } else { + $this->value = $option->getAttribute('value'); + } + } + } + + // if no option is selected and if it is a simple select box, take the first option as the value + $option = $this->xpath->query('descendant::option', $this->node)->item(0); + if (!$found && !$this->multiple && $option instanceof \DOMElement) { + $this->value = $option->getAttribute('value'); + } + } + } + + /** + * Returns option value with associated disabled flag + * + * @param \DOMNode $node + * + * @return array + */ + private function buildOptionValue($node) + { + $option = array(); + + $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : '1'; + $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue; + $option['disabled'] = ($node->hasAttribute('disabled') && $node->getAttribute('disabled') == 'disabled'); + + return $option; + } + + /** + * Checks whether given vale is in the existing options + * + * @param string $optionValue + * @param array $options + * + * @return bool + */ + public function containsOption($optionValue, $options) + { + 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; + } +} 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..4f82220 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/FileFormField.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\DomCrawler\Field; + +/** + * FileFormField represents a file form field (an HTML file input tag). + * + * @author Fabien Potencier + * + * @api + */ +class FileFormField extends FormField +{ + /** + * Sets the PHP error code associated with the field. + * + * @param integer $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 + * + * @api + */ + 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' != $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..6412272 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/FormField.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\DomCrawler\Field; + +/** + * FormField is the abstract class for all form fields. + * + * @author Fabien Potencier + */ +abstract class FormField +{ + /** + * @var \DOMNode + */ + protected $node; + /** + * @var string + */ + protected $name; + /** + * @var string + */ + protected $value; + /** + * @var \DOMDocument + */ + protected $document; + /** + * @var \DOMXPath + */ + protected $xpath; + /** + * @var Boolean + */ + protected $disabled; + + /** + * Constructor. + * + * @param \DOMNode $node The node associated with this field + */ + public function __construct(\DOMNode $node) + { + $this->node = $node; + $this->name = $node->getAttribute('name'); + + $this->document = new \DOMDocument('1.0', 'UTF-8'); + $this->node = $this->document->importNode($this->node, true); + + $root = $this->document->appendChild($this->document->createElement('_root')); + $root->appendChild($this->node); + $this->xpath = new \DOMXPath($this->document); + + $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 + * + * @api + */ + public function setValue($value) + { + $this->value = (string) $value; + } + + /** + * Returns true if the field should be included in the submitted values. + * + * @return Boolean 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 Boolean + */ + 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..d3d3957 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/InputFormField.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\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 + * + * @api + */ +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' == $this->node->getAttribute('type')) { + throw new \LogicException('Checkboxes should be instances of ChoiceFormField.'); + } + + if ('file' == $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..794e966 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/TextareaFormField.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\DomCrawler\Field; + +/** + * TextareaFormField represents a textarea form field (an HTML textarea tag). + * + * @author Fabien Potencier + * + * @api + */ +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 = null; + foreach ($this->node->childNodes as $node) { + $this->value .= $this->document->saveXML($node); + } + } +} 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..d30a8b1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Form.php @@ -0,0 +1,424 @@ + + * + * 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; + +/** + * Form represents an HTML form. + * + * @author Fabien Potencier + * + * @api + */ +class Form extends Link implements \ArrayAccess +{ + /** + * @var \DOMNode + */ + private $button; + + /** + * @var Field\FormField[] + */ + private $fields; + + /** + * Constructor. + * + * @param \DOMNode $node A \DOMNode 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) + * + * @throws \LogicException if the node is not a button inside a form tag + * + * @api + */ + public function __construct(\DOMNode $node, $currentUri, $method = null) + { + parent::__construct($node, $currentUri, $method); + + $this->initialize(); + } + + /** + * Gets the form node associated with this form. + * + * @return \DOMNode A \DOMNode instance + */ + public function getFormNode() + { + return $this->node; + } + + /** + * Sets the value of the fields. + * + * @param array $values An array of field values + * + * @return Form + * + * @api + */ + 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. + * + * @api + */ + 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. + * + * @api + */ + 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. + * + * @api + */ + public function getPhpValues() + { + $qs = http_build_query($this->getValues(), '', '&'); + parse_str($qs, $values); + + 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. + * + * @api + */ + public function getPhpFiles() + { + $qs = http_build_query($this->getFiles(), '', '&'); + parse_str($qs, $values); + + 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 + * + * @api + */ + public function getUri() + { + $uri = parent::getUri(); + + if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH')) && $queryString = http_build_query($this->getValues(), null, '&')) { + $sep = false === strpos($uri, '?') ? '?' : '&'; + $uri .= $sep.$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 + * + * @api + */ + 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 Boolean true if the field exists, false otherwise + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + public function get($name) + { + return $this->fields->get($name); + } + + /** + * Sets a named field. + * + * @param FormField $field The field + * + * @api + */ + public function set(FormField $field) + { + $this->fields->add($field); + } + + /** + * Gets all fields. + * + * @return FormField[] An array of fields + * + * @api + */ + public function all() + { + return $this->fields->all(); + } + + /** + * Returns true if the named field exists. + * + * @param string $name The field name + * + * @return Boolean 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); + } + + /** + * Sets the node for the form. + * + * Expects a 'submit' button \DOMNode and finds the corresponding form element. + * + * @param \DOMNode $node A \DOMNode instance + * + * @throws \LogicException If given node is not a button or input or does not have a form ancestor + */ + protected function setNode(\DOMNode $node) + { + $this->button = $node; + if ('button' == $node->nodeName || ('input' == $node->nodeName && in_array($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; + } + + private function initialize() + { + $this->fields = new FormFieldRegistry(); + + $document = new \DOMDocument('1.0', 'UTF-8'); + $node = $document->importNode($this->node, true); + $button = $document->importNode($this->button, true); + $root = $document->appendChild($document->createElement('_root')); + $root->appendChild($node); + $root->appendChild($button); + $xpath = new \DOMXPath($document); + + // add descendant elements to the form + $fieldNodes = $xpath->query('descendant::input | descendant::button | descendant::textarea | descendant::select', $root); + foreach ($fieldNodes as $node) { + $this->addField($node, $button); + } + + // find form elements corresponding to the current form by the HTML5 form attribute + if ($this->node->hasAttribute('id')) { + $formId = Crawler::xpathLiteral($this->node->getAttribute('id')); + $xpath = new \DOMXPath($this->node->ownerDocument); + $fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s]', $formId, $formId, $formId, $formId)); + foreach ($fieldNodes as $node) { + $this->addField($node, $button); + } + } + } + + private function addField(\DOMNode $node, \DOMNode $button) + { + if (!$node->hasAttribute('name') || !$node->getAttribute('name')) { + return; + } + + $nodeName = $node->nodeName; + + if ($node === $button) { + $this->set(new Field\InputFormField($node)); + } elseif ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == $node->getAttribute('type')) { + $this->set(new Field\ChoiceFormField($node)); + } elseif ('input' == $nodeName && 'radio' == $node->getAttribute('type')) { + if ($this->has($node->getAttribute('name'))) { + $this->get($node->getAttribute('name'))->addChoice($node); + } else { + $this->set(new Field\ChoiceFormField($node)); + } + } elseif ('input' == $nodeName && 'file' == $node->getAttribute('type')) { + $this->set(new Field\FileFormField($node)); + } elseif ('input' == $nodeName && !in_array($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..677a9aa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/FormFieldRegistry.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\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 Boolean 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\ChoiceFormField) { + $target->setValue($value); + } else { + $fields = self::create($name, $value); + foreach ($fields->all() as $k => $v) { + $this->set($k, $v); + } + } + } + + /** + * Returns the list of field with their value. + * + * @return array 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 array 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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Component/DomCrawler/Link.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Link.php new file mode 100644 index 0000000..293d4e0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Link.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\Component\DomCrawler; + +/** + * Link represents an HTML link (an HTML a tag). + * + * @author Fabien Potencier + * + * @api + */ +class Link +{ + /** + * @var \DOMNode A \DOMNode instance + */ + 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 \DOMNode $node A \DOMNode 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 + * + * @api + */ + public function __construct(\DOMNode $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 \DOMNode A \DOMNode instance + */ + public function getNode() + { + return $this->node; + } + + /** + * Gets the method associated with this link. + * + * @return string The method + * + * @api + */ + public function getMethod() + { + return $this->method; + } + + /** + * Gets the URI associated with this link. + * + * @return string The URI + * + * @api + */ + 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; + } + + // only an anchor + if ('#' === $uri[0]) { + $baseUri = $this->currentUri; + if (false !== $pos = strpos($baseUri, '#')) { + $baseUri = substr($baseUri, 0, $pos); + } + + return $baseUri.$uri; + } + + // only a query string + if ('?' === $uri[0]) { + $baseUri = $this->currentUri; + + // remove the query string from the current uri + if (false !== $pos = strpos($baseUri, '?')) { + $baseUri = substr($baseUri, 0, $pos); + } + + return $baseUri.$uri; + } + + // absolute URL with relative schema + if (0 === strpos($uri, '//')) { + return preg_replace('#^([^/]*)//.*$#', '$1', $this->currentUri).$uri; + } + + $baseUri = preg_replace('#^(.*?//[^/]+)(?:\/.*)?$#', '$1', $this->currentUri); + + // 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 = $path.'/'; + } + + $output = array(); + + foreach (explode('/', $path) as $segment) { + if ('..' === $segment) { + array_pop($output); + } elseif ('.' !== $segment) { + array_push($output, $segment); + } + } + + return implode('/', $output); + } + + /** + * Sets current \DOMNode instance. + * + * @param \DOMNode $node A \DOMNode instance + * + * @throws \LogicException If given node is not an anchor + */ + protected function setNode(\DOMNode $node) + { + if ('a' != $node->nodeName) { + throw new \LogicException(sprintf('Unable to click on a "%s" tag.', $node->nodeName)); + } + + $this->node = $node; + } +} 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..721c0d9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/README.md @@ -0,0 +1,32 @@ +DomCrawler Component +==================== + +DomCrawler eases DOM navigation for HTML and XML documents. + +If you are familiar with jQuery, DomCrawler is a PHP equivalent: + + 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: + + 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.phar install --dev + $ 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 100644 index 0000000..e7a3484 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -0,0 +1,675 @@ + + * + * 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'); + + $crawler = new Crawler(new \DOMNode()); + $this->assertCount(1, $crawler, '__construct() takes a node as a first argument'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::add + */ + 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'); + + 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 an \DOMNode'); + + $crawler = new Crawler(); + $crawler->add('Foo'); + $this->assertEquals('Foo', $crawler->filterXPath('//body')->text(), '->add() adds nodes from a string'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testAddInvalidNode() + { + $crawler = new Crawler(); + $crawler->add(1); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent + */ + 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'); + + $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'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent + */ + public function testAddHtmlContentCharset() + { + $crawler = new Crawler(); + $crawler->addHtmlContent('
    Tiếng Việt', 'UTF-8'); + + $this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text()); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent + */ + 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'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent + */ + public function testAddHtmlContentUnsupportedCharset() + { + $crawler = new Crawler(); + $crawler->addHtmlContent(file_get_contents(__DIR__.'/Fixtures/windows-1250.html'), 'Windows-1250'); + + $this->assertEquals('Žťčýů', $crawler->filterXPath('//p')->text()); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent + */ + public function testAddHtmlContentWithErrors() + { + 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(false); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent + */ + 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'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent + */ + public function testAddXmlContentCharset() + { + $crawler = new Crawler(); + $crawler->addXmlContent('
    Tiếng Việt
    ', 'UTF-8'); + + $this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text()); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent + */ + public function testAddXmlContentWithErrors() + { + 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(false); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addContent + */ + 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'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addDocument + */ + public function testAddDocument() + { + $crawler = new Crawler(); + $crawler->addDocument($this->createDomDocument()); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addDocument() adds nodes from a \DOMDocument'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addNodeList + */ + public function testAddNodeList() + { + $crawler = new Crawler(); + $crawler->addNodeList($this->createNodeList()); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodeList() adds nodes from a \DOMNodeList'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addNodes + */ + public function testAddNodes() + { + 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'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addNode + */ + public function testAddNode() + { + $crawler = new Crawler(); + $crawler->addNode($this->createNodeList()->item(0)); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNode() adds nodes from an \DOMNode'); + } + + public function testClear() + { + $crawler = new Crawler(new \DOMNode()); + $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 testReduce() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + $nodes = $crawler->reduce(function ($node, $i) { + return $i == 1 ? false : true; + }); + $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 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'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::filterXPath + */ + 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'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::filter + */ + public function testFilter() + { + if (!class_exists('Symfony\Component\CssSelector\CssSelector')) { + $this->markTestSkipped('The "CssSelector" component is not available'); + } + + $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 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 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'); + } + } + + 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(), '->form() takes an array of values to submit as its first argument'); + $this->assertEquals(array('FooBarName' => 'FooBarValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler2->form()->getValues(), '->form() takes an array of values to submit as its first argument'); + + 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'); + } + } + + 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'); + } + } + + public function testBaseTag() + { + $crawler = new Crawler('
    '); + $this->assertEquals('http://base.com/link', $crawler->filterXPath('//a')->link()->getUri()); + + $crawler = new Crawler('', 'https://domain.com'); + $this->assertEquals('https://base.com/link', $crawler->filterXPath('//a')->link()->getUri(), ' tag can use a schema-less url'); + + $crawler = new Crawler('', 'https://domain.com'); + $this->assertEquals('https://domain.com/path/link', $crawler->filterXPath('//a')->link()->getUri(), ' tag can set a path'); + } + + public function createTestCrawler($uri = null) + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + + + Foo + Fabien\'s Foo + Fabien"s Foo + \' Fabien"s Foo + + Bar +    Fabien\'s Bar   + Fabien"s Bar + \' Fabien"s Bar + + GetLink + +
    + + + + ', + array('bar' => array('InputFormField', 'bar')), + ), + array( + 'appends the submitted button value but not other submit buttons', + ' + ', + array('foobar' => array('InputFormField', 'foobar')), + ), + 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 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 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'), $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'); + } + + 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'); + } + + /** + * @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( + '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->assertEquals('Symfony\\Component\\DomCrawler\\Field\\InputFormField', get_class($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->assertEquals(1, count($fields), '->all() return an array of form field objects'); + $this->assertEquals('Symfony\\Component\\DomCrawler\\Field\\InputFormField', get_class($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'); + } + + /** + * @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' + ) + )); + } + + 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.''); + + $nodes = $dom->getElementsByTagName('input'); + $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); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Tests/LinkTest.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Tests/LinkTest.php new file mode 100644 index 0000000..1761470 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Tests/LinkTest.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\DomCrawler\Tests; + +use Symfony\Component\DomCrawler\Link; + +class LinkTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \LogicException + */ + public function testConstructorWithANonATag() + { + $dom = new \DOMDocument(); + $dom->loadHTML('
    '); + + new Link($dom->getElementsByTagName('div')->item(0), 'http://www.example.com/'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorWithAnInvalidCurrentUri() + { + $dom = new \DOMDocument(); + $dom->loadHTML('foo'); + + new Link($dom->getElementsByTagName('a')->item(0), 'example.com'); + } + + public function testGetNode() + { + $dom = new \DOMDocument(); + $dom->loadHTML('foo'); + + $node = $dom->getElementsByTagName('a')->item(0); + $link = new Link($node, 'http://example.com/'); + + $this->assertEquals($node, $link->getNode(), '->getNode() returns the node associated with the link'); + } + + public function testGetMethod() + { + $dom = new \DOMDocument(); + $dom->loadHTML('foo'); + + $node = $dom->getElementsByTagName('a')->item(0); + $link = new Link($node, 'http://example.com/'); + + $this->assertEquals('GET', $link->getMethod(), '->getMethod() returns the method of the link'); + + $link = new Link($node, 'http://example.com/', 'post'); + $this->assertEquals('POST', $link->getMethod(), '->getMethod() returns the method of the link'); + } + + /** + * @dataProvider getGetUriTests + */ + public function testGetUri($url, $currentUri, $expected) + { + $dom = new \DOMDocument(); + $dom->loadHTML(sprintf('foo', $url)); + $link = new Link($dom->getElementsByTagName('a')->item(0), $currentUri); + + $this->assertEquals($expected, $link->getUri()); + } + + public function getGetUriTests() + { + return array( + array('/foo', 'http://localhost/bar/foo/', 'http://localhost/foo'), + array('/foo', 'http://localhost/bar/foo', 'http://localhost/foo'), + array(' + /foo', 'http://localhost/bar/foo/', 'http://localhost/foo'), + array('/foo + ', 'http://localhost/bar/foo', 'http://localhost/foo'), + + array('foo', 'http://localhost/bar/foo/', 'http://localhost/bar/foo/foo'), + array('foo', 'http://localhost/bar/foo', 'http://localhost/bar/foo'), + + array('', 'http://localhost/bar/', 'http://localhost/bar/'), + array('#', 'http://localhost/bar/', 'http://localhost/bar/#'), + array('#bar', 'http://localhost/bar/#foo', 'http://localhost/bar/#bar'), + array('?a=b', 'http://localhost/bar/', 'http://localhost/bar/?a=b'), + + array('http://login.foo.com/foo', 'http://localhost/bar/', 'http://login.foo.com/foo'), + array('https://login.foo.com/foo', 'https://localhost/bar/', 'https://login.foo.com/foo'), + array('mailto:foo@bar.com', 'http://localhost/foo', 'mailto:foo@bar.com'), + + // tests schema relative URL (issue #7169) + array('//login.foo.com/foo', 'http://localhost/bar/', 'http://login.foo.com/foo'), + array('//login.foo.com/foo', 'https://localhost/bar/', 'https://login.foo.com/foo'), + + array('?foo=2', 'http://localhost?foo=1', 'http://localhost?foo=2'), + array('?foo=2', 'http://localhost/?foo=1', 'http://localhost/?foo=2'), + array('?foo=2', 'http://localhost/bar?foo=1', 'http://localhost/bar?foo=2'), + array('?foo=2', 'http://localhost/bar/?foo=1', 'http://localhost/bar/?foo=2'), + array('?bar=2', 'http://localhost?foo=1', 'http://localhost?bar=2'), + + array('foo', 'http://login.foo.com/bar/baz?/query/string', 'http://login.foo.com/bar/foo'), + + array('.', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/'), + array('./', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/'), + array('./foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo/bar/foo'), + array('..', 'http://localhost/foo/bar/baz', 'http://localhost/foo/'), + array('../', 'http://localhost/foo/bar/baz', 'http://localhost/foo/'), + array('../foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo/foo'), + array('../..', 'http://localhost/foo/bar/baz', 'http://localhost/'), + array('../../', 'http://localhost/foo/bar/baz', 'http://localhost/'), + array('../../foo', 'http://localhost/foo/bar/baz', 'http://localhost/foo'), + array('../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'), + array('../bar/../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'), + array('../bar/./../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'), + array('../../', 'http://localhost/', 'http://localhost/'), + array('../../', 'http://localhost', 'http://localhost/'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/composer.json b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/composer.json new file mode 100644 index 0000000..4e56c92 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/composer.json @@ -0,0 +1,37 @@ +{ + "name": "symfony/dom-crawler", + "type": "library", + "description": "Symfony DomCrawler Component", + "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.3" + }, + "require-dev": { + "symfony/css-selector": "~2.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\DomCrawler\\": "" } + }, + "target-dir": "Symfony/Component/DomCrawler", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/phpunit.xml.dist new file mode 100644 index 0000000..0f8d2da --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/CHANGELOG.md new file mode 100644 index 0000000..536c5ac --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/CHANGELOG.md @@ -0,0 +1,16 @@ +CHANGELOG +========= + +2.1.0 +----- + + * added TraceableEventDispatcherInterface + * added ContainerAwareEventDispatcher + * added a reference to the EventDispatcher on the Event + * added a reference to the Event name on the event + * added fluid interface to the dispatch() method which now returns the Event + object + * added GenericEvent event class + * added the possibility for subscribers to subscribe several times for the + same event + * added ImmutableEventDispatcher diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php new file mode 100644 index 0000000..9448ed4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php @@ -0,0 +1,202 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazily loads listeners and subscribers from the dependency injection + * container + * + * @author Fabien Potencier + * @author Bernhard Schussek + * @author Jordan Alliot + */ +class ContainerAwareEventDispatcher extends EventDispatcher +{ + /** + * The container from where services are loaded + * @var ContainerInterface + */ + private $container; + + /** + * The service IDs of the event listeners and subscribers + * @var array + */ + private $listenerIds = array(); + + /** + * The services registered as listeners + * @var array + */ + private $listeners = array(); + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Adds a service as event listener + * + * @param string $eventName Event for which the listener is added + * @param array $callback The service ID of the listener service & the method + * name that has to be called + * @param integer $priority The higher this value, the earlier an event listener + * will be triggered in the chain. + * Defaults to 0. + * + * @throws \InvalidArgumentException + */ + public function addListenerService($eventName, $callback, $priority = 0) + { + if (!is_array($callback) || 2 !== count($callback)) { + throw new \InvalidArgumentException('Expected an array("service", "method") argument'); + } + + $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); + } + + public function removeListener($eventName, $listener) + { + $this->lazyLoad($eventName); + + if (isset($this->listeners[$eventName])) { + foreach ($this->listeners[$eventName] as $key => $l) { + foreach ($this->listenerIds[$eventName] as $i => $args) { + list($serviceId, $method, $priority) = $args; + if ($key === $serviceId.'.'.$method) { + if ($listener === array($l, $method)) { + unset($this->listeners[$eventName][$key]); + if (empty($this->listeners[$eventName])) { + unset($this->listeners[$eventName]); + } + unset($this->listenerIds[$eventName][$i]); + if (empty($this->listenerIds[$eventName])) { + unset($this->listenerIds[$eventName]); + } + } + } + } + } + } + + parent::removeListener($eventName, $listener); + } + + /** + * @see EventDispatcherInterface::hasListeners + */ + public function hasListeners($eventName = null) + { + if (null === $eventName) { + return (Boolean) count($this->listenerIds) || (Boolean) count($this->listeners); + } + + if (isset($this->listenerIds[$eventName])) { + return true; + } + + return parent::hasListeners($eventName); + } + + /** + * @see EventDispatcherInterface::getListeners + */ + public function getListeners($eventName = null) + { + if (null === $eventName) { + foreach (array_keys($this->listenerIds) as $serviceEventName) { + $this->lazyLoad($serviceEventName); + } + } else { + $this->lazyLoad($eventName); + } + + return parent::getListeners($eventName); + } + + /** + * Adds a service as event subscriber + * + * @param string $serviceId The service ID of the subscriber service + * @param string $class The service's class name (which must implement EventSubscriberInterface) + */ + public function addSubscriberService($serviceId, $class) + { + foreach ($class::getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->listenerIds[$eventName][] = array($serviceId, $params, 0); + } elseif (is_string($params[0])) { + $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * {@inheritDoc} + * + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @throws \InvalidArgumentException if the service is not defined + */ + public function dispatch($eventName, Event $event = null) + { + $this->lazyLoad($eventName); + + return parent::dispatch($eventName, $event); + } + + public function getContainer() + { + return $this->container; + } + + /** + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @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. + */ + protected function lazyLoad($eventName) + { + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $args) { + list($serviceId, $method, $priority) = $args; + $listener = $this->container->get($serviceId); + + $key = $serviceId.'.'.$method; + if (!isset($this->listeners[$eventName][$key])) { + $this->addListener($eventName, array($listener, $method), $priority); + } elseif ($listener !== $this->listeners[$eventName][$key]) { + parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); + $this->addListener($eventName, array($listener, $method), $priority); + } + + $this->listeners[$eventName][$key] = $listener; + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php new file mode 100644 index 0000000..a67a979 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.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\EventDispatcher\Debug; + +/** + * @author Fabien Potencier + */ +interface TraceableEventDispatcherInterface +{ + /** + * Gets the called listeners. + * + * @return array An array of called listeners + */ + public function getCalledListeners(); + + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + */ + public function getNotCalledListeners(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Event.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Event.php new file mode 100644 index 0000000..42f09ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Event.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\EventDispatcher; + +/** + * Event 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. + * + * You can call the method stopPropagation() to abort the execution of + * further listeners in your event listener. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +class Event +{ + /** + * @var Boolean Whether no further event listeners should be triggered + */ + private $propagationStopped = false; + + /** + * @var EventDispatcher Dispatcher that dispatched this event + */ + private $dispatcher; + + /** + * @var string This event's name + */ + private $name; + + /** + * Returns whether further event listeners should be triggered. + * + * @see Event::stopPropagation + * @return Boolean Whether propagation was already stopped for this event. + * + * @api + */ + public function isPropagationStopped() + { + return $this->propagationStopped; + } + + /** + * Stops the propagation of the event to further event listeners. + * + * If multiple event listeners are connected to the same event, no + * further event listener will be triggered once any trigger calls + * stopPropagation(). + * + * @api + */ + public function stopPropagation() + { + $this->propagationStopped = true; + } + + /** + * Stores the EventDispatcher that dispatches this Event + * + * @param EventDispatcherInterface $dispatcher + * + * @api + */ + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Returns the EventDispatcher that dispatches this Event + * + * @return EventDispatcherInterface + * + * @api + */ + public function getDispatcher() + { + return $this->dispatcher; + } + + /** + * Gets the event's name. + * + * @return string + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the event's name property. + * + * @param string $name The event name. + * + * @api + */ + public function setName($name) + { + $this->name = $name; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcher.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcher.php new file mode 100644 index 0000000..eb1fb59 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcher.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\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * @author Fabien Potencier + * @author Jordi Boggiano + * @author Jordan Alliot + * + * @api + */ +class EventDispatcher implements EventDispatcherInterface +{ + private $listeners = array(); + private $sorted = array(); + + /** + * @see EventDispatcherInterface::dispatch + * + * @api + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + $event->setDispatcher($this); + $event->setName($eventName); + + if (!isset($this->listeners[$eventName])) { + return $event; + } + + $this->doDispatch($this->getListeners($eventName), $eventName, $event); + + return $event; + } + + /** + * @see EventDispatcherInterface::getListeners + */ + public function getListeners($eventName = null) + { + if (null !== $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + + return $this->sorted[$eventName]; + } + + foreach (array_keys($this->listeners) as $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + } + + return $this->sorted; + } + + /** + * @see EventDispatcherInterface::hasListeners + */ + public function hasListeners($eventName = null) + { + return (Boolean) count($this->getListeners($eventName)); + } + + /** + * @see EventDispatcherInterface::addListener + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + /** + * @see EventDispatcherInterface::removeListener + */ + public function removeListener($eventName, $listener) + { + if (!isset($this->listeners[$eventName])) { + return; + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (false !== ($key = array_search($listener, $listeners, true))) { + unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]); + } + } + } + + /** + * @see EventDispatcherInterface::addSubscriber + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->addListener($eventName, array($subscriber, $params)); + } elseif (is_string($params[0])) { + $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * @see EventDispatcherInterface::removeSubscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_array($params) && is_array($params[0])) { + foreach ($params as $listener) { + $this->removeListener($eventName, array($subscriber, $listener[0])); + } + } else { + $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0])); + } + } + } + + /** + * Triggers the listeners of an event. + * + * This method can be overridden to add functionality that is executed + * for each listener. + * + * @param array[callback] $listeners The event listeners. + * @param string $eventName The name of the event to dispatch. + * @param Event $event The event object to pass to the event handlers/listeners. + */ + protected function doDispatch($listeners, $eventName, Event $event) + { + foreach ($listeners as $listener) { + call_user_func($listener, $event); + if ($event->isPropagationStopped()) { + break; + } + } + } + + /** + * Sorts the internal list of listeners for the given event by priority. + * + * @param string $eventName The name of the event. + */ + private function sortListeners($eventName) + { + $this->sorted[$eventName] = array(); + + if (isset($this->listeners[$eventName])) { + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php new file mode 100644 index 0000000..7aead23 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.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\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Bernhard Schussek + * + * @api + */ +interface EventDispatcherInterface +{ + /** + * 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 Event $event The event to pass to the event handlers/listeners. + * If not supplied, an empty Event instance is created. + * + * @return Event + * + * @api + */ + public function dispatch($eventName, Event $event = null); + + /** + * Adds an event listener that listens on the specified events. + * + * @param string $eventName The event to listen on + * @param callable $listener The listener + * @param integer $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0); + + /** + * Adds an event subscriber. + * + * The subscriber is asked for all the events he is + * interested in and added as a listener for these events. + * + * @param EventSubscriberInterface $subscriber The subscriber. + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber); + + /** + * Removes an event listener from the specified events. + * + * @param string|array $eventName The event(s) to remove a listener from + * @param callable $listener The listener to remove + */ + public function removeListener($eventName, $listener); + + /** + * Removes an event subscriber. + * + * @param EventSubscriberInterface $subscriber The subscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber); + + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string $eventName The name of the event + * + * @return array The event listeners for the specified event, or all event listeners by event name + */ + public function getListeners($eventName = null); + + /** + * Checks whether an event has any registered listeners. + * + * @param string $eventName The name of the event + * + * @return Boolean true if the specified event has any listeners, false otherwise + */ + public function hasListeners($eventName = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php new file mode 100644 index 0000000..080f892 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.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\EventDispatcher; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +interface EventSubscriberInterface +{ + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) + * + * @return array The event names to listen to + * + * @api + */ + public static function getSubscribedEvents(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/GenericEvent.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/GenericEvent.php new file mode 100644 index 0000000..3a5efcf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/GenericEvent.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\EventDispatcher; + +/** + * Event encapsulation class. + * + * Encapsulates events thus decoupling the observer from the subject they encapsulate. + * + * @author Drak + */ +class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate +{ + /** + * Observer pattern subject. + * + * @var mixed usually object or callable + */ + protected $subject; + + /** + * Array of arguments. + * + * @var array + */ + protected $arguments; + + /** + * Encapsulate an event with $subject and $args. + * + * @param mixed $subject The subject of the event, usually an object. + * @param array $arguments Arguments to store in the event. + */ + public function __construct($subject = null, array $arguments = array()) + { + $this->subject = $subject; + $this->arguments = $arguments; + } + + /** + * Getter for subject property. + * + * @return mixed $subject The observer subject. + */ + public function getSubject() + { + return $this->subject; + } + + /** + * Get argument by key. + * + * @param string $key Key. + * + * @throws \InvalidArgumentException If key is not found. + * + * @return mixed Contents of array key. + */ + public function getArgument($key) + { + if ($this->hasArgument($key)) { + return $this->arguments[$key]; + } + + throw new \InvalidArgumentException(sprintf('%s not found in %s', $key, $this->getName())); + } + + /** + * Add argument to event. + * + * @param string $key Argument name. + * @param mixed $value Value. + * + * @return GenericEvent + */ + public function setArgument($key, $value) + { + $this->arguments[$key] = $value; + + return $this; + } + + /** + * Getter for all arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Set args property. + * + * @param array $args Arguments. + * + * @return GenericEvent + */ + public function setArguments(array $args = array()) + { + $this->arguments = $args; + + return $this; + } + + /** + * Has argument. + * + * @param string $key Key of arguments array. + * + * @return boolean + */ + public function hasArgument($key) + { + return array_key_exists($key, $this->arguments); + } + + /** + * ArrayAccess for argument getter. + * + * @param string $key Array key. + * + * @throws \InvalidArgumentException If key does not exist in $this->args. + * + * @return mixed + */ + public function offsetGet($key) + { + return $this->getArgument($key); + } + + /** + * ArrayAccess for argument setter. + * + * @param string $key Array key to set. + * @param mixed $value Value. + */ + public function offsetSet($key, $value) + { + $this->setArgument($key, $value); + } + + /** + * ArrayAccess for unset argument. + * + * @param string $key Array key. + */ + public function offsetUnset($key) + { + if ($this->hasArgument($key)) { + unset($this->arguments[$key]); + } + } + + /** + * ArrayAccess has argument. + * + * @param string $key Array key. + * + * @return boolean + */ + public function offsetExists($key) + { + return $this->hasArgument($key); + } + + /** + * IteratorAggregate for iterating over the object like an array + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->arguments); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php new file mode 100644 index 0000000..b70b81a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.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\EventDispatcher; + +/** + * A read-only proxy for an event dispatcher. + * + * @author Bernhard Schussek + */ +class ImmutableEventDispatcher implements EventDispatcherInterface +{ + /** + * The proxied dispatcher. + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * Creates an unmodifiable proxy for an event dispatcher. + * + * @param EventDispatcherInterface $dispatcher The proxied event dispatcher. + */ + public function __construct(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + return $this->dispatcher->dispatch($eventName, $event); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Component/EventDispatcher/README.md b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/README.md new file mode 100644 index 0000000..11f6b18 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/README.md @@ -0,0 +1,25 @@ +EventDispatcher Component +========================= + +EventDispatcher implements a lightweight version of the Observer design +pattern. + + use Symfony\Component\EventDispatcher\EventDispatcher; + use Symfony\Component\EventDispatcher\Event; + + $dispatcher = new EventDispatcher(); + + $dispatcher->addListener('event_name', function (Event $event) { + // ... + }); + + $dispatcher->dispatch('event_name'); + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/EventDispatcher/ + $ composer.phar install --dev + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php new file mode 100644 index 0000000..71f3ad0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php @@ -0,0 +1,257 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Scope; +use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ContainerAwareEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\DependencyInjection\Container')) { + $this->markTestSkipped('The "DependencyInjection" component is not available'); + } + } + + public function testAddAListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testAddASubscriberService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.subscriber', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testPreventDuplicateListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10); + + $dispatcher->dispatch('onEvent', $event); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testTriggerAListenerServiceOutOfScope() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $container->leaveScope('scope'); + $dispatcher->dispatch('onEvent'); + } + + public function testReEnteringAScope() + { + $event = new Event(); + + $service1 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service1 + ->expects($this->exactly(2)) + ->method('onEvent') + ->with($event) + ; + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service1, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + $dispatcher->dispatch('onEvent', $event); + + $service2 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service2 + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container->enterScope('scope'); + $container->set('service.listener', $service2, 'scope'); + + $dispatcher->dispatch('onEvent', $event); + + $container->leaveScope('scope'); + + $dispatcher->dispatch('onEvent'); + } + + public function testHasListenersOnLazyLoad() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $event->setDispatcher($dispatcher); + $event->setName('onEvent'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $this->assertTrue($dispatcher->hasListeners()); + + if ($dispatcher->hasListeners('onEvent')) { + $dispatcher->dispatch('onEvent'); + } + } + + public function testGetListenersOnLazyLoad() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $listeners = $dispatcher->getListeners(); + + $this->assertTrue(isset($listeners['onEvent'])); + + $this->assertCount(1, $dispatcher->getListeners('onEvent')); + } + + public function testRemoveAfterDispatch() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', new Event()); + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } + + public function testRemoveBeforeDispatch() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } +} + +class Service +{ + public function onEvent(Event $e) + { + } +} + +class SubscriberService implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'onEvent' => 'onEvent', + 'onEvent' => array('onEvent', 10), + 'onEvent' => array('onEvent'), + ); + } + + public function onEvent(Event $e) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php new file mode 100644 index 0000000..ad7e448 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php @@ -0,0 +1,320 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class EventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /* Some pseudo events */ + const preFoo = 'pre.foo'; + const postFoo = 'post.foo'; + const preBar = 'pre.bar'; + const postBar = 'post.bar'; + + private $dispatcher; + + private $listener; + + protected function setUp() + { + $this->dispatcher = new EventDispatcher(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->listener = null; + } + + public function testInitialState() + { + $this->assertEquals(array(), $this->dispatcher->getListeners()); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddListener() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); + $this->assertCount(2, $this->dispatcher->getListeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); + $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); + $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->addListener('post.foo', $listener4, -10); + $this->dispatcher->addListener('post.foo', $listener5); + $this->dispatcher->addListener('post.foo', $listener6, 10); + + $expected = array( + 'pre.foo' => array($listener3, $listener2, $listener1), + 'post.foo' => array($listener6, $listener5, $listener4), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners()); + } + + public function testDispatch() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->dispatcher->dispatch(self::preFoo); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertEquals('pre.foo', $event->getName()); + $this->assertSame($event, $return); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + $invoked++; + }; + $this->dispatcher->addListener('pre.foo', $listener); + $this->dispatcher->addListener('post.foo', $listener); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); + $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); + $this->dispatcher->dispatch(self::postFoo); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->dispatcher->addListener('pre.bar', $this->listener); + $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('pre.bar', $this->listener); + $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('notExists', $this->listener); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testAddSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertEquals('preFoo2', $listeners[0][1]); + } + + public function testRemoveSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testRemoveSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testRemoveSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testEventReceivesTheDispatcherInstance() + { + $test = $this; + $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { + $dispatcher = $event->getDispatcher(); + }); + $this->dispatcher->dispatch('test'); + $this->assertSame($this->dispatcher, $dispatcher); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function() {}); + $this->assertTrue($dispatcher->hasListeners('bug.62976')); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(Event $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(Event $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); + } +} + +class TestEventSubscriberWithPriorities implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'pre.foo' => array('preFoo', 10), + 'post.foo' => array('postFoo'), + ); + } +} + +class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => array( + array('preFoo1'), + array('preFoo2', 10) + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/EventTest.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/EventTest.php new file mode 100644 index 0000000..52aa9ad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/EventTest.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\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; + +/** + * Test class for Event. + */ +class EventTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\EventDispatcher\Event + */ + protected $event; + + /** + * @var \Symfony\Component\EventDispatcher\EventDispatcher + */ + protected $dispatcher; + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + $this->event = new Event; + $this->dispatcher = new EventDispatcher(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + $this->event = null; + $this->eventDispatcher = null; + } + + public function testIsPropagationStopped() + { + $this->assertFalse($this->event->isPropagationStopped()); + } + + public function testStopPropagationAndIsPropagationStopped() + { + $this->event->stopPropagation(); + $this->assertTrue($this->event->isPropagationStopped()); + } + + public function testSetDispatcher() + { + $this->event->setDispatcher($this->dispatcher); + $this->assertSame($this->dispatcher, $this->event->getDispatcher()); + } + + public function testGetDispatcher() + { + $this->assertNull($this->event->getDispatcher()); + } + + public function testGetName() + { + $this->assertNull($this->event->getName()); + } + + public function testSetName() + { + $this->event->setName('foo'); + $this->assertEquals('foo', $this->event->getName()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php new file mode 100644 index 0000000..8dd6f5b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/GenericEventTest.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\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\GenericEvent; + +/** + * Test class for Event. + */ +class GenericEventTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @var GenericEvent + */ + private $event; + + private $subject; + + /** + * Prepares the environment before running a test. + */ + protected function setUp() + { + parent::setUp(); + + $this->subject = new \StdClass(); + $this->event = new GenericEvent($this->subject, array('name' => 'Event'), 'foo'); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->subject = null; + $this->event = null; + + parent::tearDown(); + } + + public function testConstruct() + { + $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event'))); + } + + /** + * Tests Event->getArgs() + */ + public function testGetArguments() + { + // test getting all + $this->assertSame(array('name' => 'Event'), $this->event->getArguments()); + } + + public function testSetArguments() + { + $result = $this->event->setArguments(array('foo' => 'bar')); + $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event); + $this->assertSame($this->event, $result); + } + + public function testSetArgument() + { + $result = $this->event->setArgument('foo2', 'bar2'); + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + $this->assertEquals($this->event, $result); + } + + public function testGetArgument() + { + // test getting key + $this->assertEquals('Event', $this->event->getArgument('name')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetArgException() + { + $this->event->getArgument('nameNotExist'); + } + + public function testOffsetGet() + { + // test getting key + $this->assertEquals('Event', $this->event['name']); + + // test getting invalid arg + $this->setExpectedException('InvalidArgumentException'); + $this->assertFalse($this->event['nameNotExist']); + } + + public function testOffsetSet() + { + $this->event['foo2'] = 'bar2'; + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + } + + public function testOffsetUnset() + { + unset($this->event['name']); + $this->assertAttributeSame(array(), 'arguments', $this->event); + } + + public function testOffsetIsset() + { + $this->assertTrue(isset($this->event['name'])); + $this->assertFalse(isset($this->event['nameNotExist'])); + } + + public function testHasArgument() + { + $this->assertTrue($this->event->hasArgument('name')); + $this->assertFalse($this->event->hasArgument('nameNotExist')); + } + + public function testGetSubject() + { + $this->assertSame($this->subject, $this->event->getSubject()); + } + + public function testHasIterator() + { + $data = array(); + foreach ($this->event as $key => $value) { + $data[$key] = $value; + } + $this->assertEquals(array('name' => 'Event'), $data); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php new file mode 100644 index 0000000..6402f89 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.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\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author Bernhard Schussek + */ +class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $innerDispatcher; + + /** + * @var ImmutableEventDispatcher + */ + private $dispatcher; + + protected function setUp() + { + $this->innerDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher); + } + + public function testDispatchDelegates() + { + $event = new Event(); + + $this->innerDispatcher->expects($this->once()) + ->method('dispatch') + ->with('event', $event) + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->dispatch('event', $event)); + } + + public function testGetListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('getListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->getListeners('event')); + } + + public function testHasListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('hasListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->hasListeners('event')); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddListenerDisallowed() + { + $this->dispatcher->addListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveListenerDisallowed() + { + $this->dispatcher->removeListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->removeSubscriber($subscriber); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/composer.json b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/composer.json new file mode 100644 index 0000000..1db2ecf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/event-dispatcher", + "type": "library", + "description": "Symfony EventDispatcher Component", + "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.3" + }, + "require-dev": { + "symfony/dependency-injection": "~2.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" } + }, + "target-dir": "Symfony/Component/EventDispatcher", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/phpunit.xml.dist new file mode 100644 index 0000000..0c3de4f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Filesystem/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/CHANGELOG.md new file mode 100644 index 0000000..e6aee66 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/CHANGELOG.md @@ -0,0 +1,18 @@ +CHANGELOG +========= + +2.3.0 +----- + + * added the dumpFile() method to atomically write files + +2.2.0 +----- + + * added a delete option for the mirror() method + +2.1.0 +----- + + * 24eb396 : BC Break : mkdir() function now throws exception in case of failure instead of returning Boolean value + * created the component diff --git a/vendor/symfony/symfony/src/Symfony/Component/Filesystem/Exception/ExceptionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/Exception/ExceptionInterface.php new file mode 100644 index 0000000..bc9748d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/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\Filesystem\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Romain Neutron + * + * @api + */ +interface ExceptionInterface +{ + +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Filesystem/Exception/IOException.php b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/Exception/IOException.php new file mode 100644 index 0000000..5b27e66 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/Exception/IOException.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\Filesystem\Exception; + +/** + * Exception class thrown when a filesystem operation failure happens + * + * @author Romain Neutron + * + * @api + */ +class IOException extends \RuntimeException implements ExceptionInterface +{ + +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Filesystem/Filesystem.php b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/Filesystem.php new file mode 100644 index 0000000..6e015b4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/Filesystem.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\Filesystem; + +use Symfony\Component\Filesystem\Exception\IOException; + +/** + * Provides basic utility to manipulate the file system. + * + * @author Fabien Potencier + */ +class Filesystem +{ + /** + * Copies a file. + * + * This method only copies the file if the origin file is newer than the target file. + * + * By default, if the target already exists, it is not overridden. + * + * @param string $originFile The original filename + * @param string $targetFile The target filename + * @param boolean $override Whether to override an existing file or not + * + * @throws IOException When copy fails + */ + public function copy($originFile, $targetFile, $override = false) + { + if (stream_is_local($originFile) && !is_file($originFile)) { + throw new IOException(sprintf('Failed to copy %s because file not exists', $originFile)); + } + + $this->mkdir(dirname($targetFile)); + + if (!$override && is_file($targetFile)) { + $doCopy = filemtime($originFile) > filemtime($targetFile); + } else { + $doCopy = true; + } + + if ($doCopy) { + // https://bugs.php.net/bug.php?id=64634 + $source = fopen($originFile, 'r'); + $target = fopen($targetFile, 'w+'); + stream_copy_to_stream($source, $target); + fclose($source); + fclose($target); + unset($source, $target); + + if (!is_file($targetFile)) { + throw new IOException(sprintf('Failed to copy %s to %s', $originFile, $targetFile)); + } + } + } + + /** + * Creates a directory recursively. + * + * @param string|array|\Traversable $dirs The directory path + * @param integer $mode The directory mode + * + * @throws IOException On any directory creation failure + */ + public function mkdir($dirs, $mode = 0777) + { + foreach ($this->toIterator($dirs) as $dir) { + if (is_dir($dir)) { + continue; + } + + if (true !== @mkdir($dir, $mode, true)) { + throw new IOException(sprintf('Failed to create %s', $dir)); + } + } + } + + /** + * Checks the existence of files or directories. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check + * + * @return Boolean true if the file exists, false otherwise + */ + public function exists($files) + { + foreach ($this->toIterator($files) as $file) { + if (!file_exists($file)) { + return false; + } + } + + return true; + } + + /** + * Sets access and modification time of file. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create + * @param integer $time The touch time as a unix timestamp + * @param integer $atime The access time as a unix timestamp + * + * @throws IOException When touch fails + */ + public function touch($files, $time = null, $atime = null) + { + foreach ($this->toIterator($files) as $file) { + $touch = $time ? @touch($file, $time, $atime) : @touch($file); + if (true !== $touch) { + throw new IOException(sprintf('Failed to touch %s', $file)); + } + } + } + + /** + * Removes files or directories. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove + * + * @throws IOException When removal fails + */ + public function remove($files) + { + $files = iterator_to_array($this->toIterator($files)); + $files = array_reverse($files); + foreach ($files as $file) { + if (!file_exists($file) && !is_link($file)) { + continue; + } + + if (is_dir($file) && !is_link($file)) { + $this->remove(new \FilesystemIterator($file)); + + if (true !== @rmdir($file)) { + throw new IOException(sprintf('Failed to remove directory %s', $file)); + } + } else { + // https://bugs.php.net/bug.php?id=52176 + if (defined('PHP_WINDOWS_VERSION_MAJOR') && is_dir($file)) { + if (true !== @rmdir($file)) { + throw new IOException(sprintf('Failed to remove file %s', $file)); + } + } else { + if (true !== @unlink($file)) { + throw new IOException(sprintf('Failed to remove file %s', $file)); + } + } + } + } + } + + /** + * Change mode for an array of files or directories. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode + * @param integer $mode The new mode (octal) + * @param integer $umask The mode mask (octal) + * @param Boolean $recursive Whether change the mod recursively or not + * + * @throws IOException When the change fail + */ + public function chmod($files, $mode, $umask = 0000, $recursive = false) + { + foreach ($this->toIterator($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chmod(new \FilesystemIterator($file), $mode, $umask, true); + } + if (true !== @chmod($file, $mode & ~$umask)) { + throw new IOException(sprintf('Failed to chmod file %s', $file)); + } + } + } + + /** + * Change the owner of an array of files or directories + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner + * @param string $user The new owner user name + * @param Boolean $recursive Whether change the owner recursively or not + * + * @throws IOException When the change fail + */ + public function chown($files, $user, $recursive = false) + { + foreach ($this->toIterator($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chown(new \FilesystemIterator($file), $user, true); + } + if (is_link($file) && function_exists('lchown')) { + if (true !== @lchown($file, $user)) { + throw new IOException(sprintf('Failed to chown file %s', $file)); + } + } else { + if (true !== @chown($file, $user)) { + throw new IOException(sprintf('Failed to chown file %s', $file)); + } + } + } + } + + /** + * Change the group of an array of files or directories + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group + * @param string $group The group name + * @param Boolean $recursive Whether change the group recursively or not + * + * @throws IOException When the change fail + */ + public function chgrp($files, $group, $recursive = false) + { + foreach ($this->toIterator($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chgrp(new \FilesystemIterator($file), $group, true); + } + if (is_link($file) && function_exists('lchgrp')) { + if (true !== @lchgrp($file, $group)) { + throw new IOException(sprintf('Failed to chgrp file %s', $file)); + } + } else { + if (true !== @chgrp($file, $group)) { + throw new IOException(sprintf('Failed to chgrp file %s', $file)); + } + } + } + } + + /** + * Renames a file or a directory. + * + * @param string $origin The origin filename or directory + * @param string $target The new filename or directory + * @param Boolean $overwrite Whether to overwrite the target if it already exists + * + * @throws IOException When target file or directory already exists + * @throws IOException When origin cannot be renamed + */ + public function rename($origin, $target, $overwrite = false) + { + // we check that target does not exist + if (!$overwrite && is_readable($target)) { + throw new IOException(sprintf('Cannot rename because the target "%s" already exist.', $target)); + } + + if (true !== @rename($origin, $target)) { + throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target)); + } + } + + /** + * Creates a symbolic link or copy a directory. + * + * @param string $originDir The origin directory path + * @param string $targetDir The symbolic link name + * @param Boolean $copyOnWindows Whether to copy files if on Windows + * + * @throws IOException When symlink fails + */ + public function symlink($originDir, $targetDir, $copyOnWindows = false) + { + if (!function_exists('symlink') && $copyOnWindows) { + $this->mirror($originDir, $targetDir); + + return; + } + + $this->mkdir(dirname($targetDir)); + + $ok = false; + if (is_link($targetDir)) { + if (readlink($targetDir) != $originDir) { + $this->remove($targetDir); + } else { + $ok = true; + } + } + + if (!$ok) { + if (true !== @symlink($originDir, $targetDir)) { + $report = error_get_last(); + if (is_array($report)) { + if (defined('PHP_WINDOWS_VERSION_MAJOR') && false !== strpos($report['message'], 'error code(1314)')) { + throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?'); + } + } + throw new IOException(sprintf('Failed to create symbolic link from %s to %s', $originDir, $targetDir)); + } + } + } + + /** + * Given an existing path, convert it to a path relative to a given starting path + * + * @param string $endPath Absolute path of target + * @param string $startPath Absolute path where traversal begins + * + * @return string Path of target relative to starting path + */ + public function makePathRelative($endPath, $startPath) + { + // Normalize separators on windows + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $endPath = strtr($endPath, '\\', '/'); + $startPath = strtr($startPath, '\\', '/'); + } + + // Split the paths into arrays + $startPathArr = explode('/', trim($startPath, '/')); + $endPathArr = explode('/', trim($endPath, '/')); + + // Find for which directory the common path stops + $index = 0; + while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { + $index++; + } + + // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) + $depth = count($startPathArr) - $index; + + // Repeated "../" for each level need to reach the common path + $traverser = str_repeat('../', $depth); + + $endPathRemainder = implode('/', array_slice($endPathArr, $index)); + + // Construct $endPath from traversing to the common path, then to the remaining $endPath + $relativePath = $traverser.(strlen($endPathRemainder) > 0 ? $endPathRemainder.'/' : ''); + + return (strlen($relativePath) === 0) ? './' : $relativePath; + } + + /** + * Mirrors a directory to another. + * + * @param string $originDir The origin directory + * @param string $targetDir The target directory + * @param \Traversable $iterator A Traversable instance + * @param array $options An array of boolean options + * Valid options are: + * - $options['override'] Whether to override an existing file on copy or not (see copy()) + * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink()) + * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) + * + * @throws IOException When file type is unknown + */ + public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()) + { + $targetDir = rtrim($targetDir, '/\\'); + $originDir = rtrim($originDir, '/\\'); + + // Iterate in destination folder to remove obsolete entries + if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) { + $deleteIterator = $iterator; + if (null === $deleteIterator) { + $flags = \FilesystemIterator::SKIP_DOTS; + $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST); + } + foreach ($deleteIterator as $file) { + $origin = str_replace($targetDir, $originDir, $file->getPathname()); + if (!$this->exists($origin)) { + $this->remove($file); + } + } + } + + $copyOnWindows = false; + if (isset($options['copy_on_windows']) && !function_exists('symlink')) { + $copyOnWindows = $options['copy_on_windows']; + } + + if (null === $iterator) { + $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST); + } + + foreach ($iterator as $file) { + $target = str_replace($originDir, $targetDir, $file->getPathname()); + + if ($copyOnWindows) { + if (is_link($file) || is_file($file)) { + $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); + } elseif (is_dir($file)) { + $this->mkdir($target); + } else { + throw new IOException(sprintf('Unable to guess "%s" file type.', $file)); + } + } else { + if (is_link($file)) { + $this->symlink($file, $target); + } elseif (is_dir($file)) { + $this->mkdir($target); + } elseif (is_file($file)) { + $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); + } else { + throw new IOException(sprintf('Unable to guess "%s" file type.', $file)); + } + } + } + } + + /** + * Returns whether the file path is an absolute path. + * + * @param string $file A file path + * + * @return Boolean + */ + public function isAbsolutePath($file) + { + if (strspn($file, '/\\', 0, 1) + || (strlen($file) > 3 && ctype_alpha($file[0]) + && substr($file, 1, 1) === ':' + && (strspn($file, '/\\', 2, 1)) + ) + || null !== parse_url($file, PHP_URL_SCHEME) + ) { + return true; + } + + return false; + } + + /** + * @param mixed $files + * + * @return \Traversable + */ + private function toIterator($files) + { + if (!$files instanceof \Traversable) { + $files = new \ArrayObject(is_array($files) ? $files : array($files)); + } + + return $files; + } + + /** + * Atomically dumps content into a file. + * + * @param string $filename The file to be written to. + * @param string $content The data to write into the file. + * @param integer $mode The file mode (octal). + * @throws IOException If the file cannot be written to. + */ + public function dumpFile($filename, $content, $mode = 0666) + { + $dir = dirname($filename); + + if (!is_dir($dir)) { + $this->mkdir($dir); + } elseif (!is_writable($dir)) { + throw new IOException(sprintf('Unable to write in the %s directory\n', $dir)); + } + + $tmpFile = tempnam($dir, basename($filename)); + + if (false === @file_put_contents($tmpFile, $content)) { + throw new IOException(sprintf('Failed to write file "%s".', $filename)); + } + + $this->rename($tmpFile, $filename, true); + $this->chmod($filename, $mode); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Filesystem/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Component/Filesystem/README.md b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/README.md new file mode 100644 index 0000000..94ac146 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/README.md @@ -0,0 +1,45 @@ +Filesystem Component +==================== + +Filesystem provides basic utility to manipulate the file system: + +```php +copy($originFile, $targetFile, $override = false); + +$filesystem->mkdir($dirs, $mode = 0777); + +$filesystem->touch($files, $time = null, $atime = null); + +$filesystem->remove($files); + +$filesystem->chmod($files, $mode, $umask = 0000, $recursive = false); + +$filesystem->chown($files, $user, $recursive = false); + +$filesystem->chgrp($files, $group, $recursive = false); + +$filesystem->rename($origin, $target); + +$filesystem->symlink($originDir, $targetDir, $copyOnWindows = false); + +$filesystem->makePathRelative($endPath, $startPath); + +$filesystem->mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()); + +$filesystem->isAbsolutePath($file); +``` + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Filesystem/ + $ composer.phar install --dev + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php new file mode 100644 index 0000000..02969f3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -0,0 +1,982 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Tests; + +use Symfony\Component\Filesystem\Filesystem; + +/** + * Test class for Filesystem. + */ +class FilesystemTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var string $workspace + */ + private $workspace = null; + + /** + * @var \Symfony\Component\Filesystem\Filesystem $filesystem + */ + private $filesystem = null; + + private static $symlinkOnWindows = null; + + public static function setUpBeforeClass() + { + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + self::$symlinkOnWindows = true; + $originDir = tempnam(sys_get_temp_dir(), 'sl'); + $targetDir = tempnam(sys_get_temp_dir(), 'sl'); + if (true !== @symlink($originDir, $targetDir)) { + $report = error_get_last(); + if (is_array($report) && false !== strpos($report['message'], 'error code(1314)')) { + self::$symlinkOnWindows = false; + } + } + } + } + + public function setUp() + { + $this->filesystem = new Filesystem(); + $this->workspace = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.time().rand(0, 1000); + mkdir($this->workspace, 0777, true); + $this->workspace = realpath($this->workspace); + } + + public function tearDown() + { + $this->clean($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); + } + } + + public function testCopyCreatesNewFile() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testCopyFails() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + } + + public function testCopyOverridesExistingFileIfModified() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + touch($targetFilePath, time() - 1000); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + public function testCopyDoesNotOverrideExistingFileByDefault() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + + // make sure both files have the same modification time + $modificationTime = time() - 1000; + touch($sourceFilePath, $modificationTime); + touch($targetFilePath, $modificationTime); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('TARGET FILE', file_get_contents($targetFilePath)); + } + + public function testCopyOverridesExistingFileIfForced() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + + // make sure both files have the same modification time + $modificationTime = time() - 1000; + touch($sourceFilePath, $modificationTime); + touch($targetFilePath, $modificationTime); + + $this->filesystem->copy($sourceFilePath, $targetFilePath, true); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + public function testCopyCreatesTargetDirectoryIfItDoesNotExist() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFileDirectory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; + $targetFilePath = $targetFileDirectory.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertTrue(is_dir($targetFileDirectory)); + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + public function testMkdirCreatesDirectoriesRecursively() + { + $directory = $this->workspace + .DIRECTORY_SEPARATOR.'directory' + .DIRECTORY_SEPARATOR.'sub_directory'; + + $this->filesystem->mkdir($directory); + + $this->assertTrue(is_dir($directory)); + } + + public function testMkdirCreatesDirectoriesFromArray() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $directories = array( + $basePath.'1', $basePath.'2', $basePath.'3' + ); + + $this->filesystem->mkdir($directories); + + $this->assertTrue(is_dir($basePath.'1')); + $this->assertTrue(is_dir($basePath.'2')); + $this->assertTrue(is_dir($basePath.'3')); + } + + public function testMkdirCreatesDirectoriesFromTraversableObject() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $directories = new \ArrayObject(array( + $basePath.'1', $basePath.'2', $basePath.'3' + )); + + $this->filesystem->mkdir($directories); + + $this->assertTrue(is_dir($basePath.'1')); + $this->assertTrue(is_dir($basePath.'2')); + $this->assertTrue(is_dir($basePath.'3')); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testMkdirCreatesDirectoriesFails() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $dir = $basePath.'2'; + + file_put_contents($dir, ''); + + $this->filesystem->mkdir($dir); + } + + public function testTouchCreatesEmptyFile() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'1'; + + $this->filesystem->touch($file); + + $this->assertFileExists($file); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testTouchFails() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'1'.DIRECTORY_SEPARATOR.'2'; + + $this->filesystem->touch($file); + } + + public function testTouchCreatesEmptyFilesFromArray() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $files = array( + $basePath.'1', $basePath.'2', $basePath.'3' + ); + + $this->filesystem->touch($files); + + $this->assertFileExists($basePath.'1'); + $this->assertFileExists($basePath.'2'); + $this->assertFileExists($basePath.'3'); + } + + public function testTouchCreatesEmptyFilesFromTraversableObject() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $files = new \ArrayObject(array( + $basePath.'1', $basePath.'2', $basePath.'3' + )); + + $this->filesystem->touch($files); + + $this->assertFileExists($basePath.'1'); + $this->assertFileExists($basePath.'2'); + $this->assertFileExists($basePath.'3'); + } + + public function testRemoveCleansFilesAndDirectoriesIteratively() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + mkdir($basePath); + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $this->filesystem->remove($basePath); + + $this->assertTrue(!is_dir($basePath)); + } + + public function testRemoveCleansArrayOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = array( + $basePath.'dir', $basePath.'file' + ); + + $this->filesystem->remove($files); + + $this->assertTrue(!is_dir($basePath.'dir')); + $this->assertTrue(!is_file($basePath.'file')); + } + + public function testRemoveCleansTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file' + )); + + $this->filesystem->remove($files); + + $this->assertTrue(!is_dir($basePath.'dir')); + $this->assertTrue(!is_file($basePath.'file')); + } + + public function testRemoveIgnoresNonExistingFiles() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + + $files = array( + $basePath.'dir', $basePath.'file' + ); + + $this->filesystem->remove($files); + + $this->assertTrue(!is_dir($basePath.'dir')); + } + + public function testRemoveCleansInvalidLinks() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + mkdir($basePath); + mkdir($basePath.'dir'); + // create symlink to unexisting file + @symlink($basePath.'file', $basePath.'link'); + + $this->filesystem->remove($basePath); + + $this->assertTrue(!is_dir($basePath)); + } + + public function testFilesExists() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + mkdir($basePath); + touch($basePath.'file1'); + mkdir($basePath.'folder'); + + $this->assertTrue($this->filesystem->exists($basePath.'file1')); + $this->assertTrue($this->filesystem->exists($basePath.'folder')); + } + + public function testFilesExistsTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file' + )); + + $this->assertTrue($this->filesystem->exists($files)); + } + + public function testFilesNotExistsTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + touch($basePath.'file2'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file', $basePath.'file2' + )); + + unlink($basePath.'file'); + + $this->assertFalse($this->filesystem->exists($files)); + } + + public function testInvalidFileNotExists() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + $this->assertFalse($this->filesystem->exists($basePath.time())); + } + + public function testChmodChangesFileMode() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0400); + $this->filesystem->chmod($dir, 0753); + + $this->assertEquals(753, $this->getFilePermissions($dir)); + $this->assertEquals(400, $this->getFilePermissions($file)); + } + + public function testChmodWrongMod() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'file'; + touch($dir); + + $this->filesystem->chmod($dir, 'Wrongmode'); + } + + public function testChmodRecursive() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0400, 0000, true); + $this->filesystem->chmod($dir, 0753, 0000, true); + + $this->assertEquals(753, $this->getFilePermissions($dir)); + $this->assertEquals(753, $this->getFilePermissions($file)); + } + + public function testChmodAppliesUmask() + { + $this->markAsSkippedIfChmodIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0770, 0022); + $this->assertEquals(750, $this->getFilePermissions($file)); + } + + public function testChmodChangesModeOfArrayOfFiles() + { + $this->markAsSkippedIfChmodIsMissing(); + + $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $files = array($directory, $file); + + mkdir($directory); + touch($file); + + $this->filesystem->chmod($files, 0753); + + $this->assertEquals(753, $this->getFilePermissions($file)); + $this->assertEquals(753, $this->getFilePermissions($directory)); + } + + public function testChmodChangesModeOfTraversableFileObject() + { + $this->markAsSkippedIfChmodIsMissing(); + + $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $files = new \ArrayObject(array($directory, $file)); + + mkdir($directory); + touch($file); + + $this->filesystem->chmod($files, 0753); + + $this->assertEquals(753, $this->getFilePermissions($file)); + $this->assertEquals(753, $this->getFilePermissions($directory)); + } + + public function testChown() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chown($dir, $this->getFileOwner($dir)); + } + + public function testChownRecursive() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chown($dir, $this->getFileOwner($dir), true); + } + + public function testChownSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chown($link, $this->getFileOwner($link)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChownSymlinkFails() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChownFail() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999)); + } + + public function testChgrp() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chgrp($dir, $this->getFileGroup($dir)); + } + + public function testChgrpRecursive() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chgrp($dir, $this->getFileGroup($dir), true); + } + + public function testChgrpSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chgrp($link, $this->getFileGroup($link)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChgrpSymlinkFails() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChgrpFail() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chgrp($dir, 'user'.time().mt_rand(1000, 9999)); + } + + public function testRename() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + touch($file); + + $this->filesystem->rename($file, $newPath); + + $this->assertFileNotExists($file); + $this->assertFileExists($newPath); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testRenameThrowsExceptionIfTargetAlreadyExists() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + + touch($file); + touch($newPath); + + $this->filesystem->rename($file, $newPath); + } + + public function testRenameOverwritesTheTargetIfItAlreadyExists() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + + touch($file); + touch($newPath); + + $this->filesystem->rename($file, $newPath, true); + + $this->assertFileNotExists($file); + $this->assertFileExists($newPath); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testRenameThrowsExceptionOnError() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.uniqid(); + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + + $this->filesystem->rename($file, $newPath); + } + + public function testSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + $this->assertEquals($file, readlink($link)); + } + + /** + * @depends testSymlink + */ + public function testRemoveSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + $this->filesystem->remove($link); + + $this->assertTrue(!is_link($link)); + } + + public function testSymlinkIsOverwrittenIfPointsToDifferentTarget() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + symlink($this->workspace, $link); + + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + $this->assertEquals($file, readlink($link)); + } + + public function testSymlinkIsNotOverwrittenIfAlreadyCreated() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + symlink($file, $link); + + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + $this->assertEquals($file, readlink($link)); + } + + public function testSymlinkCreatesTargetDirectoryIfItDoesNotExist() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link1 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'link'; + $link2 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'subdir'.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link1); + $this->filesystem->symlink($file, $link2); + + $this->assertTrue(is_link($link1)); + $this->assertEquals($file, readlink($link1)); + $this->assertTrue(is_link($link2)); + $this->assertEquals($file, readlink($link2)); + } + + /** + * @dataProvider providePathsForMakePathRelative + */ + public function testMakePathRelative($endPath, $startPath, $expectedPath) + { + $path = $this->filesystem->makePathRelative($endPath, $startPath); + + $this->assertEquals($expectedPath, $path); + } + + /** + * @return array + */ + public function providePathsForMakePathRelative() + { + $paths = array( + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component', '../'), + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component/', '../'), + array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component', '../'), + array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component/', '../'), + array('var/lib/symfony/', 'var/lib/symfony/src/Symfony/Component', '../../../'), + array('/usr/lib/symfony/', '/var/lib/symfony/src/Symfony/Component', '../../../../../../usr/lib/symfony/'), + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/', 'src/Symfony/'), + array('/aa/bb', '/aa/bb', './'), + array('/aa/bb', '/aa/bb/', './'), + array('/aa/bb/', '/aa/bb', './'), + array('/aa/bb/', '/aa/bb/', './'), + array('/aa/bb/cc', '/aa/bb/cc/dd', '../'), + array('/aa/bb/cc', '/aa/bb/cc/dd/', '../'), + array('/aa/bb/cc/', '/aa/bb/cc/dd', '../'), + array('/aa/bb/cc/', '/aa/bb/cc/dd/', '../'), + array('/aa/bb/cc', '/aa', 'bb/cc/'), + array('/aa/bb/cc', '/aa/', 'bb/cc/'), + array('/aa/bb/cc/', '/aa', 'bb/cc/'), + array('/aa/bb/cc/', '/aa/', 'bb/cc/'), + array('/a/aab/bb', '/a/aa', '../aab/bb/'), + array('/a/aab/bb', '/a/aa/', '../aab/bb/'), + array('/a/aab/bb/', '/a/aa', '../aab/bb/'), + array('/a/aab/bb/', '/a/aa/', '../aab/bb/'), + ); + + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $paths[] = array('c:\var\lib/symfony/src/Symfony/', 'c:/var/lib/symfony/', 'src/Symfony/'); + } + + return $paths; + } + + public function testMirrorCopiesFilesAndDirectoriesRecursively() + { + $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; + $directory = $sourcePath.'directory'.DIRECTORY_SEPARATOR; + $file1 = $directory.'file1'; + $file2 = $sourcePath.'file2'; + + mkdir($sourcePath); + mkdir($directory); + file_put_contents($file1, 'FILE1'); + file_put_contents($file2, 'FILE2'); + + $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertTrue(is_dir($targetPath.'directory')); + $this->assertFileEquals($file1, $targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'); + $this->assertFileEquals($file2, $targetPath.'file2'); + + $this->filesystem->remove($file1); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => false)); + $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + + file_put_contents($file1, 'FILE1'); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + + $this->filesystem->remove($directory); + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertFalse($this->filesystem->exists($targetPath.'directory')); + $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + } + + public function testMirrorCopiesLinks() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; + + mkdir($sourcePath); + file_put_contents($sourcePath.'file1', 'FILE1'); + symlink($sourcePath.'file1', $sourcePath.'link1'); + + $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertFileEquals($sourcePath.'file1', $targetPath.DIRECTORY_SEPARATOR.'link1'); + $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); + } + + public function testMirrorCopiesLinkedDirectoryContents() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; + + mkdir($sourcePath.'nested/', 0777, true); + file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1'); + // Note: We symlink directory, not file + symlink($sourcePath.'nested', $sourcePath.'link1'); + + $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.DIRECTORY_SEPARATOR.'link1/file1.txt'); + $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); + } + + /** + * @dataProvider providePathsForIsAbsolutePath + */ + public function testIsAbsolutePath($path, $expectedResult) + { + $result = $this->filesystem->isAbsolutePath($path); + + $this->assertEquals($expectedResult, $result); + } + + /** + * @return array + */ + public function providePathsForIsAbsolutePath() + { + return array( + array('/var/lib', true), + array('c:\\\\var\\lib', true), + array('\\var\\lib', true), + array('var/lib', false), + array('../var/lib', false), + array('', false), + array(null, false) + ); + } + + public function testDumpFile() + { + $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; + + $this->filesystem->dumpFile($filename, 'bar', 0753); + + $this->assertFileExists($filename); + $this->assertSame('bar', file_get_contents($filename)); + + // skip mode check on windows + if (!defined('PHP_WINDOWS_VERSION_MAJOR')) { + $this->assertEquals(753, $this->getFilePermissions($filename)); + } + } + + public function testDumpFileOverwritesAnExistingFile() + { + $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt'; + file_put_contents($filename, 'FOO BAR'); + + $this->filesystem->dumpFile($filename, 'bar'); + + $this->assertFileExists($filename); + $this->assertSame('bar', file_get_contents($filename)); + } + + /** + * Returns file permissions as three digits (i.e. 755) + * + * @param string $filePath + * + * @return integer + */ + private function getFilePermissions($filePath) + { + return (int) substr(sprintf('%o', fileperms($filePath)), -3); + } + + private function getFileOwner($filepath) + { + $this->markAsSkippedIfPosixIsMissing(); + + $infos = stat($filepath); + if ($datas = posix_getpwuid($infos['uid'])) { + return $datas['name']; + } + } + + private function getFileGroup($filepath) + { + $this->markAsSkippedIfPosixIsMissing(); + + $infos = stat($filepath); + if ($datas = posix_getgrgid($infos['gid'])) { + return $datas['name']; + } + } + + private function markAsSkippedIfSymlinkIsMissing() + { + if (!function_exists('symlink')) { + $this->markTestSkipped('symlink is not supported'); + } + + if (defined('PHP_WINDOWS_VERSION_MAJOR') && false === self::$symlinkOnWindows) { + $this->markTestSkipped('symlink requires "Create symbolic links" privilege on windows'); + } + } + + private function markAsSkippedIfChmodIsMissing() + { + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $this->markTestSkipped('chmod is not supported on windows'); + } + } + + private function markAsSkippedIfPosixIsMissing() + { + if (defined('PHP_WINDOWS_VERSION_MAJOR') || !function_exists('posix_isatty')) { + $this->markTestSkipped('Posix is not supported'); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Filesystem/composer.json b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/composer.json new file mode 100644 index 0000000..167dd50 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/filesystem", + "type": "library", + "description": "Symfony Filesystem Component", + "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.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Filesystem\\": "" } + }, + "target-dir": "Symfony/Component/Filesystem", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Filesystem/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/phpunit.xml.dist new file mode 100644 index 0000000..ef0bf95 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Filesystem/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/AbstractAdapter.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/AbstractAdapter.php new file mode 100644 index 0000000..46473b3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/AbstractAdapter.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\Finder\Adapter; + +/** + * Interface for finder engine implementations. + * + * @author Jean-François Simon + */ +abstract class AbstractAdapter implements AdapterInterface +{ + protected $followLinks = false; + protected $mode = 0; + protected $minDepth = 0; + protected $maxDepth = PHP_INT_MAX; + protected $exclude = array(); + protected $names = array(); + protected $notNames = array(); + protected $contains = array(); + protected $notContains = array(); + protected $sizes = array(); + protected $dates = array(); + protected $filters = array(); + protected $sort = false; + protected $paths = array(); + protected $notPaths = array(); + protected $ignoreUnreadableDirs = false; + + private static $areSupported = array(); + + /** + * {@inheritDoc} + */ + public function isSupported() + { + $name = $this->getName(); + + if (!array_key_exists($name, self::$areSupported)) { + self::$areSupported[$name] = $this->canBeUsed(); + } + + return self::$areSupported[$name]; + } + + /** + * {@inheritdoc} + */ + public function setFollowLinks($followLinks) + { + $this->followLinks = $followLinks; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setMode($mode) + { + $this->mode = $mode; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDepths(array $depths) + { + $this->minDepth = 0; + $this->maxDepth = PHP_INT_MAX; + + foreach ($depths as $comparator) { + switch ($comparator->getOperator()) { + case '>': + $this->minDepth = $comparator->getTarget() + 1; + break; + case '>=': + $this->minDepth = $comparator->getTarget(); + break; + case '<': + $this->maxDepth = $comparator->getTarget() - 1; + break; + case '<=': + $this->maxDepth = $comparator->getTarget(); + break; + default: + $this->minDepth = $this->maxDepth = $comparator->getTarget(); + } + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setExclude(array $exclude) + { + $this->exclude = $exclude; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setNames(array $names) + { + $this->names = $names; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setNotNames(array $notNames) + { + $this->notNames = $notNames; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setContains(array $contains) + { + $this->contains = $contains; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setNotContains(array $notContains) + { + $this->notContains = $notContains; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setSizes(array $sizes) + { + $this->sizes = $sizes; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDates(array $dates) + { + $this->dates = $dates; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setFilters(array $filters) + { + $this->filters = $filters; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setSort($sort) + { + $this->sort = $sort; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setPath(array $paths) + { + $this->paths = $paths; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setNotPath(array $notPaths) + { + $this->notPaths = $notPaths; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function ignoreUnreadableDirs($ignore = true) + { + $this->ignoreUnreadableDirs = (Boolean) $ignore; + + return $this; + } + + /** + * Returns whether the adapter is supported in the current environment. + * + * This method should be implemented in all adapters. Do not implement + * isSupported in the adapters as the generic implementation provides a cache + * layer. + * + * @see isSupported + * + * @return Boolean Whether the adapter is supported + */ + abstract protected function canBeUsed(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php new file mode 100644 index 0000000..028a5e5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php @@ -0,0 +1,327 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Adapter; + +use Symfony\Component\Finder\Exception\AccessDeniedException; +use Symfony\Component\Finder\Iterator; +use Symfony\Component\Finder\Shell\Shell; +use Symfony\Component\Finder\Expression\Expression; +use Symfony\Component\Finder\Shell\Command; +use Symfony\Component\Finder\Iterator\SortableIterator; +use Symfony\Component\Finder\Comparator\NumberComparator; +use Symfony\Component\Finder\Comparator\DateComparator; + +/** + * Shell engine implementation using GNU find command. + * + * @author Jean-François Simon + */ +abstract class AbstractFindAdapter extends AbstractAdapter +{ + /** + * @var Shell + */ + protected $shell; + + /** + * Constructor. + */ + public function __construct() + { + $this->shell = new Shell(); + } + + /** + * {@inheritdoc} + */ + public function searchInDirectory($dir) + { + // having "/../" in path make find fail + $dir = realpath($dir); + + // searching directories containing or not containing strings leads to no result + if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode && ($this->contains || $this->notContains)) { + return new Iterator\FilePathsIterator(array(), $dir); + } + + $command = Command::create(); + $find = $this->buildFindCommand($command, $dir); + + if ($this->followLinks) { + $find->add('-follow'); + } + + $find->add('-mindepth')->add($this->minDepth + 1); + + if (PHP_INT_MAX !== $this->maxDepth) { + $find->add('-maxdepth')->add($this->maxDepth + 1); + } + + if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode) { + $find->add('-type d'); + } elseif (Iterator\FileTypeFilterIterator::ONLY_FILES === $this->mode) { + $find->add('-type f'); + } + + $this->buildNamesFiltering($find, $this->names); + $this->buildNamesFiltering($find, $this->notNames, true); + $this->buildPathsFiltering($find, $dir, $this->paths); + $this->buildPathsFiltering($find, $dir, $this->notPaths, true); + $this->buildSizesFiltering($find, $this->sizes); + $this->buildDatesFiltering($find, $this->dates); + + $useGrep = $this->shell->testCommand('grep') && $this->shell->testCommand('xargs'); + $useSort = is_int($this->sort) && $this->shell->testCommand('sort') && $this->shell->testCommand('cut'); + + if ($useGrep && ($this->contains || $this->notContains)) { + $grep = $command->ins('grep'); + $this->buildContentFiltering($grep, $this->contains); + $this->buildContentFiltering($grep, $this->notContains, true); + } + + if ($useSort) { + $this->buildSorting($command, $this->sort); + } + + $command->setErrorHandler( + $this->ignoreUnreadableDirs + // If directory is unreadable and finder is set to ignore it, `stderr` is ignored. + ? function ($stderr) { return; } + : function ($stderr) { throw new AccessDeniedException($stderr); } + ); + + $paths = $this->shell->testCommand('uniq') ? $command->add('| uniq')->execute() : array_unique($command->execute()); + $iterator = new Iterator\FilePathsIterator($paths, $dir); + + if ($this->exclude) { + $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude); + } + + if (!$useGrep && ($this->contains || $this->notContains)) { + $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains); + } + + if ($this->filters) { + $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters); + } + + if (!$useSort && $this->sort) { + $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort); + $iterator = $iteratorAggregate->getIterator(); + } + + return $iterator; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return $this->shell->testCommand('find'); + } + + /** + * @param Command $command + * @param string $dir + * + * @return Command + */ + protected function buildFindCommand(Command $command, $dir) + { + return $command + ->ins('find') + ->add('find ') + ->arg($dir) + ->add('-noleaf'); // the -noleaf option is required for filesystems that don't follow the '.' and '..' conventions + } + + /** + * @param Command $command + * @param string[] $names + * @param Boolean $not + */ + private function buildNamesFiltering(Command $command, array $names, $not = false) + { + if (0 === count($names)) { + return; + } + + $command->add($not ? '-not' : null)->cmd('('); + + foreach ($names as $i => $name) { + $expr = Expression::create($name); + + // Find does not support expandable globs ("*.{a,b}" syntax). + if ($expr->isGlob() && $expr->getGlob()->isExpandable()) { + $expr = Expression::create($expr->getGlob()->toRegex(false)); + } + + // Fixes 'not search' and 'full path matching' regex problems. + // - Jokers '.' are replaced by [^/]. + // - We add '[^/]*' before and after regex (if no ^|$ flags are present). + if ($expr->isRegex()) { + $regex = $expr->getRegex(); + $regex->prepend($regex->hasStartFlag() ? '/' : '/[^/]*') + ->setStartFlag(false) + ->setStartJoker(true) + ->replaceJokers('[^/]'); + if (!$regex->hasEndFlag() || $regex->hasEndJoker()) { + $regex->setEndJoker(false)->append('[^/]*'); + } + } + + $command + ->add($i > 0 ? '-or' : null) + ->add($expr->isRegex() + ? ($expr->isCaseSensitive() ? '-regex' : '-iregex') + : ($expr->isCaseSensitive() ? '-name' : '-iname') + ) + ->arg($expr->renderPattern()); + } + + $command->cmd(')'); + } + + /** + * @param Command $command + * @param string $dir + * @param string[] $paths + * @param Boolean $not + */ + private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false) + { + if (0 === count($paths)) { + return; + } + + $command->add($not ? '-not' : null)->cmd('('); + + foreach ($paths as $i => $path) { + $expr = Expression::create($path); + + // Find does not support expandable globs ("*.{a,b}" syntax). + if ($expr->isGlob() && $expr->getGlob()->isExpandable()) { + $expr = Expression::create($expr->getGlob()->toRegex(false)); + } + + // Fixes 'not search' regex problems. + if ($expr->isRegex()) { + $regex = $expr->getRegex(); + $regex->prepend($regex->hasStartFlag() ? $dir.DIRECTORY_SEPARATOR : '.*')->setEndJoker(!$regex->hasEndFlag()); + } else { + $expr->prepend('*')->append('*'); + } + + $command + ->add($i > 0 ? '-or' : null) + ->add($expr->isRegex() + ? ($expr->isCaseSensitive() ? '-regex' : '-iregex') + : ($expr->isCaseSensitive() ? '-path' : '-ipath') + ) + ->arg($expr->renderPattern()); + } + + $command->cmd(')'); + } + + /** + * @param Command $command + * @param NumberComparator[] $sizes + */ + private function buildSizesFiltering(Command $command, array $sizes) + { + foreach ($sizes as $i => $size) { + $command->add($i > 0 ? '-and' : null); + + switch ($size->getOperator()) { + case '<=': + $command->add('-size -'.($size->getTarget() + 1).'c'); + break; + case '>=': + $command->add('-size +'. ($size->getTarget() - 1).'c'); + break; + case '>': + $command->add('-size +'.$size->getTarget().'c'); + break; + case '!=': + $command->add('-size -'.$size->getTarget().'c'); + $command->add('-size +'.$size->getTarget().'c'); + case '<': + default: + $command->add('-size -'.$size->getTarget().'c'); + } + } + } + + /** + * @param Command $command + * @param DateComparator[] $dates + */ + private function buildDatesFiltering(Command $command, array $dates) + { + foreach ($dates as $i => $date) { + $command->add($i > 0 ? '-and' : null); + + $mins = (int) round((time()-$date->getTarget()) / 60); + + if (0 > $mins) { + // mtime is in the future + $command->add(' -mmin -0'); + // we will have no result so we don't need to continue + return; + } + + switch ($date->getOperator()) { + case '<=': + $command->add('-mmin +'.($mins - 1)); + break; + case '>=': + $command->add('-mmin -'.($mins + 1)); + break; + case '>': + $command->add('-mmin -'.$mins); + break; + case '!=': + $command->add('-mmin +'.$mins.' -or -mmin -'.$mins); + break; + case '<': + default: + $command->add('-mmin +'.$mins); + } + } + } + + /** + * @param Command $command + * @param string $sort + * + * @throws \InvalidArgumentException + */ + private function buildSorting(Command $command, $sort) + { + $this->buildFormatSorting($command, $sort); + } + + /** + * @param Command $command + * @param string $sort + */ + abstract protected function buildFormatSorting(Command $command, $sort); + + /** + * @param Command $command + * @param array $contains + * @param Boolean $not + */ + abstract protected function buildContentFiltering(Command $command, array $contains, $not = false); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/AdapterInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/AdapterInterface.php new file mode 100644 index 0000000..50c018b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/AdapterInterface.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\Finder\Adapter; + +/** + * @author Jean-François Simon + */ +interface AdapterInterface +{ + /** + * @param Boolean $followLinks + * + * @return AdapterInterface Current instance + */ + public function setFollowLinks($followLinks); + + /** + * @param integer $mode + * + * @return AdapterInterface Current instance + */ + public function setMode($mode); + + /** + * @param array $exclude + * + * @return AdapterInterface Current instance + */ + public function setExclude(array $exclude); + + /** + * @param array $depths + * + * @return AdapterInterface Current instance + */ + public function setDepths(array $depths); + + /** + * @param array $names + * + * @return AdapterInterface Current instance + */ + public function setNames(array $names); + + /** + * @param array $notNames + * + * @return AdapterInterface Current instance + */ + public function setNotNames(array $notNames); + + /** + * @param array $contains + * + * @return AdapterInterface Current instance + */ + public function setContains(array $contains); + + /** + * @param array $notContains + * + * @return AdapterInterface Current instance + */ + public function setNotContains(array $notContains); + + /** + * @param array $sizes + * + * @return AdapterInterface Current instance + */ + public function setSizes(array $sizes); + + /** + * @param array $dates + * + * @return AdapterInterface Current instance + */ + public function setDates(array $dates); + + /** + * @param array $filters + * + * @return AdapterInterface Current instance + */ + public function setFilters(array $filters); + + /** + * @param \Closure|integer $sort + * + * @return AdapterInterface Current instance + */ + public function setSort($sort); + + /** + * @param array $paths + * + * @return AdapterInterface Current instance + */ + public function setPath(array $paths); + + /** + * @param array $notPaths + * + * @return AdapterInterface Current instance + */ + public function setNotPath(array $notPaths); + + /** + * @param boolean $ignore + * + * @return AdapterInterface Current instance + */ + public function ignoreUnreadableDirs($ignore = true); + + /** + * @param string $dir + * + * @return \Iterator Result iterator + */ + public function searchInDirectory($dir); + + /** + * Tests adapter support for current platform. + * + * @return Boolean + */ + public function isSupported(); + + /** + * Returns adapter name. + * + * @return string + */ + public function getName(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/BsdFindAdapter.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/BsdFindAdapter.php new file mode 100644 index 0000000..4a25bae --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/BsdFindAdapter.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\Finder\Adapter; + +use Symfony\Component\Finder\Shell\Shell; +use Symfony\Component\Finder\Shell\Command; +use Symfony\Component\Finder\Iterator\SortableIterator; +use Symfony\Component\Finder\Expression\Expression; + +/** + * Shell engine implementation using BSD find command. + * + * @author Jean-François Simon + */ +class BsdFindAdapter extends AbstractFindAdapter +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'bsd_find'; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return in_array($this->shell->getType(), array(Shell::TYPE_BSD, Shell::TYPE_DARWIN)) && parent::canBeUsed(); + } + + /** + * {@inheritdoc} + */ + protected function buildFormatSorting(Command $command, $sort) + { + switch ($sort) { + case SortableIterator::SORT_BY_NAME: + $command->ins('sort')->add('| sort'); + + return; + case SortableIterator::SORT_BY_TYPE: + $format = '%HT'; + break; + case SortableIterator::SORT_BY_ACCESSED_TIME: + $format = '%a'; + break; + case SortableIterator::SORT_BY_CHANGED_TIME: + $format = '%c'; + break; + case SortableIterator::SORT_BY_MODIFIED_TIME: + $format = '%m'; + break; + default: + throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort)); + } + + $command + ->add('-print0 | xargs -0 stat -f') + ->arg($format.'%t%N') + ->add('| sort | cut -f 2'); + } + + /** + * {@inheritdoc} + */ + protected function buildFindCommand(Command $command, $dir) + { + parent::buildFindCommand($command, $dir)->addAtIndex('-E', 1); + + return $command; + } + + /** + * {@inheritdoc} + */ + protected function buildContentFiltering(Command $command, array $contains, $not = false) + { + foreach ($contains as $contain) { + $expr = Expression::create($contain); + + // todo: avoid forking process for each $pattern by using multiple -e options + $command + ->add('| grep -v \'^$\'') + ->add('| xargs -I{} grep -I') + ->add($expr->isCaseSensitive() ? null : '-i') + ->add($not ? '-L' : '-l') + ->add('-Ee')->arg($expr->renderPattern()) + ->add('{}') + ; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/GnuFindAdapter.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/GnuFindAdapter.php new file mode 100644 index 0000000..b72451e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/GnuFindAdapter.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\Finder\Adapter; + +use Symfony\Component\Finder\Shell\Shell; +use Symfony\Component\Finder\Shell\Command; +use Symfony\Component\Finder\Iterator\SortableIterator; +use Symfony\Component\Finder\Expression\Expression; + +/** + * Shell engine implementation using GNU find command. + * + * @author Jean-François Simon + */ +class GnuFindAdapter extends AbstractFindAdapter +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'gnu_find'; + } + + /** + * {@inheritdoc} + */ + protected function buildFormatSorting(Command $command, $sort) + { + switch ($sort) { + case SortableIterator::SORT_BY_NAME: + $command->ins('sort')->add('| sort'); + + return; + case SortableIterator::SORT_BY_TYPE: + $format = '%y'; + break; + case SortableIterator::SORT_BY_ACCESSED_TIME: + $format = '%A@'; + break; + case SortableIterator::SORT_BY_CHANGED_TIME: + $format = '%C@'; + break; + case SortableIterator::SORT_BY_MODIFIED_TIME: + $format = '%T@'; + break; + default: + throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort)); + } + + $command + ->get('find') + ->add('-printf') + ->arg($format.' %h/%f\\n') + ->add('| sort | cut') + ->arg('-d ') + ->arg('-f2-') + ; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return $this->shell->getType() === Shell::TYPE_UNIX && parent::canBeUsed(); + } + + /** + * {@inheritdoc} + */ + protected function buildFindCommand(Command $command, $dir) + { + return parent::buildFindCommand($command, $dir)->add('-regextype posix-extended'); + } + + /** + * {@inheritdoc} + */ + protected function buildContentFiltering(Command $command, array $contains, $not = false) + { + foreach ($contains as $contain) { + $expr = Expression::create($contain); + + // todo: avoid forking process for each $pattern by using multiple -e options + $command + ->add('| xargs -I{} -r grep -I') + ->add($expr->isCaseSensitive() ? null : '-i') + ->add($not ? '-L' : '-l') + ->add('-Ee')->arg($expr->renderPattern()) + ->add('{}') + ; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/PhpAdapter.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/PhpAdapter.php new file mode 100644 index 0000000..378a26a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Adapter/PhpAdapter.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\Finder\Adapter; + +use Symfony\Component\Finder\Iterator; + +/** + * PHP finder engine implementation. + * + * @author Jean-François Simon + */ +class PhpAdapter extends AbstractAdapter +{ + /** + * {@inheritdoc} + */ + public function searchInDirectory($dir) + { + $flags = \RecursiveDirectoryIterator::SKIP_DOTS; + + if ($this->followLinks) { + $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; + } + + $iterator = new \RecursiveIteratorIterator( + new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs), + \RecursiveIteratorIterator::SELF_FIRST + ); + + if ($this->minDepth > 0 || $this->maxDepth < PHP_INT_MAX) { + $iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->minDepth, $this->maxDepth); + } + + if ($this->mode) { + $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode); + } + + if ($this->exclude) { + $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude); + } + + if ($this->names || $this->notNames) { + $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames); + } + + if ($this->contains || $this->notContains) { + $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains); + } + + if ($this->sizes) { + $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes); + } + + if ($this->dates) { + $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates); + } + + if ($this->filters) { + $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters); + } + + if ($this->sort) { + $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort); + $iterator = $iteratorAggregate->getIterator(); + } + + if ($this->paths || $this->notPaths) { + $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths); + } + + return $iterator; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'php'; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/Finder/CHANGELOG.md new file mode 100644 index 0000000..7ad2308 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/CHANGELOG.md @@ -0,0 +1,30 @@ +CHANGELOG +========= + +2.3.0 +----- + + * added a way to ignore unreadable directories (via Finder::ignoreUnreadableDirs()) + * unified the way subfolders that are not executable are handled by always throwing an AccessDeniedException exception + +2.2.0 +----- + + * added Finder::path() and Finder::notPath() methods + * added finder adapters to improve performance on specific platforms + * added support for wildcard characters (glob patterns) in the paths passed + to Finder::in() + +2.1.0 +----- + + * added Finder::sortByAccessedTime(), Finder::sortByChangedTime(), and + Finder::sortByModifiedTime() + * added Countable to Finder + * added support for an array of directories as an argument to + Finder::exclude() + * added searching based on the file content via Finder::contains() and + Finder::notContains() + * added support for the != operator in the Comparator + * [BC BREAK] filter expressions (used for file name and content) are no more + considered as regexps but glob patterns when they are enclosed in '*' or '?' diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Comparator/Comparator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Comparator/Comparator.php new file mode 100644 index 0000000..83c511a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Comparator/Comparator.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\Finder\Comparator; + +/** + * Comparator. + * + * @author Fabien Potencier + */ +class Comparator +{ + private $target; + private $operator = '=='; + + /** + * Gets the target value. + * + * @return string The target value + */ + public function getTarget() + { + return $this->target; + } + + /** + * Sets the target value. + * + * @param string $target The target value + */ + public function setTarget($target) + { + $this->target = $target; + } + + /** + * Gets the comparison operator. + * + * @return string The operator + */ + public function getOperator() + { + return $this->operator; + } + + /** + * Sets the comparison operator. + * + * @param string $operator A valid operator + * + * @throws \InvalidArgumentException + */ + public function setOperator($operator) + { + if (!$operator) { + $operator = '=='; + } + + if (!in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) { + throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator)); + } + + $this->operator = $operator; + } + + /** + * Tests against the target. + * + * @param mixed $test A test value + * + * @return Boolean + */ + public function test($test) + { + switch ($this->operator) { + case '>': + return $test > $this->target; + case '>=': + return $test >= $this->target; + case '<': + return $test < $this->target; + case '<=': + return $test <= $this->target; + case '!=': + return $test != $this->target; + } + + return $test == $this->target; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Comparator/DateComparator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Comparator/DateComparator.php new file mode 100644 index 0000000..9de444f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Comparator/DateComparator.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\Finder\Comparator; + +/** + * DateCompare compiles date comparisons. + * + * @author Fabien Potencier + */ +class DateComparator extends Comparator +{ + + /** + * Constructor. + * + * @param string $test A comparison string + * + * @throws \InvalidArgumentException If the test is not understood + */ + public function __construct($test) + { + if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) { + throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test)); + } + + try { + $date = new \DateTime($matches[2]); + $target = $date->format('U'); + } catch (\Exception $e) { + throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2])); + } + + $operator = isset($matches[1]) ? $matches[1] : '=='; + if ('since' === $operator || 'after' === $operator) { + $operator = '>'; + } + + if ('until' === $operator || 'before' === $operator) { + $operator = '<'; + } + + $this->setOperator($operator); + $this->setTarget($target); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Comparator/NumberComparator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Comparator/NumberComparator.php new file mode 100644 index 0000000..955cc67 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Comparator/NumberComparator.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\Finder\Comparator; + +/** + * NumberComparator compiles a simple comparison to an anonymous + * subroutine, which you can call with a value to be tested again. + * + * Now this would be very pointless, if NumberCompare didn't understand + * magnitudes. + * + * The target value may use magnitudes of kilobytes (k, ki), + * megabytes (m, mi), or gigabytes (g, gi). Those suffixed + * with an i use the appropriate 2**n version in accordance with the + * IEC standard: http://physics.nist.gov/cuu/Units/binary.html + * + * Based on the Perl Number::Compare module. + * + * @author Fabien Potencier PHP port + * @author Richard Clamp Perl version + * + * @copyright 2004-2005 Fabien Potencier + * @copyright 2002 Richard Clamp + * + * @see http://physics.nist.gov/cuu/Units/binary.html + */ +class NumberComparator extends Comparator +{ + /** + * Constructor. + * + * @param string $test A comparison string + * + * @throws \InvalidArgumentException If the test is not understood + */ + public function __construct($test) + { + if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) { + throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test)); + } + + $target = $matches[2]; + if (!is_numeric($target)) { + throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target)); + } + if (isset($matches[3])) { + // magnitude + switch (strtolower($matches[3])) { + case 'k': + $target *= 1000; + break; + case 'ki': + $target *= 1024; + break; + case 'm': + $target *= 1000000; + break; + case 'mi': + $target *= 1024*1024; + break; + case 'g': + $target *= 1000000000; + break; + case 'gi': + $target *= 1024*1024*1024; + break; + } + } + + $this->setTarget($target); + $this->setOperator(isset($matches[1]) ? $matches[1] : '=='); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/AccessDeniedException.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/AccessDeniedException.php new file mode 100644 index 0000000..ee195ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/AccessDeniedException.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\Finder\Exception; + +/** + * @author Jean-François Simon + */ +class AccessDeniedException extends \UnexpectedValueException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/AdapterFailureException.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/AdapterFailureException.php new file mode 100644 index 0000000..15fa221 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/AdapterFailureException.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\Finder\Exception; + +use Symfony\Component\Finder\Adapter\AdapterInterface; + +/** + * Base exception for all adapter failures. + * + * @author Jean-François Simon + */ +class AdapterFailureException extends \RuntimeException implements ExceptionInterface +{ + /** + * @var \Symfony\Component\Finder\Adapter\AdapterInterface + */ + private $adapter; + + /** + * @param AdapterInterface $adapter + * @param string|null $message + * @param \Exception|null $previous + */ + public function __construct(AdapterInterface $adapter, $message = null, \Exception $previous = null) + { + $this->adapter = $adapter; + parent::__construct($message ?: 'Search failed with "'.$adapter->getName().'" adapter.', $previous); + } + + /** + * {@inheritdoc} + */ + public function getAdapter() + { + return $this->adapter; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/ExceptionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/ExceptionInterface.php new file mode 100644 index 0000000..bff0214 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/ExceptionInterface.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\Finder\Exception; + +/** + * @author Jean-François Simon + */ +interface ExceptionInterface +{ + /** + * @return \Symfony\Component\Finder\Adapter\AdapterInterface + */ + public function getAdapter(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/OperationNotPermitedException.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/OperationNotPermitedException.php new file mode 100644 index 0000000..3663112 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/OperationNotPermitedException.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\Finder\Exception; + +/** + * @author Jean-François Simon + */ +class OperationNotPermitedException extends AdapterFailureException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/ShellCommandFailureException.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/ShellCommandFailureException.php new file mode 100644 index 0000000..2658f6a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Exception/ShellCommandFailureException.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\Finder\Exception; + +use Symfony\Component\Finder\Adapter\AdapterInterface; +use Symfony\Component\Finder\Shell\Command; + +/** + * @author Jean-François Simon + */ +class ShellCommandFailureException extends AdapterFailureException +{ + /** + * @var Command + */ + private $command; + + /** + * @param AdapterInterface $adapter + * @param Command $command + * @param \Exception|null $previous + */ + public function __construct(AdapterInterface $adapter, Command $command, \Exception $previous = null) + { + $this->command = $command; + parent::__construct($adapter, 'Shell command failed: "'.$command->join().'".', $previous); + } + + /** + * @return Command + */ + public function getCommand() + { + return $this->command; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/Expression.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/Expression.php new file mode 100644 index 0000000..ceedbc1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/Expression.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\Finder\Expression; + +/** + * @author Jean-François Simon + */ +class Expression implements ValueInterface +{ + const TYPE_REGEX = 1; + const TYPE_GLOB = 2; + + /** + * @var ValueInterface + */ + private $value; + + /** + * @param string $expr + * + * @return Expression + */ + public static function create($expr) + { + return new self($expr); + } + + /** + * @param string $expr + */ + public function __construct($expr) + { + try { + $this->value = Regex::create($expr); + } catch (\InvalidArgumentException $e) { + $this->value = new Glob($expr); + } + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(); + } + + /** + * {@inheritdoc} + */ + public function render() + { + return $this->value->render(); + } + + /** + * {@inheritdoc} + */ + public function renderPattern() + { + return $this->value->renderPattern(); + } + + /** + * @return bool + */ + public function isCaseSensitive() + { + return $this->value->isCaseSensitive(); + } + + /** + * @return int + */ + public function getType() + { + return $this->value->getType(); + } + + /** + * {@inheritdoc} + */ + public function prepend($expr) + { + $this->value->prepend($expr); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function append($expr) + { + $this->value->append($expr); + + return $this; + } + + /** + * @return bool + */ + public function isRegex() + { + return self::TYPE_REGEX === $this->value->getType(); + } + + /** + * @return bool + */ + public function isGlob() + { + return self::TYPE_GLOB === $this->value->getType(); + } + + /** + * @throws \LogicException + * + * @return Glob + */ + public function getGlob() + { + if (self::TYPE_GLOB !== $this->value->getType()) { + throw new \LogicException('Regex cant be transformed to glob.'); + } + + return $this->value; + } + + /** + * @return Regex + */ + public function getRegex() + { + return self::TYPE_REGEX === $this->value->getType() ? $this->value : $this->value->toRegex(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/Glob.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/Glob.php new file mode 100644 index 0000000..3023cee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/Glob.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\Finder\Expression; + +/** + * @author Jean-François Simon + */ +class Glob implements ValueInterface +{ + /** + * @var string + */ + private $pattern; + + /** + * @param string $pattern + */ + public function __construct($pattern) + { + $this->pattern = $pattern; + } + + /** + * {@inheritdoc} + */ + public function render() + { + return $this->pattern; + } + + /** + * {@inheritdoc} + */ + public function renderPattern() + { + return $this->pattern; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return Expression::TYPE_GLOB; + } + + /** + * {@inheritdoc} + */ + public function isCaseSensitive() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function prepend($expr) + { + $this->pattern = $expr.$this->pattern; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function append($expr) + { + $this->pattern .= $expr; + + return $this; + } + + /** + * Tests if glob is expandable ("*.{a,b}" syntax). + * + * @return bool + */ + public function isExpandable() + { + return false !== strpos($this->pattern, '{') + && false !== strpos($this->pattern, '}'); + } + + /** + * @param bool $strictLeadingDot + * @param bool $strictWildcardSlash + * + * @return Regex + */ + public function toRegex($strictLeadingDot = true, $strictWildcardSlash = true) + { + $firstByte = true; + $escaping = false; + $inCurlies = 0; + $regex = ''; + $sizeGlob = strlen($this->pattern); + for ($i = 0; $i < $sizeGlob; $i++) { + $car = $this->pattern[$i]; + if ($firstByte) { + if ($strictLeadingDot && '.' !== $car) { + $regex .= '(?=[^\.])'; + } + + $firstByte = false; + } + + if ('/' === $car) { + $firstByte = true; + } + + if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) { + $regex .= "\\$car"; + } elseif ('*' === $car) { + $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*'); + } elseif ('?' === $car) { + $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.'); + } elseif ('{' === $car) { + $regex .= $escaping ? '\\{' : '('; + if (!$escaping) { + ++$inCurlies; + } + } elseif ('}' === $car && $inCurlies) { + $regex .= $escaping ? '}' : ')'; + if (!$escaping) { + --$inCurlies; + } + } elseif (',' === $car && $inCurlies) { + $regex .= $escaping ? ',' : '|'; + } elseif ('\\' === $car) { + if ($escaping) { + $regex .= '\\\\'; + $escaping = false; + } else { + $escaping = true; + } + + continue; + } else { + $regex .= $car; + } + $escaping = false; + } + + return new Regex('^'.$regex.'$'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/Regex.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/Regex.php new file mode 100644 index 0000000..05544d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/Regex.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\Finder\Expression; + +/** + * @author Jean-François Simon + */ +class Regex implements ValueInterface +{ + const START_FLAG = '^'; + const END_FLAG = '$'; + const BOUNDARY = '~'; + const JOKER = '.*'; + const ESCAPING = '\\'; + + /** + * @var string + */ + private $pattern; + + /** + * @var array + */ + private $options; + + /** + * @var bool + */ + private $startFlag; + + /** + * @var bool + */ + private $endFlag; + + /** + * @var bool + */ + private $startJoker; + + /** + * @var bool + */ + private $endJoker; + + /** + * @param string $expr + * + * @return Regex + * + * @throws \InvalidArgumentException + */ + public static function create($expr) + { + if (preg_match('/^(.{3,}?)([imsxuADU]*)$/', $expr, $m)) { + $start = substr($m[1], 0, 1); + $end = substr($m[1], -1); + + if (($start === $end && !preg_match('/[*?[:alnum:] \\\\]/', $start)) || ($start === '{' && $end === '}')) { + return new self(substr($m[1], 1, -1), $m[2], $end); + } + } + + throw new \InvalidArgumentException('Given expression is not a regex.'); + } + + /** + * @param string $pattern + * @param string $options + * @param string $delimiter + */ + public function __construct($pattern, $options = '', $delimiter = null) + { + if (null !== $delimiter) { + // removes delimiter escaping + $pattern = str_replace('\\'.$delimiter, $delimiter, $pattern); + } + + $this->parsePattern($pattern); + $this->options = $options; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(); + } + + /** + * {@inheritdoc} + */ + public function render() + { + return self::BOUNDARY + .$this->renderPattern() + .self::BOUNDARY + .$this->options; + } + + /** + * {@inheritdoc} + */ + public function renderPattern() + { + return ($this->startFlag ? self::START_FLAG : '') + .($this->startJoker ? self::JOKER : '') + .str_replace(self::BOUNDARY, '\\'.self::BOUNDARY, $this->pattern) + .($this->endJoker ? self::JOKER : '') + .($this->endFlag ? self::END_FLAG : ''); + } + + /** + * {@inheritdoc} + */ + public function isCaseSensitive() + { + return !$this->hasOption('i'); + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return Expression::TYPE_REGEX; + } + + /** + * {@inheritdoc} + */ + public function prepend($expr) + { + $this->pattern = $expr.$this->pattern; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function append($expr) + { + $this->pattern .= $expr; + + return $this; + } + + /** + * @param string $option + * + * @return bool + */ + public function hasOption($option) + { + return false !== strpos($this->options, $option); + } + + /** + * @param string $option + * + * @return Regex + */ + public function addOption($option) + { + if (!$this->hasOption($option)) { + $this->options.= $option; + } + + return $this; + } + + /** + * @param string $option + * + * @return Regex + */ + public function removeOption($option) + { + $this->options = str_replace($option, '', $this->options); + + return $this; + } + + /** + * @param bool $startFlag + * + * @return Regex + */ + public function setStartFlag($startFlag) + { + $this->startFlag = $startFlag; + + return $this; + } + + /** + * @return bool + */ + public function hasStartFlag() + { + return $this->startFlag; + } + + /** + * @param bool $endFlag + * + * @return Regex + */ + public function setEndFlag($endFlag) + { + $this->endFlag = (bool) $endFlag; + + return $this; + } + + /** + * @return bool + */ + public function hasEndFlag() + { + return $this->endFlag; + } + + /** + * @param bool $startJoker + * + * @return Regex + */ + public function setStartJoker($startJoker) + { + $this->startJoker = $startJoker; + + return $this; + } + + /** + * @return bool + */ + public function hasStartJoker() + { + return $this->startJoker; + } + + /** + * @param bool $endJoker + * + * @return Regex + */ + public function setEndJoker($endJoker) + { + $this->endJoker = (bool) $endJoker; + + return $this; + } + + /** + * @return bool + */ + public function hasEndJoker() + { + return $this->endJoker; + } + + /** + * @param array $replacement + * + * @return Regex + */ + public function replaceJokers($replacement) + { + $replace = function ($subject) use ($replacement) { + $subject = $subject[0]; + $replace = 0 === substr_count($subject, '\\') % 2; + + return $replace ? str_replace('.', $replacement, $subject) : $subject; + }; + + $this->pattern = preg_replace_callback('~[\\\\]*\\.~', $replace, $this->pattern); + + return $this; + } + + /** + * @param string $pattern + */ + private function parsePattern($pattern) + { + if ($this->startFlag = self::START_FLAG === substr($pattern, 0, 1)) { + $pattern = substr($pattern, 1); + } + + if ($this->startJoker = self::JOKER === substr($pattern, 0, 2)) { + $pattern = substr($pattern, 2); + } + + if ($this->endFlag = (self::END_FLAG === substr($pattern, -1) && self::ESCAPING !== substr($pattern, -2, -1))) { + $pattern = substr($pattern, 0, -1); + } + + if ($this->endJoker = (self::JOKER === substr($pattern, -2) && self::ESCAPING !== substr($pattern, -3, -2))) { + $pattern = substr($pattern, 0, -2); + } + + $this->pattern = $pattern; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/ValueInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/ValueInterface.php new file mode 100644 index 0000000..34ce0e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Expression/ValueInterface.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\Finder\Expression; + +/** + * @author Jean-François Simon + */ +interface ValueInterface +{ + /** + * Renders string representation of expression. + * + * @return string + */ + public function render(); + + /** + * Renders string representation of pattern. + * + * @return string + */ + public function renderPattern(); + + /** + * Returns value case sensitivity. + * + * @return bool + */ + public function isCaseSensitive(); + + /** + * Returns expression type. + * + * @return int + */ + public function getType(); + + /** + * @param string $expr + * + * @return ValueInterface + */ + public function prepend($expr); + + /** + * @param string $expr + * + * @return ValueInterface + */ + public function append($expr); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Finder.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Finder.php new file mode 100644 index 0000000..43a8643 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Finder.php @@ -0,0 +1,829 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder; + +use Symfony\Component\Finder\Adapter\AdapterInterface; +use Symfony\Component\Finder\Adapter\GnuFindAdapter; +use Symfony\Component\Finder\Adapter\BsdFindAdapter; +use Symfony\Component\Finder\Adapter\PhpAdapter; +use Symfony\Component\Finder\Exception\ExceptionInterface; + +/** + * Finder allows to build rules to find files and directories. + * + * It is a thin wrapper around several specialized iterator classes. + * + * All rules may be invoked several times. + * + * All methods return the current Finder object to allow easy chaining: + * + * $finder = Finder::create()->files()->name('*.php')->in(__DIR__); + * + * @author Fabien Potencier + * + * @api + */ +class Finder implements \IteratorAggregate, \Countable +{ + const IGNORE_VCS_FILES = 1; + const IGNORE_DOT_FILES = 2; + + private $mode = 0; + private $names = array(); + private $notNames = array(); + private $exclude = array(); + private $filters = array(); + private $depths = array(); + private $sizes = array(); + private $followLinks = false; + private $sort = false; + private $ignore = 0; + private $dirs = array(); + private $dates = array(); + private $iterators = array(); + private $contains = array(); + private $notContains = array(); + private $adapters = array(); + private $paths = array(); + private $notPaths = array(); + private $ignoreUnreadableDirs = false; + + private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'); + + /** + * Constructor. + */ + public function __construct() + { + $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES; + + $this + ->addAdapter(new GnuFindAdapter()) + ->addAdapter(new BsdFindAdapter()) + ->addAdapter(new PhpAdapter(), -50) + ->setAdapter('php') + ; + } + + /** + * Creates a new Finder. + * + * @return Finder A new Finder instance + * + * @api + */ + public static function create() + { + return new static(); + } + + /** + * Registers a finder engine implementation. + * + * @param AdapterInterface $adapter An adapter instance + * @param integer $priority Highest is selected first + * + * @return Finder The current Finder instance + */ + public function addAdapter(Adapter\AdapterInterface $adapter, $priority = 0) + { + $this->adapters[$adapter->getName()] = array( + 'adapter' => $adapter, + 'priority' => $priority, + 'selected' => false, + ); + + return $this->sortAdapters(); + } + + /** + * Sets the selected adapter to the best one according to the current platform the code is run on. + * + * @return Finder The current Finder instance + */ + public function useBestAdapter() + { + $this->resetAdapterSelection(); + + return $this->sortAdapters(); + } + + /** + * Selects the adapter to use. + * + * @param string $name + * + * @throws \InvalidArgumentException + * + * @return Finder The current Finder instance + */ + public function setAdapter($name) + { + if (!isset($this->adapters[$name])) { + throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name)); + } + + $this->resetAdapterSelection(); + $this->adapters[$name]['selected'] = true; + + return $this->sortAdapters(); + } + + /** + * Removes all adapters registered in the finder. + * + * @return Finder The current Finder instance + */ + public function removeAdapters() + { + $this->adapters = array(); + + return $this; + } + + /** + * Returns registered adapters ordered by priority without extra information. + * + * @return AdapterInterface[] + */ + public function getAdapters() + { + return array_values(array_map(function(array $adapter) { + return $adapter['adapter']; + }, $this->adapters)); + } + + /** + * Restricts the matching to directories only. + * + * @return Finder The current Finder instance + * + * @api + */ + public function directories() + { + $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES; + + return $this; + } + + /** + * Restricts the matching to files only. + * + * @return Finder The current Finder instance + * + * @api + */ + public function files() + { + $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES; + + return $this; + } + + /** + * Adds tests for the directory depth. + * + * Usage: + * + * $finder->depth('> 1') // the Finder will start matching at level 1. + * $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point. + * + * @param int $level The depth level expression + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\DepthRangeFilterIterator + * @see Symfony\Component\Finder\Comparator\NumberComparator + * + * @api + */ + public function depth($level) + { + $this->depths[] = new Comparator\NumberComparator($level); + + return $this; + } + + /** + * Adds tests for file dates (last modified). + * + * The date must be something that strtotime() is able to parse: + * + * $finder->date('since yesterday'); + * $finder->date('until 2 days ago'); + * $finder->date('> now - 2 hours'); + * $finder->date('>= 2005-10-15'); + * + * @param string $date A date rage string + * + * @return Finder The current Finder instance + * + * @see strtotime + * @see Symfony\Component\Finder\Iterator\DateRangeFilterIterator + * @see Symfony\Component\Finder\Comparator\DateComparator + * + * @api + */ + public function date($date) + { + $this->dates[] = new Comparator\DateComparator($date); + + return $this; + } + + /** + * Adds rules that files must match. + * + * You can use patterns (delimited with / sign), globs or simple strings. + * + * $finder->name('*.php') + * $finder->name('/\.php$/') // same as above + * $finder->name('test.php') + * + * @param string $pattern A pattern (a regexp, a glob, or a string) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator + * + * @api + */ + public function name($pattern) + { + $this->names[] = $pattern; + + return $this; + } + + /** + * Adds rules that files must not match. + * + * @param string $pattern A pattern (a regexp, a glob, or a string) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator + * + * @api + */ + public function notName($pattern) + { + $this->notNames[] = $pattern; + + return $this; + } + + /** + * Adds tests that file contents must match. + * + * Strings or PCRE patterns can be used: + * + * $finder->contains('Lorem ipsum') + * $finder->contains('/Lorem ipsum/i') + * + * @param string $pattern A pattern (string or regexp) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilecontentFilterIterator + */ + public function contains($pattern) + { + $this->contains[] = $pattern; + + return $this; + } + + /** + * Adds tests that file contents must not match. + * + * Strings or PCRE patterns can be used: + * + * $finder->notContains('Lorem ipsum') + * $finder->notContains('/Lorem ipsum/i') + * + * @param string $pattern A pattern (string or regexp) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilecontentFilterIterator + */ + public function notContains($pattern) + { + $this->notContains[] = $pattern; + + return $this; + } + + /** + * Adds rules that filenames must match. + * + * You can use patterns (delimited with / sign) or simple strings. + * + * $finder->path('some/special/dir') + * $finder->path('/some\/special\/dir/') // same as above + * + * Use only / as dirname separator. + * + * @param string $pattern A pattern (a regexp or a string) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator + */ + public function path($pattern) + { + $this->paths[] = $pattern; + + return $this; + } + + /** + * Adds rules that filenames must not match. + * + * You can use patterns (delimited with / sign) or simple strings. + * + * $finder->notPath('some/special/dir') + * $finder->notPath('/some\/special\/dir/') // same as above + * + * Use only / as dirname separator. + * + * @param string $pattern A pattern (a regexp or a string) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator + */ + public function notPath($pattern) + { + $this->notPaths[] = $pattern; + + return $this; + } + + /** + * Adds tests for file sizes. + * + * $finder->size('> 10K'); + * $finder->size('<= 1Ki'); + * $finder->size(4); + * + * @param string $size A size range string + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SizeRangeFilterIterator + * @see Symfony\Component\Finder\Comparator\NumberComparator + * + * @api + */ + public function size($size) + { + $this->sizes[] = new Comparator\NumberComparator($size); + + return $this; + } + + /** + * Excludes directories. + * + * @param string|array $dirs A directory path or an array of directories + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator + * + * @api + */ + public function exclude($dirs) + { + $this->exclude = array_merge($this->exclude, (array) $dirs); + + return $this; + } + + /** + * Excludes "hidden" directories and files (starting with a dot). + * + * @param Boolean $ignoreDotFiles Whether to exclude "hidden" files or not + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator + * + * @api + */ + public function ignoreDotFiles($ignoreDotFiles) + { + if ($ignoreDotFiles) { + $this->ignore = $this->ignore | static::IGNORE_DOT_FILES; + } else { + $this->ignore = $this->ignore & ~static::IGNORE_DOT_FILES; + } + + return $this; + } + + /** + * Forces the finder to ignore version control directories. + * + * @param Boolean $ignoreVCS Whether to exclude VCS files or not + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator + * + * @api + */ + public function ignoreVCS($ignoreVCS) + { + if ($ignoreVCS) { + $this->ignore = $this->ignore | static::IGNORE_VCS_FILES; + } else { + $this->ignore = $this->ignore & ~static::IGNORE_VCS_FILES; + } + + return $this; + } + + /** + * Adds VCS patterns. + * + * @see ignoreVCS + * + * @param string|string[] $pattern VCS patterns to ignore + */ + public static function addVCSPattern($pattern) + { + foreach ((array) $pattern as $p) { + self::$vcsPatterns[] = $p; + } + + self::$vcsPatterns = array_unique(self::$vcsPatterns); + } + + /** + * Sorts files and directories by an anonymous function. + * + * The anonymous function receives two \SplFileInfo instances to compare. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @param \Closure $closure An anonymous function + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sort(\Closure $closure) + { + $this->sort = $closure; + + return $this; + } + + /** + * Sorts files and directories by name. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sortByName() + { + $this->sort = Iterator\SortableIterator::SORT_BY_NAME; + + return $this; + } + + /** + * Sorts files and directories by type (directories before files), then by name. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sortByType() + { + $this->sort = Iterator\SortableIterator::SORT_BY_TYPE; + + return $this; + } + + /** + * Sorts files and directories by the last accessed time. + * + * This is the time that the file was last accessed, read or written to. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sortByAccessedTime() + { + $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME; + + return $this; + } + + /** + * Sorts files and directories by the last inode changed time. + * + * This is the time that the inode information was last modified (permissions, owner, group or other metadata). + * + * On Windows, since inode is not available, changed time is actually the file creation time. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sortByChangedTime() + { + $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME; + + return $this; + } + + /** + * Sorts files and directories by the last modified time. + * + * This is the last time the actual contents of the file were last modified. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sortByModifiedTime() + { + $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME; + + return $this; + } + + /** + * Filters the iterator with an anonymous function. + * + * The anonymous function receives a \SplFileInfo and must return false + * to remove files. + * + * @param \Closure $closure An anonymous function + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\CustomFilterIterator + * + * @api + */ + public function filter(\Closure $closure) + { + $this->filters[] = $closure; + + return $this; + } + + /** + * Forces the following of symlinks. + * + * @return Finder The current Finder instance + * + * @api + */ + public function followLinks() + { + $this->followLinks = true; + + return $this; + } + + /** + * Tells finder to ignore unreadable directories. + * + * By default, scanning unreadable directories content throws an AccessDeniedException. + * + * @param boolean $ignore + * + * @return Finder The current Finder instance + */ + public function ignoreUnreadableDirs($ignore = true) + { + $this->ignoreUnreadableDirs = (Boolean) $ignore; + + return $this; + } + + /** + * Searches files and directories which match defined rules. + * + * @param string|array $dirs A directory path or an array of directories + * + * @return Finder The current Finder instance + * + * @throws \InvalidArgumentException if one of the directories does not exist + * + * @api + */ + public function in($dirs) + { + $resolvedDirs = array(); + + foreach ((array) $dirs as $dir) { + if (is_dir($dir)) { + $resolvedDirs[] = $dir; + } elseif ($glob = glob($dir, GLOB_ONLYDIR)) { + $resolvedDirs = array_merge($resolvedDirs, $glob); + } else { + throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir)); + } + } + + $this->dirs = array_merge($this->dirs, $resolvedDirs); + + return $this; + } + + /** + * Returns an Iterator for the current Finder configuration. + * + * This method implements the IteratorAggregate interface. + * + * @return \Iterator An iterator + * + * @throws \LogicException if the in() method has not been called + */ + public function getIterator() + { + if (0 === count($this->dirs) && 0 === count($this->iterators)) { + throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.'); + } + + if (1 === count($this->dirs) && 0 === count($this->iterators)) { + return $this->searchInDirectory($this->dirs[0]); + } + + $iterator = new \AppendIterator(); + foreach ($this->dirs as $dir) { + $iterator->append($this->searchInDirectory($dir)); + } + + foreach ($this->iterators as $it) { + $iterator->append($it); + } + + return $iterator; + } + + /** + * Appends an existing set of files/directories to the finder. + * + * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array. + * + * @param mixed $iterator + * + * @return Finder The finder + * + * @throws \InvalidArgumentException When the given argument is not iterable. + */ + public function append($iterator) + { + if ($iterator instanceof \IteratorAggregate) { + $this->iterators[] = $iterator->getIterator(); + } elseif ($iterator instanceof \Iterator) { + $this->iterators[] = $iterator; + } elseif ($iterator instanceof \Traversable || is_array($iterator)) { + $it = new \ArrayIterator(); + foreach ($iterator as $file) { + $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file)); + } + $this->iterators[] = $it; + } else { + throw new \InvalidArgumentException('Finder::append() method wrong argument type.'); + } + + return $this; + } + + /** + * Counts all the results collected by the iterators. + * + * @return int + */ + public function count() + { + return iterator_count($this->getIterator()); + } + + /** + * @return Finder The current Finder instance + */ + private function sortAdapters() + { + uasort($this->adapters, function (array $a, array $b) { + if ($a['selected'] || $b['selected']) { + return $a['selected'] ? -1 : 1; + } + + return $a['priority'] > $b['priority'] ? -1 : 1; + }); + + return $this; + } + + /** + * @param $dir + * + * @return \Iterator + * + * @throws \RuntimeException When none of the adapters are supported + */ + private function searchInDirectory($dir) + { + if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) { + $this->exclude = array_merge($this->exclude, self::$vcsPatterns); + } + + if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) { + $this->notPaths[] = '#(^|/)\..+(/|$)#'; + } + + foreach ($this->adapters as $adapter) { + if ($adapter['adapter']->isSupported()) { + try { + return $this + ->buildAdapter($adapter['adapter']) + ->searchInDirectory($dir); + } catch (ExceptionInterface $e) {} + } + } + + throw new \RuntimeException('No supported adapter found.'); + } + + /** + * @param AdapterInterface $adapter + * + * @return AdapterInterface + */ + private function buildAdapter(AdapterInterface $adapter) + { + return $adapter + ->setFollowLinks($this->followLinks) + ->setDepths($this->depths) + ->setMode($this->mode) + ->setExclude($this->exclude) + ->setNames($this->names) + ->setNotNames($this->notNames) + ->setContains($this->contains) + ->setNotContains($this->notContains) + ->setSizes($this->sizes) + ->setDates($this->dates) + ->setFilters($this->filters) + ->setSort($this->sort) + ->setPath($this->paths) + ->setNotPath($this->notPaths) + ->ignoreUnreadableDirs($this->ignoreUnreadableDirs); + } + + /** + * Unselects all adapters. + */ + private function resetAdapterSelection() + { + $this->adapters = array_map(function (array $properties) { + $properties['selected'] = false; + + return $properties; + }, $this->adapters); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Glob.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Glob.php new file mode 100644 index 0000000..dd26384 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Glob.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\Finder; + +/** + * Glob matches globbing patterns against text. + * + * if match_glob("foo.*", "foo.bar") echo "matched\n"; + * + * // prints foo.bar and foo.baz + * $regex = glob_to_regex("foo.*"); + * for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t) + * { + * if (/$regex/) echo "matched: $car\n"; + * } + * + * Glob implements glob(3) style matching that can be used to match + * against text, rather than fetching names from a filesystem. + * + * Based on the Perl Text::Glob module. + * + * @author Fabien Potencier PHP port + * @author Richard Clamp Perl version + * @copyright 2004-2005 Fabien Potencier + * @copyright 2002 Richard Clamp + */ +class Glob +{ + /** + * Returns a regexp which is the equivalent of the glob pattern. + * + * @param string $glob The glob pattern + * @param Boolean $strictLeadingDot + * @param Boolean $strictWildcardSlash + * + * @return string regex The regexp + */ + public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true) + { + $firstByte = true; + $escaping = false; + $inCurlies = 0; + $regex = ''; + $sizeGlob = strlen($glob); + for ($i = 0; $i < $sizeGlob; $i++) { + $car = $glob[$i]; + if ($firstByte) { + if ($strictLeadingDot && '.' !== $car) { + $regex .= '(?=[^\.])'; + } + + $firstByte = false; + } + + if ('/' === $car) { + $firstByte = true; + } + + if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) { + $regex .= "\\$car"; + } elseif ('*' === $car) { + $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*'); + } elseif ('?' === $car) { + $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.'); + } elseif ('{' === $car) { + $regex .= $escaping ? '\\{' : '('; + if (!$escaping) { + ++$inCurlies; + } + } elseif ('}' === $car && $inCurlies) { + $regex .= $escaping ? '}' : ')'; + if (!$escaping) { + --$inCurlies; + } + } elseif (',' === $car && $inCurlies) { + $regex .= $escaping ? ',' : '|'; + } elseif ('\\' === $car) { + if ($escaping) { + $regex .= '\\\\'; + $escaping = false; + } else { + $escaping = true; + } + + continue; + } else { + $regex .= $car; + } + $escaping = false; + } + + return '#^'.$regex.'$#'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/CustomFilterIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/CustomFilterIterator.php new file mode 100644 index 0000000..58976c0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/CustomFilterIterator.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\Finder\Iterator; + +/** + * CustomFilterIterator filters files by applying anonymous functions. + * + * The anonymous function receives a \SplFileInfo and must return false + * to remove files. + * + * @author Fabien Potencier + */ +class CustomFilterIterator extends FilterIterator +{ + private $filters = array(); + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param array $filters An array of PHP callbacks + * + * @throws \InvalidArgumentException + */ + public function __construct(\Iterator $iterator, array $filters) + { + foreach ($filters as $filter) { + if (!is_callable($filter)) { + throw new \InvalidArgumentException('Invalid PHP callback.'); + } + } + $this->filters = $filters; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return Boolean true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + + foreach ($this->filters as $filter) { + if (false === call_user_func($filter, $fileinfo)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php new file mode 100644 index 0000000..3e5713b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.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\Finder\Iterator; + +use Symfony\Component\Finder\Comparator\DateComparator; + +/** + * DateRangeFilterIterator filters out files that are not in the given date range (last modified dates). + * + * @author Fabien Potencier + */ +class DateRangeFilterIterator extends FilterIterator +{ + private $comparators = array(); + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param DateComparator[] $comparators An array of DateComparator instances + */ + public function __construct(\Iterator $iterator, array $comparators) + { + $this->comparators = $comparators; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return Boolean true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + + if (!$fileinfo->isFile()) { + return true; + } + + $filedate = $fileinfo->getMTime(); + foreach ($this->comparators as $compare) { + if (!$compare->test($filedate)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php new file mode 100644 index 0000000..67cf5de --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.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\Finder\Iterator; + +/** + * DepthRangeFilterIterator limits the directory depth. + * + * @author Fabien Potencier + */ +class DepthRangeFilterIterator extends FilterIterator +{ + private $minDepth = 0; + + /** + * Constructor. + * + * @param \RecursiveIteratorIterator $iterator The Iterator to filter + * @param int $minDepth The min depth + * @param int $maxDepth The max depth + */ + public function __construct(\RecursiveIteratorIterator $iterator, $minDepth = 0, $maxDepth = PHP_INT_MAX) + { + $this->minDepth = $minDepth; + $iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth); + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return Boolean true if the value should be kept, false otherwise + */ + public function accept() + { + return $this->getInnerIterator()->getDepth() >= $this->minDepth; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php new file mode 100644 index 0000000..42c8ce7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.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\Finder\Iterator; + +/** + * ExcludeDirectoryFilterIterator filters out directories. + * + * @author Fabien Potencier + */ +class ExcludeDirectoryFilterIterator extends FilterIterator +{ + private $patterns; + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param array $directories An array of directories to exclude + */ + public function __construct(\Iterator $iterator, array $directories) + { + $this->patterns = array(); + foreach ($directories as $directory) { + $this->patterns[] = '#(^|/)'.preg_quote($directory, '#').'(/|$)#'; + } + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return Boolean true if the value should be kept, false otherwise + */ + public function accept() + { + $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath(); + $path = strtr($path, '\\', '/'); + foreach ($this->patterns as $pattern) { + if (preg_match($pattern, $path)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilePathsIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilePathsIterator.php new file mode 100644 index 0000000..48634f2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilePathsIterator.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\Finder\Iterator; + +use Symfony\Component\Finder\SplFileInfo; + +/** + * Iterate over shell command result. + * + * @author Jean-François Simon + */ +class FilePathsIterator extends \ArrayIterator +{ + /** + * @var string + */ + private $baseDir; + + /** + * @var int + */ + private $baseDirLength; + + /** + * @var string + */ + private $subPath; + + /** + * @var string + */ + private $subPathname; + + /** + * @var SplFileInfo + */ + private $current; + + /** + * @param array $paths List of paths returned by shell command + * @param string $baseDir Base dir for relative path building + */ + public function __construct(array $paths, $baseDir) + { + $this->baseDir = $baseDir; + $this->baseDirLength = strlen($baseDir); + + parent::__construct($paths); + } + + /** + * @param string $name + * @param array $arguments + * + * @return mixed + */ + public function __call($name, array $arguments) + { + return call_user_func_array(array($this->current(), $name), $arguments); + } + + /** + * Return an instance of SplFileInfo with support for relative paths. + * + * @return SplFileInfo File information + */ + public function current() + { + return $this->current; + } + + /** + * @return string + */ + public function key() + { + return $this->current->getPathname(); + } + + public function next() + { + parent::next(); + $this->buildProperties(); + } + + public function rewind() + { + parent::rewind(); + $this->buildProperties(); + } + + /** + * @return string + */ + public function getSubPath() + { + return $this->subPath; + } + + /** + * @return string + */ + public function getSubPathname() + { + return $this->subPathname; + } + + private function buildProperties() + { + $absolutePath = parent::current(); + + if ($this->baseDir === substr($absolutePath, 0, $this->baseDirLength)) { + $this->subPathname = ltrim(substr($absolutePath, $this->baseDirLength), '/\\'); + $dir = dirname($this->subPathname); + $this->subPath = '.' === $dir ? '' : $dir; + } else { + $this->subPath = $this->subPathname = ''; + } + + $this->current = new SplFileInfo(parent::current(), $this->subPath, $this->subPathname); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php new file mode 100644 index 0000000..5b7bbb1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.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\Finder\Iterator; + +/** + * FileTypeFilterIterator only keeps files, directories, or both. + * + * @author Fabien Potencier + */ +class FileTypeFilterIterator extends FilterIterator +{ + const ONLY_FILES = 1; + const ONLY_DIRECTORIES = 2; + + private $mode; + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param integer $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES) + */ + public function __construct(\Iterator $iterator, $mode) + { + $this->mode = $mode; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return Boolean true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) { + return false; + } elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) { + return false; + } + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php new file mode 100644 index 0000000..953f9c3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.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\Finder\Iterator; + +/** + * FilecontentFilterIterator filters files by their contents using patterns (regexps or strings). + * + * @author Fabien Potencier + * @author Włodzimierz Gajda + */ +class FilecontentFilterIterator extends MultiplePcreFilterIterator +{ + /** + * Filters the iterator values. + * + * @return Boolean true if the value should be kept, false otherwise + */ + public function accept() + { + if (!$this->matchRegexps && !$this->noMatchRegexps) { + return true; + } + + $fileinfo = $this->current(); + + if ($fileinfo->isDir() || !$fileinfo->isReadable()) { + return false; + } + + $content = $fileinfo->getContents(); + if (!$content) { + return false; + } + + // should at least not match one rule to exclude + foreach ($this->noMatchRegexps as $regex) { + if (preg_match($regex, $content)) { + return false; + } + } + + // should at least match one rule + $match = true; + if ($this->matchRegexps) { + $match = false; + foreach ($this->matchRegexps as $regex) { + if (preg_match($regex, $content)) { + return true; + } + } + } + + return $match; + } + + /** + * Converts string to regexp if necessary. + * + * @param string $str Pattern: string or regexp + * + * @return string regexp corresponding to a given string or regexp + */ + protected function toRegex($str) + { + return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php new file mode 100644 index 0000000..3c0f3aa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilenameFilterIterator.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\Finder\Iterator; + +use Symfony\Component\Finder\Expression\Expression; + +/** + * FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string). + * + * @author Fabien Potencier + */ +class FilenameFilterIterator extends MultiplePcreFilterIterator +{ + + /** + * Filters the iterator values. + * + * @return Boolean true if the value should be kept, false otherwise + */ + public function accept() + { + $filename = $this->current()->getFilename(); + + // should at least not match one rule to exclude + foreach ($this->noMatchRegexps as $regex) { + if (preg_match($regex, $filename)) { + return false; + } + } + + // should at least match one rule + $match = true; + if ($this->matchRegexps) { + $match = false; + foreach ($this->matchRegexps as $regex) { + if (preg_match($regex, $filename)) { + return true; + } + } + } + + return $match; + } + + /** + * Converts glob to regexp. + * + * PCRE patterns are left unchanged. + * Glob strings are transformed with Glob::toRegex(). + * + * @param string $str Pattern: glob or regexp + * + * @return string regexp corresponding to a given glob or regexp + */ + protected function toRegex($str) + { + return Expression::create($str)->getRegex()->render(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilterIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilterIterator.php new file mode 100644 index 0000000..f4da44c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/FilterIterator.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\Finder\Iterator; + +/** + * This iterator just overrides the rewind method in order to correct a PHP bug. + * + * @see https://bugs.php.net/bug.php?id=49104 + * + * @author Alex Bogomazov + */ +abstract class FilterIterator extends \FilterIterator +{ + /** + * This is a workaround for the problem with \FilterIterator leaving inner \FilesystemIterator in wrong state after + * rewind in some cases. + * + * @see FilterIterator::rewind() + */ + public function rewind() + { + $iterator = $this; + while ($iterator instanceof \OuterIterator) { + $innerIterator = $iterator->getInnerIterator(); + + if ($innerIterator instanceof RecursiveDirectoryIterator) { + if ($innerIterator->isRewindable()) { + $innerIterator->next(); + $innerIterator->rewind(); + } + } elseif ($iterator->getInnerIterator() instanceof \FilesystemIterator) { + $iterator->getInnerIterator()->next(); + $iterator->getInnerIterator()->rewind(); + } + $iterator = $iterator->getInnerIterator(); + } + + parent::rewind(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php new file mode 100644 index 0000000..3a9dd55 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.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\Finder\Iterator; + +use Symfony\Component\Finder\Expression\Expression; + +/** + * MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings). + * + * @author Fabien Potencier + */ +abstract class MultiplePcreFilterIterator extends FilterIterator +{ + protected $matchRegexps; + protected $noMatchRegexps; + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param array $matchPatterns An array of patterns that need to match + * @param array $noMatchPatterns An array of patterns that need to not match + */ + public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns) + { + $this->matchRegexps = array(); + foreach ($matchPatterns as $pattern) { + $this->matchRegexps[] = $this->toRegex($pattern); + } + + $this->noMatchRegexps = array(); + foreach ($noMatchPatterns as $pattern) { + $this->noMatchRegexps[] = $this->toRegex($pattern); + } + + parent::__construct($iterator); + } + + /** + * Checks whether the string is a regex. + * + * @param string $str + * + * @return Boolean Whether the given string is a regex + */ + protected function isRegex($str) + { + return Expression::create($str)->isRegex(); + } + + /** + * Converts string into regexp. + * + * @param string $str Pattern + * + * @return string regexp corresponding to a given string + */ + abstract protected function toRegex($str); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/PathFilterIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/PathFilterIterator.php new file mode 100644 index 0000000..c736f0b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/PathFilterIterator.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\Finder\Iterator; + +/** + * PathFilterIterator filters files by path patterns (e.g. some/special/dir). + * + * @author Fabien Potencier + * @author Włodzimierz Gajda + */ +class PathFilterIterator extends MultiplePcreFilterIterator +{ + + /** + * Filters the iterator values. + * + * @return Boolean true if the value should be kept, false otherwise + */ + public function accept() + { + $filename = $this->current()->getRelativePathname(); + + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $filename = strtr($filename, '\\', '/'); + } + + // should at least not match one rule to exclude + foreach ($this->noMatchRegexps as $regex) { + if (preg_match($regex, $filename)) { + return false; + } + } + + // should at least match one rule + $match = true; + if ($this->matchRegexps) { + $match = false; + foreach ($this->matchRegexps as $regex) { + if (preg_match($regex, $filename)) { + return true; + } + } + } + + return $match; + } + + /** + * Converts strings to regexp. + * + * PCRE patterns are left unchanged. + * + * Default conversion: + * 'lorem/ipsum/dolor' ==> 'lorem\/ipsum\/dolor/' + * + * Use only / as directory separator (on Windows also). + * + * @param string $str Pattern: regexp or dirname. + * + * @return string regexp corresponding to a given string or regexp + */ + protected function toRegex($str) + { + return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php new file mode 100644 index 0000000..f189712 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.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\Finder\Iterator; + +use Symfony\Component\Finder\Exception\AccessDeniedException; +use Symfony\Component\Finder\SplFileInfo; + +/** + * Extends the \RecursiveDirectoryIterator to support relative paths + * + * @author Victor Berchet + */ +class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator +{ + /** + * @var boolean + */ + private $ignoreUnreadableDirs; + + /** + * @var Boolean + */ + private $rewindable; + + /** + * Constructor. + * + * @param string $path + * @param int $flags + * @param boolean $ignoreUnreadableDirs + * + * @throws \RuntimeException + */ + public function __construct($path, $flags, $ignoreUnreadableDirs = false) + { + if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) { + throw new \RuntimeException('This iterator only support returning current as fileinfo.'); + } + + parent::__construct($path, $flags); + $this->ignoreUnreadableDirs = $ignoreUnreadableDirs; + } + + /** + * Return an instance of SplFileInfo with support for relative paths + * + * @return SplFileInfo File information + */ + public function current() + { + return new SplFileInfo(parent::current()->getPathname(), $this->getSubPath(), $this->getSubPathname()); + } + + /** + * @return \RecursiveIterator + * + * @throws AccessDeniedException + */ + public function getChildren() + { + try { + return parent::getChildren(); + } catch (\UnexpectedValueException $e) { + if ($this->ignoreUnreadableDirs) { + // If directory is unreadable and finder is set to ignore it, a fake empty content is returned. + return new \RecursiveArrayIterator(array()); + } else { + throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e); + } + } + } + + /** + * Do nothing for non rewindable stream + */ + public function rewind() + { + if (false === $this->isRewindable()) { + return; + } + + // @see https://bugs.php.net/bug.php?id=49104 + parent::next(); + + parent::rewind(); + } + + /** + * Checks if the stream is rewindable. + * + * @return Boolean true when the stream is rewindable, false otherwise + */ + public function isRewindable() + { + if (null !== $this->rewindable) { + return $this->rewindable; + } + + if (false !== $stream = @opendir($this->getPath())) { + $infos = stream_get_meta_data($stream); + closedir($stream); + + if ($infos['seekable']) { + return $this->rewindable = true; + } + } + + return $this->rewindable = false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php new file mode 100644 index 0000000..77f9cc4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.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\Finder\Iterator; + +use Symfony\Component\Finder\Comparator\NumberComparator; + +/** + * SizeRangeFilterIterator filters out files that are not in the given size range. + * + * @author Fabien Potencier + */ +class SizeRangeFilterIterator extends FilterIterator +{ + private $comparators = array(); + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param NumberComparator[] $comparators An array of NumberComparator instances + */ + public function __construct(\Iterator $iterator, array $comparators) + { + $this->comparators = $comparators; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return Boolean true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + if (!$fileinfo->isFile()) { + return true; + } + + $filesize = $fileinfo->getSize(); + foreach ($this->comparators as $compare) { + if (!$compare->test($filesize)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/SortableIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/SortableIterator.php new file mode 100644 index 0000000..cdc734f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Iterator/SortableIterator.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\Finder\Iterator; + +/** + * SortableIterator applies a sort on a given Iterator. + * + * @author Fabien Potencier + */ +class SortableIterator implements \IteratorAggregate +{ + const SORT_BY_NAME = 1; + const SORT_BY_TYPE = 2; + const SORT_BY_ACCESSED_TIME = 3; + const SORT_BY_CHANGED_TIME = 4; + const SORT_BY_MODIFIED_TIME = 5; + + private $iterator; + private $sort; + + /** + * Constructor. + * + * @param \Traversable $iterator The Iterator to filter + * @param integer|callback $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback) + * + * @throws \InvalidArgumentException + */ + public function __construct(\Traversable $iterator, $sort) + { + $this->iterator = $iterator; + + if (self::SORT_BY_NAME === $sort) { + $this->sort = function ($a, $b) { + return strcmp($a->getRealpath(), $b->getRealpath()); + }; + } elseif (self::SORT_BY_TYPE === $sort) { + $this->sort = function ($a, $b) { + if ($a->isDir() && $b->isFile()) { + return -1; + } elseif ($a->isFile() && $b->isDir()) { + return 1; + } + + return strcmp($a->getRealpath(), $b->getRealpath()); + }; + } elseif (self::SORT_BY_ACCESSED_TIME === $sort) { + $this->sort = function ($a, $b) { + return ($a->getATime() > $b->getATime()); + }; + } elseif (self::SORT_BY_CHANGED_TIME === $sort) { + $this->sort = function ($a, $b) { + return ($a->getCTime() > $b->getCTime()); + }; + } elseif (self::SORT_BY_MODIFIED_TIME === $sort) { + $this->sort = function ($a, $b) { + return ($a->getMTime() > $b->getMTime()); + }; + } elseif (is_callable($sort)) { + $this->sort = $sort; + } else { + throw new \InvalidArgumentException('The SortableIterator takes a PHP callback or a valid built-in sort algorithm as an argument.'); + } + } + + public function getIterator() + { + $array = iterator_to_array($this->iterator, true); + uasort($array, $this->sort); + + return new \ArrayIterator($array); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/Finder/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Component/Finder/README.md b/vendor/symfony/symfony/src/Symfony/Component/Finder/README.md new file mode 100644 index 0000000..a4caf93 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/README.md @@ -0,0 +1,41 @@ +Finder Component +================ + +Finder finds files and directories via an intuitive fluent interface. + + use Symfony\Component\Finder\Finder; + + $finder = new Finder(); + + $iterator = $finder + ->files() + ->name('*.php') + ->depth(0) + ->size('>= 1K') + ->in(__DIR__); + + foreach ($iterator as $file) { + print $file->getRealpath()."\n"; + } + +But you can also use it to find files stored remotely like in this example where +we are looking for files on Amazon S3: + + $s3 = new \Zend_Service_Amazon_S3($key, $secret); + $s3->registerStreamWrapper("s3"); + + $finder = new Finder(); + $finder->name('photos*')->size('< 100K')->date('since 1 hour ago'); + foreach ($finder->in('s3://bucket-name') as $file) { + print $file->getFilename()."\n"; + } + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Finder/ + $ composer.phar install --dev + $ phpunit + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Shell/Command.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Shell/Command.php new file mode 100644 index 0000000..9a02f7e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Shell/Command.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\Finder\Shell; + +/** + * @author Jean-François Simon + */ +class Command +{ + /** + * @var Command|null + */ + private $parent; + + /** + * @var array + */ + private $bits; + + /** + * @var array + */ + private $labels; + + /** + * @var \Closure|null + */ + private $errorHandler; + + /** + * Constructor. + * + * @param Command $parent Parent command + */ + public function __construct(Command $parent = null) + { + $this->parent = $parent; + $this->bits = array(); + $this->labels = array(); + } + + /** + * Returns command as string. + * + * @return string + */ + public function __toString() + { + return $this->join(); + } + + /** + * Creates a new Command instance. + * + * @param Command $parent Parent command + * + * @return Command New Command instance + */ + public static function create(Command $parent = null) + { + return new self($parent); + } + + /** + * Escapes special chars from input. + * + * @param string $input A string to escape + * + * @return string The escaped string + */ + public static function escape($input) + { + return escapeshellcmd($input); + } + + /** + * Quotes input. + * + * @param string $input An argument string + * + * @return string The quoted string + */ + public static function quote($input) + { + return escapeshellarg($input); + } + + /** + * Appends a string or a Command instance. + * + * @param string|Command $bit + * + * @return Command The current Command instance + */ + public function add($bit) + { + $this->bits[] = $bit; + + return $this; + } + + /** + * Prepends a string or a command instance. + * + * @param string|Command $bit + * + * @return Command The current Command instance + */ + public function top($bit) + { + array_unshift($this->bits, $bit); + + foreach ($this->labels as $label => $index) { + $this->labels[$label] += 1; + } + + return $this; + } + + /** + * Appends an argument, will be quoted. + * + * @param string $arg + * + * @return Command The current Command instance + */ + public function arg($arg) + { + $this->bits[] = self::quote($arg); + + return $this; + } + + /** + * Appends escaped special command chars. + * + * @param string $esc + * + * @return Command The current Command instance + */ + public function cmd($esc) + { + $this->bits[] = self::escape($esc); + + return $this; + } + + /** + * Inserts a labeled command to feed later. + * + * @param string $label The unique label + * + * @return Command The current Command instance + * + * @throws \RuntimeException If label already exists + */ + public function ins($label) + { + if (isset($this->labels[$label])) { + throw new \RuntimeException(sprintf('Label "%s" already exists.', $label)); + } + + $this->bits[] = self::create($this); + $this->labels[$label] = count($this->bits)-1; + + return $this->bits[$this->labels[$label]]; + } + + /** + * Retrieves a previously labeled command. + * + * @param string $label + * + * @return Command The labeled command + * + * @throws \RuntimeException + */ + public function get($label) + { + if (!isset($this->labels[$label])) { + throw new \RuntimeException(sprintf('Label "%s" does not exists.', $label)); + } + + return $this->bits[$this->labels[$label]]; + } + + /** + * Returns parent command (if any). + * + * @return Command Parent command + * + * @throws \RuntimeException If command has no parent + */ + public function end() + { + if (null === $this->parent) { + throw new \RuntimeException('Calling end on root command doesn\'t make sense.'); + } + + return $this->parent; + } + + /** + * Counts bits stored in command. + * + * @return int The bits count + */ + public function length() + { + return count($this->bits); + } + + /** + * @param \Closure $errorHandler + * + * @return Command + */ + public function setErrorHandler(\Closure $errorHandler) + { + $this->errorHandler = $errorHandler; + + return $this; + } + + /** + * @return callable|null + */ + public function getErrorHandler() + { + return $this->errorHandler; + } + + /** + * Executes current command. + * + * @return array The command result + * + * @throws \RuntimeException + */ + public function execute() + { + if (null === $this->errorHandler) { + exec($this->join(), $output); + } else { + $process = proc_open($this->join(), array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes); + $output = preg_split('~(\r\n|\r|\n)~', stream_get_contents($pipes[1]), -1, PREG_SPLIT_NO_EMPTY); + + if ($error = stream_get_contents($pipes[2])) { + call_user_func($this->errorHandler, $error); + } + + proc_close($process); + } + + return $output ?: array(); + } + + /** + * Joins bits. + * + * @return string + */ + public function join() + { + return implode(' ', array_filter( + array_map(function($bit) { + return $bit instanceof Command ? $bit->join() : ($bit ?: null); + }, $this->bits), + function($bit) { return null !== $bit; } + )); + } + + /** + * Insert a string or a Command instance before the bit at given position $index (index starts from 0). + * + * @param string|Command $bit + * @param integer $index + * + * @return Command The current Command instance + */ + public function addAtIndex($bit, $index) + { + array_splice($this->bits, $index, 0, $bit); + + return $this; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Shell/Shell.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Shell/Shell.php new file mode 100644 index 0000000..0b01636 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Shell/Shell.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\Finder\Shell; + +/** + * @author Jean-François Simon + */ +class Shell +{ + const TYPE_UNIX = 1; + const TYPE_DARWIN = 2; + const TYPE_CYGWIN = 3; + const TYPE_WINDOWS = 4; + const TYPE_BSD = 5; + + /** + * @var string|null + */ + private $type; + + /** + * Returns guessed OS type. + * + * @return int + */ + public function getType() + { + if (null === $this->type) { + $this->type = $this->guessType(); + } + + return $this->type; + } + + /** + * Tests if a command is available. + * + * @param string $command + * + * @return bool + */ + public function testCommand($command) + { + if (self::TYPE_WINDOWS === $this->type) { + // todo: find a way to test if windows command exists + return false; + } + + if (!function_exists('exec')) { + return false; + } + + // todo: find a better way (command could not be available) + exec('command -v '.$command, $output, $code); + + return 0 === $code && count($output) > 0; + } + + /** + * Guesses OS type. + * + * @return int + */ + private function guessType() + { + $os = strtolower(PHP_OS); + + if (false !== strpos($os, 'cygwin')) { + return self::TYPE_CYGWIN; + } + + if (false !== strpos($os, 'darwin')) { + return self::TYPE_DARWIN; + } + + if (false !== strpos($os, 'bsd')) { + return self::TYPE_BSD; + } + + if (0 === strpos($os, 'win')) { + return self::TYPE_WINDOWS; + } + + return self::TYPE_UNIX; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/SplFileInfo.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/SplFileInfo.php new file mode 100644 index 0000000..e7e58f7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/SplFileInfo.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\Finder; + +/** + * Extends \SplFileInfo to support relative paths + * + * @author Fabien Potencier + */ +class SplFileInfo extends \SplFileInfo +{ + private $relativePath; + private $relativePathname; + + /** + * Constructor + * + * @param string $file The file name + * @param string $relativePath The relative path + * @param string $relativePathname The relative path name + */ + public function __construct($file, $relativePath, $relativePathname) + { + parent::__construct($file); + $this->relativePath = $relativePath; + $this->relativePathname = $relativePathname; + } + + /** + * Returns the relative path + * + * @return string the relative path + */ + public function getRelativePath() + { + return $this->relativePath; + } + + /** + * Returns the relative path name + * + * @return string the relative path name + */ + public function getRelativePathname() + { + return $this->relativePathname; + } + + /** + * Returns the contents of the file + * + * @return string the contents of the file + * + * @throws \RuntimeException + */ + public function getContents() + { + $level = error_reporting(0); + $content = file_get_contents($this->getRealpath()); + error_reporting($level); + if (false === $content) { + $error = error_get_last(); + throw new \RuntimeException($error['message']); + } + + return $content; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Comparator/ComparatorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Comparator/ComparatorTest.php new file mode 100644 index 0000000..bf59844 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Comparator/ComparatorTest.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\Finder\Tests\Comparator; + +use Symfony\Component\Finder\Comparator\Comparator; + +class ComparatorTest extends \PHPUnit_Framework_TestCase +{ + public function testGetSetOperator() + { + $comparator = new Comparator(); + try { + $comparator->setOperator('foo'); + $this->fail('->setOperator() throws an \InvalidArgumentException if the operator is not valid.'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '->setOperator() throws an \InvalidArgumentException if the operator is not valid.'); + } + + $comparator = new Comparator(); + $comparator->setOperator('>'); + $this->assertEquals('>', $comparator->getOperator(), '->getOperator() returns the current operator'); + } + + public function testGetSetTarget() + { + $comparator = new Comparator(); + $comparator->setTarget(8); + $this->assertEquals(8, $comparator->getTarget(), '->getTarget() returns the target'); + } + + /** + * @dataProvider getTestData + */ + public function testTest($operator, $target, $match, $noMatch) + { + $c = new Comparator(); + $c->setOperator($operator); + $c->setTarget($target); + + foreach ($match as $m) { + $this->assertTrue($c->test($m), '->test() tests a string against the expression'); + } + + foreach ($noMatch as $m) { + $this->assertFalse($c->test($m), '->test() tests a string against the expression'); + } + } + + public function getTestData() + { + return array( + array('<', '1000', array('500', '999'), array('1000', '1500')), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Comparator/DateComparatorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Comparator/DateComparatorTest.php new file mode 100644 index 0000000..ac8256a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Comparator/DateComparatorTest.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\Finder\Tests\Comparator; + +use Symfony\Component\Finder\Comparator\DateComparator; + +class DateComparatorTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + try { + new DateComparator('foobar'); + $this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } + + try { + new DateComparator(''); + $this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } + } + + /** + * @dataProvider getTestData + */ + public function testTest($test, $match, $noMatch) + { + $c = new DateComparator($test); + + foreach ($match as $m) { + $this->assertTrue($c->test($m), '->test() tests a string against the expression'); + } + + foreach ($noMatch as $m) { + $this->assertFalse($c->test($m), '->test() tests a string against the expression'); + } + } + + public function getTestData() + { + return array( + array('< 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))), + array('until 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))), + array('before 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))), + array('> 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))), + array('after 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))), + array('since 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))), + array('!= 2005-10-10', array(strtotime('2005-10-11')), array(strtotime('2005-10-10'))), + ); + + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Comparator/NumberComparatorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Comparator/NumberComparatorTest.php new file mode 100644 index 0000000..b07870b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Comparator/NumberComparatorTest.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\Finder\Tests\Comparator; + +use Symfony\Component\Finder\Comparator\NumberComparator; + +class NumberComparatorTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @dataProvider getConstructorTestData + */ + public function testConstructor($successes, $failures) + { + foreach ($successes as $s) { + new NumberComparator($s); + } + + foreach ($failures as $f) { + try { + new NumberComparator($f); + $this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } + } + } + + /** + * @dataProvider getTestData + */ + public function testTest($test, $match, $noMatch) + { + $c = new NumberComparator($test); + + foreach ($match as $m) { + $this->assertTrue($c->test($m), '->test() tests a string against the expression'); + } + + foreach ($noMatch as $m) { + $this->assertFalse($c->test($m), '->test() tests a string against the expression'); + } + } + + public function getTestData() + { + return array( + array('< 1000', array('500', '999'), array('1000', '1500')), + + array('< 1K', array('500', '999'), array('1000', '1500')), + array('<1k', array('500', '999'), array('1000', '1500')), + array(' < 1 K ', array('500', '999'), array('1000', '1500')), + array('<= 1K', array('1000'), array('1001')), + array('> 1K', array('1001'), array('1000')), + array('>= 1K', array('1000'), array('999')), + + array('< 1KI', array('500', '1023'), array('1024', '1500')), + array('<= 1KI', array('1024'), array('1025')), + array('> 1KI', array('1025'), array('1024')), + array('>= 1KI', array('1024'), array('1023')), + + array('1KI', array('1024'), array('1023', '1025')), + array('==1KI', array('1024'), array('1023', '1025')), + + array('==1m', array('1000000'), array('999999', '1000001')), + array('==1mi', array(1024*1024), array(1024*1024-1, 1024*1024+1)), + + array('==1g', array('1000000000'), array('999999999', '1000000001')), + array('==1gi', array(1024*1024*1024), array(1024*1024*1024-1, 1024*1024*1024+1)), + + array('!= 1000', array('500', '999'), array('1000')), + ); + } + + public function getConstructorTestData() + { + return array( + array( + array( + '1', '0', + '3.5', '33.55', '123.456', '123456.78', + '.1', '.123', + '.0', '0.0', + '1.', '0.', '123.', + '==1', '!=1', '<1', '>1', '<=1', '>=1', + '==1k', '==1ki', '==1m', '==1mi', '==1g', '==1gi', + '1k', '1ki', '1m', '1mi', '1g', '1gi', + ), + array( + false, null, '', + ' ', 'foobar', + '=1', '===1', + '0 . 1', '123 .45', '234. 567', + '..', '.0.', '0.1.2', + ) + ), + ); + } + +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Expression/ExpressionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Expression/ExpressionTest.php new file mode 100644 index 0000000..c907d6a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Expression/ExpressionTest.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\Finder\Tests; + +use Symfony\Component\Finder\Expression\Expression; + +class ExpressionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getTypeGuesserData + */ + public function testTypeGuesser($expr, $type) + { + $this->assertEquals($type, Expression::create($expr)->getType()); + } + + /** + * @dataProvider getCaseSensitiveData + */ + public function testCaseSensitive($expr, $isCaseSensitive) + { + $this->assertEquals($isCaseSensitive, Expression::create($expr)->isCaseSensitive()); + } + + /** + * @dataProvider getRegexRenderingData + */ + public function testRegexRendering($expr, $body) + { + $this->assertEquals($body, Expression::create($expr)->renderPattern()); + } + + public function getTypeGuesserData() + { + return array( + array('{foo}', Expression::TYPE_REGEX), + array('/foo/', Expression::TYPE_REGEX), + array('foo', Expression::TYPE_GLOB), + array('foo*', Expression::TYPE_GLOB), + ); + } + + public function getCaseSensitiveData() + { + return array( + array('{foo}m', true), + array('/foo/i', false), + array('foo*', true), + ); + } + + public function getRegexRenderingData() + { + return array( + array('{foo}m', 'foo'), + array('/foo/i', 'foo'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Expression/GlobTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Expression/GlobTest.php new file mode 100644 index 0000000..fbaeb0e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Expression/GlobTest.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\Finder\Tests; + +use Symfony\Component\Finder\Expression\Expression; + +class GlobTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getToRegexData + */ + public function testGlobToRegex($glob, $match, $noMatch) + { + foreach ($match as $m) { + $this->assertRegExp(Expression::create($glob)->getRegex()->render(), $m, '::toRegex() converts a glob to a regexp'); + } + + foreach ($noMatch as $m) { + $this->assertNotRegExp(Expression::create($glob)->getRegex()->render(), $m, '::toRegex() converts a glob to a regexp'); + } + } + + public function getToRegexData() + { + return array( + array('', array(''), array('f', '/')), + array('*', array('foo'), array('foo/', '/foo')), + array('foo.*', array('foo.php', 'foo.a', 'foo.'), array('fooo.php', 'foo.php/foo')), + array('fo?', array('foo', 'fot'), array('fooo', 'ffoo', 'fo/')), + array('fo{o,t}', array('foo', 'fot'), array('fob', 'fo/')), + array('foo(bar|foo)', array('foo(bar|foo)'), array('foobar', 'foofoo')), + array('foo,bar', array('foo,bar'), array('foo', 'bar')), + array('fo{o,\\,}', array('foo', 'fo,'), array()), + array('fo{o,\\\\}', array('foo', 'fo\\'), array()), + array('/foo', array('/foo'), array('foo')), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Expression/RegexTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Expression/RegexTest.php new file mode 100644 index 0000000..f252696 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Expression/RegexTest.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\Finder\Tests; + +use Symfony\Component\Finder\Expression\Expression; + +class RegexTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getHasFlagsData + */ + public function testHasFlags($regex, $start, $end) + { + $expr = new Expression($regex); + + $this->assertEquals($start, $expr->getRegex()->hasStartFlag()); + $this->assertEquals($end, $expr->getRegex()->hasEndFlag()); + } + + /** + * @dataProvider getHasJokersData + */ + public function testHasJokers($regex, $start, $end) + { + $expr = new Expression($regex); + + $this->assertEquals($start, $expr->getRegex()->hasStartJoker()); + $this->assertEquals($end, $expr->getRegex()->hasEndJoker()); + } + + /** + * @dataProvider getSetFlagsData + */ + public function testSetFlags($regex, $start, $end, $expected) + { + $expr = new Expression($regex); + $expr->getRegex()->setStartFlag($start)->setEndFlag($end); + + $this->assertEquals($expected, $expr->render()); + } + + /** + * @dataProvider getSetJokersData + */ + public function testSetJokers($regex, $start, $end, $expected) + { + $expr = new Expression($regex); + $expr->getRegex()->setStartJoker($start)->setEndJoker($end); + + $this->assertEquals($expected, $expr->render()); + } + + public function testOptions() + { + $expr = new Expression('~abc~is'); + $expr->getRegex()->removeOption('i')->addOption('m'); + + $this->assertEquals('~abc~sm', $expr->render()); + } + + public function testMixFlagsAndJokers() + { + $expr = new Expression('~^.*abc.*$~is'); + + $expr->getRegex()->setStartFlag(false)->setEndFlag(false)->setStartJoker(false)->setEndJoker(false); + $this->assertEquals('~abc~is', $expr->render()); + + $expr->getRegex()->setStartFlag(true)->setEndFlag(true)->setStartJoker(true)->setEndJoker(true); + $this->assertEquals('~^.*abc.*$~is', $expr->render()); + } + + /** + * @dataProvider getReplaceJokersTestData + */ + public function testReplaceJokers($regex, $expected) + { + $expr = new Expression($regex); + $expr = $expr->getRegex()->replaceJokers('@'); + + $this->assertEquals($expected, $expr->renderPattern()); + } + + public function getHasFlagsData() + { + return array( + array('~^abc~', true, false), + array('~abc$~', false, true), + array('~abc~', false, false), + array('~^abc$~', true, true), + array('~^abc\\$~', true, false), + ); + } + + public function getHasJokersData() + { + return array( + array('~.*abc~', true, false), + array('~abc.*~', false, true), + array('~abc~', false, false), + array('~.*abc.*~', true, true), + array('~.*abc\\.*~', true, false), + ); + } + + public function getSetFlagsData() + { + return array( + array('~abc~', true, false, '~^abc~'), + array('~abc~', false, true, '~abc$~'), + array('~abc~', false, false, '~abc~'), + array('~abc~', true, true, '~^abc$~'), + ); + } + + public function getSetJokersData() + { + return array( + array('~abc~', true, false, '~.*abc~'), + array('~abc~', false, true, '~abc.*~'), + array('~abc~', false, false, '~abc~'), + array('~abc~', true, true, '~.*abc.*~'), + ); + } + + public function getReplaceJokersTestData() + { + return array( + array('~.abc~', '@abc'), + array('~\\.abc~', '\\.abc'), + array('~\\\\.abc~', '\\\\@abc'), + array('~\\\\\\.abc~', '\\\\\\.abc'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/DummyAdapter.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/DummyAdapter.php new file mode 100644 index 0000000..0cbae14 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/DummyAdapter.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\Finder\Tests\FakeAdapter; + +use Symfony\Component\Finder\Adapter\AbstractAdapter; + +/** + * @author Jean-François Simon + */ +class DummyAdapter extends AbstractAdapter +{ + /** + * @var \Iterator + */ + private $iterator; + + /** + * @param \Iterator $iterator + */ + public function __construct(\Iterator $iterator) + { + $this->iterator = $iterator; + } + + /** + * {@inheritdoc} + */ + public function searchInDirectory($dir) + { + return $this->iterator; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'yes'; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/FailingAdapter.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/FailingAdapter.php new file mode 100644 index 0000000..6e6ed24 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/FailingAdapter.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\Finder\Tests\FakeAdapter; + +use Symfony\Component\Finder\Adapter\AbstractAdapter; +use Symfony\Component\Finder\Exception\AdapterFailureException; + +/** + * @author Jean-François Simon + */ +class FailingAdapter extends AbstractAdapter +{ + /** + * {@inheritdoc} + */ + public function searchInDirectory($dir) + { + throw new AdapterFailureException($this); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'failing'; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/NamedAdapter.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/NamedAdapter.php new file mode 100644 index 0000000..5a260b0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/NamedAdapter.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\Finder\Tests\FakeAdapter; + +use Symfony\Component\Finder\Adapter\AbstractAdapter; + +/** + * @author Jean-François Simon + */ +class NamedAdapter extends AbstractAdapter +{ + /** + * @var string + */ + private $name; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function searchInDirectory($dir) + { + return new \ArrayIterator(array()); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/UnsupportedAdapter.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/UnsupportedAdapter.php new file mode 100644 index 0000000..1f91b98 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FakeAdapter/UnsupportedAdapter.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\Finder\Tests\FakeAdapter; + +use Symfony\Component\Finder\Adapter\AbstractAdapter; + +/** + * @author Jean-François Simon + */ +class UnsupportedAdapter extends AbstractAdapter +{ + /** + * {@inheritdoc} + */ + public function searchInDirectory($dir) + { + return new \ArrayIterator(array()); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'unsupported'; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FinderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FinderTest.php new file mode 100644 index 0000000..c325c59 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/FinderTest.php @@ -0,0 +1,842 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Tests; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\Adapter; +use Symfony\Component\Finder\Tests\FakeAdapter; + +class FinderTest extends Iterator\RealIteratorTestCase +{ + + public function testCreate() + { + $this->assertInstanceOf('Symfony\Component\Finder\Finder', Finder::create()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testDirectories($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->directories()); + $this->assertIterator($this->toAbsolute(array('foo', 'toto')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->directories(); + $finder->files(); + $finder->directories(); + $this->assertIterator($this->toAbsolute(array('foo', 'toto')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testFiles($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->files()); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->files(); + $finder->directories(); + $finder->files(); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testDepth($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->depth('< 1')); + $this->assertIterator($this->toAbsolute(array('foo', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->depth('<= 0')); + $this->assertIterator($this->toAbsolute(array('foo', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->depth('>= 1')); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->depth('< 1')->depth('>= 1'); + $this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testName($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->name('*.php')); + $this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->name('test.ph*'); + $finder->name('test.py'); + $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->name('~^test~i'); + $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->name('~\\.php$~i'); + $this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->name('test.p{hp,y}'); + $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testNotName($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->notName('*.php')); + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->notName('*.php'); + $finder->notName('*.py'); + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->name('test.ph*'); + $finder->name('test.py'); + $finder->notName('*.php'); + $finder->notName('*.py'); + $this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->name('test.ph*'); + $finder->name('test.py'); + $finder->notName('*.p{hp,y}'); + $this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getRegexNameTestData + * + * @group regexName + */ + public function testRegexName($adapter, $regex) + { + $finder = $this->buildFinder($adapter); + $finder->name($regex); + $this->assertIterator($this->toAbsolute(array('test.py', 'test.php')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testSize($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->files()->size('< 1K')->size('> 500')); + $this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testDate($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->files()->date('until last month')); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testExclude($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->exclude('foo')); + $this->assertIterator($this->toAbsolute(array('test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testIgnoreVCS($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->ignoreVCS(false)->ignoreDotFiles(false)); + $this->assertIterator($this->toAbsolute(array('.git', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->ignoreVCS(false)->ignoreVCS(false)->ignoreDotFiles(false); + $this->assertIterator($this->toAbsolute(array('.git', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->ignoreVCS(true)->ignoreDotFiles(false)); + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testIgnoreDotFiles($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->ignoreDotFiles(false)->ignoreVCS(false)); + $this->assertIterator($this->toAbsolute(array('.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $finder->ignoreDotFiles(false)->ignoreDotFiles(false)->ignoreVCS(false); + $this->assertIterator($this->toAbsolute(array('.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->ignoreDotFiles(true)->ignoreVCS(false)); + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testSortByName($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->sortByName()); + $this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testSortByType($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->sortByType()); + $this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'toto', 'foo/bar.tmp', 'test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testSortByAccessedTime($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->sortByAccessedTime()); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'toto', 'test.py', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testSortByChangedTime($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->sortByChangedTime()); + $this->assertIterator($this->toAbsolute(array('toto', 'test.py', 'test.php', 'foo/bar.tmp', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testSortByModifiedTime($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->sortByModifiedTime()); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'toto', 'test.py', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testSort($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->sort(function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealpath(), $b->getRealpath()); })); + $this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testFilter($adapter) + { + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->filter(function (\SplFileInfo $f) { return preg_match('/test/', $f) > 0; })); + $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testFollowLinks($adapter) + { + if ('\\' == DIRECTORY_SEPARATOR) { + return; + } + + $finder = $this->buildFinder($adapter); + $this->assertSame($finder, $finder->followLinks()); + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testIn($adapter) + { + $finder = $this->buildFinder($adapter); + try { + $finder->in('foobar'); + $this->fail('->in() throws a \InvalidArgumentException if the directory does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '->in() throws a \InvalidArgumentException if the directory does not exist'); + } + + $finder = $this->buildFinder($adapter); + $iterator = $finder->files()->name('*.php')->depth('< 1')->in(array(self::$tmpDir, __DIR__))->getIterator(); + + $this->assertIterator(array(self::$tmpDir.DIRECTORY_SEPARATOR.'test.php', __DIR__.DIRECTORY_SEPARATOR.'FinderTest.php'), $iterator); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testInWithGlob($adapter) + { + $finder = $this->buildFinder($adapter); + $finder->in(array(__DIR__.'/Fixtures/*/B/C', __DIR__.'/Fixtures/*/*/B/C'))->getIterator(); + + $this->assertIterator($this->toAbsoluteFixtures(array('A/B/C/abc.dat', 'copy/A/B/C/abc.dat.copy')), $finder); + } + + /** + * @dataProvider getAdaptersTestData + * @expectedException \InvalidArgumentException + */ + public function testInWithNonDirectoryGlob($adapter) + { + $finder = $this->buildFinder($adapter); + $finder->in(__DIR__.'/Fixtures/A/a*'); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testGetIterator($adapter) + { + $finder = $this->buildFinder($adapter); + try { + $finder->getIterator(); + $this->fail('->getIterator() throws a \LogicException if the in() method has not been called'); + } catch (\Exception $e) { + $this->assertInstanceOf('LogicException', $e, '->getIterator() throws a \LogicException if the in() method has not been called'); + } + + $finder = $this->buildFinder($adapter); + $dirs = array(); + foreach ($finder->directories()->in(self::$tmpDir) as $dir) { + $dirs[] = (string) $dir; + } + + $expected = $this->toAbsolute(array('foo', 'toto')); + + sort($dirs); + sort($expected); + + $this->assertEquals($expected, $dirs, 'implements the \IteratorAggregate interface'); + + $finder = $this->buildFinder($adapter); + $this->assertEquals(2, iterator_count($finder->directories()->in(self::$tmpDir)), 'implements the \IteratorAggregate interface'); + + $finder = $this->buildFinder($adapter); + $a = iterator_to_array($finder->directories()->in(self::$tmpDir)); + $a = array_values(array_map(function ($a) { return (string) $a; }, $a)); + sort($a); + $this->assertEquals($expected, $a, 'implements the \IteratorAggregate interface'); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testRelativePath($adapter) + { + $finder = $this->buildFinder($adapter)->in(self::$tmpDir); + + $paths = array(); + + foreach ($finder as $file) { + $paths[] = $file->getRelativePath(); + } + + $ref = array("", "", "", "", "foo", ""); + + sort($ref); + sort($paths); + + $this->assertEquals($ref, $paths); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testRelativePathname($adapter) + { + $finder = $this->buildFinder($adapter)->in(self::$tmpDir)->sortByName(); + + $paths = array(); + + foreach ($finder as $file) { + $paths[] = $file->getRelativePathname(); + } + + $ref = array("test.php", "toto", "test.py", "foo", "foo".DIRECTORY_SEPARATOR."bar.tmp", "foo bar"); + + sort($paths); + sort($ref); + + $this->assertEquals($ref, $paths); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testAppendWithAFinder($adapter) + { + $finder = $this->buildFinder($adapter); + $finder->files()->in(self::$tmpDir.DIRECTORY_SEPARATOR.'foo'); + + $finder1 = $this->buildFinder($adapter); + $finder1->directories()->in(self::$tmpDir); + + $finder = $finder->append($finder1); + + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto')), $finder->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testAppendWithAnArray($adapter) + { + $finder = $this->buildFinder($adapter); + $finder->files()->in(self::$tmpDir.DIRECTORY_SEPARATOR.'foo'); + + $finder->append($this->toAbsolute(array('foo', 'toto'))); + + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto')), $finder->getIterator()); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testAppendReturnsAFinder($adapter) + { + $this->assertInstanceOf('Symfony\\Component\\Finder\\Finder', $this->buildFinder($adapter)->append(array())); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testAppendDoesNotRequireIn($adapter) + { + $finder = $this->buildFinder($adapter); + $finder->in(self::$tmpDir.DIRECTORY_SEPARATOR.'foo'); + + $finder1 = Finder::create()->append($finder); + + $this->assertIterator(iterator_to_array($finder->getIterator()), $finder1->getIterator()); + } + + public function testCountDirectories() + { + $directory = Finder::create()->directories()->in(self::$tmpDir); + $i = 0; + + foreach ($directory as $dir) { + $i++; + } + + $this->assertCount($i, $directory); + } + + public function testCountFiles() + { + $files = Finder::create()->files()->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $i = 0; + + foreach ($files as $file) { + $i++; + } + + $this->assertCount($i, $files); + } + + /** + * @expectedException \LogicException + */ + public function testCountWithoutIn() + { + $finder = Finder::create()->files(); + count($finder); + } + + /** + * @dataProvider getContainsTestData + * @group grep + */ + public function testContains($adapter, $matchPatterns, $noMatchPatterns, $expected) + { + $finder = $this->buildFinder($adapter); + $finder->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures') + ->name('*.txt')->sortByName() + ->contains($matchPatterns) + ->notContains($noMatchPatterns); + + $this->assertIterator($this->toAbsoluteFixtures($expected), $finder); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testContainsOnDirectory(Adapter\AdapterInterface $adapter) + { + $finder = $this->buildFinder($adapter); + $finder->in(__DIR__) + ->directories() + ->name('Fixtures') + ->contains('abc'); + $this->assertIterator(array(), $finder); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testNotContainsOnDirectory(Adapter\AdapterInterface $adapter) + { + $finder = $this->buildFinder($adapter); + $finder->in(__DIR__) + ->directories() + ->name('Fixtures') + ->notContains('abc'); + $this->assertIterator(array(), $finder); + } + + /** + * Searching in multiple locations involves AppendIterator which does an unnecessary rewind which leaves FilterIterator + * with inner FilesystemIterator in an invalid state. + * + * @see https://bugs.php.net/bug.php?id=49104 + * + * @dataProvider getAdaptersTestData + */ + public function testMultipleLocations(Adapter\AdapterInterface $adapter) + { + $locations = array( + self::$tmpDir.'/', + self::$tmpDir.'/toto/', + ); + + // it is expected that there are test.py test.php in the tmpDir + $finder = $this->buildFinder($adapter); + $finder->in($locations)->depth('< 1')->name('test.php'); + + $this->assertEquals(1, count($finder)); + } + + /** + * Iterator keys must be the file pathname. + * + * @dataProvider getAdaptersTestData + */ + public function testIteratorKeys(Adapter\AdapterInterface $adapter) + { + $finder = $this->buildFinder($adapter)->in(self::$tmpDir); + foreach ($finder as $key => $file) { + $this->assertEquals($file->getPathname(), $key); + } + } + + public function testAdaptersOrdering() + { + $finder = Finder::create() + ->removeAdapters() + ->addAdapter(new FakeAdapter\NamedAdapter('a'), 0) + ->addAdapter(new FakeAdapter\NamedAdapter('b'), -50) + ->addAdapter(new FakeAdapter\NamedAdapter('c'), 50) + ->addAdapter(new FakeAdapter\NamedAdapter('d'), -25) + ->addAdapter(new FakeAdapter\NamedAdapter('e'), 25); + + $this->assertEquals( + array('c', 'e', 'a', 'd', 'b'), + array_map(function(Adapter\AdapterInterface $adapter) { + return $adapter->getName(); + }, $finder->getAdapters()) + ); + } + + public function testAdaptersChaining() + { + $iterator = new \ArrayIterator(array()); + $filenames = $this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')); + foreach ($filenames as $file) { + $iterator->append(new \Symfony\Component\Finder\SplFileInfo($file, null, null)); + } + + $finder = Finder::create() + ->removeAdapters() + ->addAdapter(new FakeAdapter\UnsupportedAdapter(), 3) + ->addAdapter(new FakeAdapter\FailingAdapter(), 2) + ->addAdapter(new FakeAdapter\DummyAdapter($iterator), 1); + + $this->assertIterator($filenames, $finder->in(sys_get_temp_dir())->getIterator()); + } + + public function getAdaptersTestData() + { + return array_map( + function ($adapter) { return array($adapter); }, + $this->getValidAdapters() + ); + } + + public function getContainsTestData() + { + $tests = array( + array('', '', array()), + array('foo', 'bar', array()), + array('', 'foobar', array('dolor.txt', 'ipsum.txt', 'lorem.txt')), + array('lorem ipsum dolor sit amet', 'foobar', array('lorem.txt')), + array('sit', 'bar', array('dolor.txt', 'ipsum.txt', 'lorem.txt')), + array('dolor sit amet', '@^L@m', array('dolor.txt', 'ipsum.txt')), + array('/^lorem ipsum dolor sit amet$/m', 'foobar', array('lorem.txt')), + array('lorem', 'foobar', array('lorem.txt')), + array('', 'lorem', array('dolor.txt', 'ipsum.txt')), + array('ipsum dolor sit amet', '/^IPSUM/m', array('lorem.txt')), + ); + + return $this->buildTestData($tests); + } + + public function getRegexNameTestData() + { + $tests = array( + array('~.+\\.p.+~i'), + array('~t.*s~i'), + ); + + return $this->buildTestData($tests); + } + + /** + * @dataProvider getTestPathData + */ + public function testPath(Adapter\AdapterInterface $adapter, $matchPatterns, $noMatchPatterns, array $expected) + { + $finder = $this->buildFinder($adapter); + $finder->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures') + ->path($matchPatterns) + ->notPath($noMatchPatterns); + + $this->assertIterator($this->toAbsoluteFixtures($expected), $finder); + } + + public function testAdapterSelection() + { + // test that by default, PhpAdapter is selected + $adapters = Finder::create()->getAdapters(); + $this->assertTrue($adapters[0] instanceof Adapter\PhpAdapter); + + // test another adapter selection + $adapters = Finder::create()->setAdapter('gnu_find')->getAdapters(); + $this->assertTrue($adapters[0] instanceof Adapter\GnuFindAdapter); + + // test that useBestAdapter method removes selection + $adapters = Finder::create()->useBestAdapter()->getAdapters(); + $this->assertFalse($adapters[0] instanceof Adapter\PhpAdapter); + } + + public function getTestPathData() + { + $tests = array( + array('', '', array()), + array('/^A\/B\/C/', '/C$/', + array('A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat') + ), + array('/^A\/B/', 'foobar', + array( + 'A'.DIRECTORY_SEPARATOR.'B', + 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C', + 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat', + 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat', + ) + ), + array('A/B/C', 'foobar', + array( + 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C', + 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat', + 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C', + 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat.copy', + ) + ), + array('A/B', 'foobar', + array( + //dirs + 'A'.DIRECTORY_SEPARATOR.'B', + 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C', + 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B', + 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C', + //files + 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat', + 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat', + 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat.copy', + 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat.copy', + ) + ), + array('/^with space\//', 'foobar', + array( + 'with space'.DIRECTORY_SEPARATOR.'foo.txt', + ) + ), + ); + + return $this->buildTestData($tests); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testAccessDeniedException(Adapter\AdapterInterface $adapter) + { + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $this->markTestSkipped('chmod is not supported on windows'); + } + + $finder = $this->buildFinder($adapter); + $finder->files()->in(self::$tmpDir); + + // make 'foo' directory non-readable + chmod(self::$tmpDir.DIRECTORY_SEPARATOR.'foo', 0333); + + try { + $this->assertIterator($this->toAbsolute(array('foo bar', 'test.php', 'test.py')), $finder->getIterator()); + $this->fail('Finder should throw an exception when opening a non-readable directory.'); + } catch (\Exception $e) { + $this->assertEquals('Symfony\\Component\\Finder\\Exception\\AccessDeniedException', get_class($e)); + } + + // restore original permissions + chmod(self::$tmpDir.DIRECTORY_SEPARATOR.'foo', 0777); + } + + /** + * @dataProvider getAdaptersTestData + */ + public function testIgnoredAccessDeniedException(Adapter\AdapterInterface $adapter) + { + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $this->markTestSkipped('chmod is not supported on windows'); + } + + $finder = $this->buildFinder($adapter); + $finder->files()->ignoreUnreadableDirs()->in(self::$tmpDir); + + // make 'foo' directory non-readable + chmod(self::$tmpDir.DIRECTORY_SEPARATOR.'foo', 0333); + + $this->assertIterator($this->toAbsolute(array('foo bar', 'test.php', 'test.py')), $finder->getIterator()); + + // restore original permissions + chmod(self::$tmpDir.DIRECTORY_SEPARATOR.'foo', 0777); + } + + private function buildTestData(array $tests) + { + $data = array(); + foreach ($this->getValidAdapters() as $adapter) { + foreach ($tests as $test) { + $data[] = array_merge(array($adapter), $test); + } + } + + return $data; + } + + private function buildFinder(Adapter\AdapterInterface $adapter) + { + return Finder::create() + ->removeAdapters() + ->addAdapter($adapter); + } + + private function getValidAdapters() + { + return array_filter( + array( + new Adapter\BsdFindAdapter(), + new Adapter\GnuFindAdapter(), + new Adapter\PhpAdapter() + ), + function (Adapter\AdapterInterface $adapter) { + return $adapter->isSupported(); + } + ); + } + + /** + * Searching in multiple locations with sub directories involves + * AppendIterator which does an unnecessary rewind which leaves + * FilterIterator with inner FilesystemIterator in an ivalid state. + * + * @see https://bugs.php.net/bug.php?id=49104 + */ + public function testMultipleLocationsWithSubDirectories() + { + $locations = array( + __DIR__.'/Fixtures/one', + self::$tmpDir.DIRECTORY_SEPARATOR.'toto', + ); + + $finder = new Finder(); + $finder->in($locations)->depth('< 10')->name('*.neon'); + + $expected = array( + __DIR__.'/Fixtures/one'.DIRECTORY_SEPARATOR.'b'.DIRECTORY_SEPARATOR.'c.neon', + __DIR__.'/Fixtures/one'.DIRECTORY_SEPARATOR.'b'.DIRECTORY_SEPARATOR.'d.neon', + ); + + $this->assertIterator($expected, $finder); + $this->assertIteratorInForeach($expected, $finder); + } + + public function testNonSeekableStream() + { + try { + $i = Finder::create()->in('ftp://ftp.mozilla.org/')->depth(0)->getIterator(); + } catch (\UnexpectedValueException $e) { + $this->markTestSkipped(sprintf('Unsupported stream "%s".', 'ftp')); + } + + $contains = array( + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'README', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'index.html', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'pub', + ); + + $this->assertIteratorInForeach($contains, $i); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/A/B/C/abc.dat b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/A/B/C/abc.dat new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/A/B/ab.dat b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/A/B/ab.dat new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/A/a.dat b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/A/a.dat new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/B/ab.dat.copy b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/B/ab.dat.copy new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/a.dat.copy b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/copy/A/a.dat.copy new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/dolor.txt b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/dolor.txt new file mode 100644 index 0000000..658bec6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/dolor.txt @@ -0,0 +1,2 @@ +dolor sit amet +DOLOR SIT AMET \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/ipsum.txt b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/ipsum.txt new file mode 100644 index 0000000..c7f392d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/ipsum.txt @@ -0,0 +1,2 @@ +ipsum dolor sit amet +IPSUM DOLOR SIT AMET \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/lorem.txt b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/lorem.txt new file mode 100644 index 0000000..2991a2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/lorem.txt @@ -0,0 +1,2 @@ +lorem ipsum dolor sit amet +LOREM IPSUM DOLOR SIT AMET \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/one/a b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/one/a new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/one/b/c.neon b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/one/b/c.neon new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/one/b/d.neon b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/one/b/d.neon new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/with space/foo.txt b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Fixtures/with space/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/CustomFilterIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/CustomFilterIteratorTest.php new file mode 100644 index 0000000..62629b1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/CustomFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\CustomFilterIterator; + +class CustomFilterIteratorTest extends IteratorTestCase +{ + /** + * @expectedException \InvalidArgumentException + */ + public function testWithInvalidFilter() + { + new CustomFilterIterator(new Iterator(), array('foo')); + } + + /** + * @dataProvider getAcceptData + */ + public function testAccept($filters, $expected) + { + $inner = new Iterator(array('test.php', 'test.py', 'foo.php')); + + $iterator = new CustomFilterIterator($inner, $filters); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + return array( + array(array(function (\SplFileInfo $fileinfo) { return false; }), array()), + array(array(function (\SplFileInfo $fileinfo) { return preg_match('/^test/', $fileinfo) > 0; }), array('test.php', 'test.py')), + array(array('is_dir'), array()), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/DateRangeFilterIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/DateRangeFilterIteratorTest.php new file mode 100644 index 0000000..18896d5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/DateRangeFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\DateRangeFilterIterator; +use Symfony\Component\Finder\Comparator\DateComparator; + +class DateRangeFilterIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($size, $expected) + { + $inner = new Iterator(self::$files); + + $iterator = new DateRangeFilterIterator($inner, $size); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + $since20YearsAgo = array( + '.git', + 'test.py', + 'foo', + 'foo/bar.tmp', + 'test.php', + 'toto', + '.bar', + '.foo', + '.foo/.bar', + 'foo bar', + '.foo/bar', + ); + + $since2MonthsAgo = array( + '.git', + 'test.py', + 'foo', + 'toto', + '.bar', + '.foo', + '.foo/.bar', + 'foo bar', + '.foo/bar', + ); + + $untilLastMonth = array( + '.git', + 'foo', + 'foo/bar.tmp', + 'test.php', + 'toto', + '.foo', + ); + + return array( + array(array(new DateComparator('since 20 years ago')), $this->toAbsolute($since20YearsAgo)), + array(array(new DateComparator('since 2 months ago')), $this->toAbsolute($since2MonthsAgo)), + array(array(new DateComparator('until last month')), $this->toAbsolute($untilLastMonth)), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/DepthRangeFilterIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/DepthRangeFilterIteratorTest.php new file mode 100644 index 0000000..be06f1c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/DepthRangeFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator; + +class DepthRangeFilterIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($minDepth, $maxDepth, $expected) + { + $inner = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->toAbsolute(), \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST); + + $iterator = new DepthRangeFilterIterator($inner, $minDepth, $maxDepth); + + $actual = array_keys(iterator_to_array($iterator)); + sort($expected); + sort($actual); + $this->assertEquals($expected, $actual); + } + + public function getAcceptData() + { + $lessThan1 = array( + '.git', + 'test.py', + 'foo', + 'test.php', + 'toto', + '.foo', + '.bar', + 'foo bar', + ); + + $lessThanOrEqualTo1 = array( + '.git', + 'test.py', + 'foo', + 'foo/bar.tmp', + 'test.php', + 'toto', + '.foo', + '.foo/.bar', + '.bar', + 'foo bar', + '.foo/bar', + ); + + $graterThanOrEqualTo1 = array( + 'foo/bar.tmp', + '.foo/.bar', + '.foo/bar', + ); + + $equalTo1 = array( + 'foo/bar.tmp', + '.foo/.bar', + '.foo/bar', + ); + + return array( + array(0, 0, $this->toAbsolute($lessThan1)), + array(0, 1, $this->toAbsolute($lessThanOrEqualTo1)), + array(2, PHP_INT_MAX, array()), + array(1, PHP_INT_MAX, $this->toAbsolute($graterThanOrEqualTo1)), + array(1, 1, $this->toAbsolute($equalTo1)), + ); + } + +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php new file mode 100644 index 0000000..e299fe7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator; +use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator; + +class ExcludeDirectoryFilterIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($directories, $expected) + { + $inner = new \RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->toAbsolute(), \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST); + + $iterator = new ExcludeDirectoryFilterIterator($inner, $directories); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + $foo = array( + '.bar', + '.foo', + '.foo/.bar', + '.foo/bar', + '.git', + 'test.py', + 'test.php', + 'toto', + 'foo bar' + ); + + $fo = array( + '.bar', + '.foo', + '.foo/.bar', + '.foo/bar', + '.git', + 'test.py', + 'foo', + 'foo/bar.tmp', + 'test.php', + 'toto', + 'foo bar' + ); + + return array( + array(array('foo'), $this->toAbsolute($foo)), + array(array('fo'), $this->toAbsolute($fo)), + ); + } + +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilePathsIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilePathsIteratorTest.php new file mode 100644 index 0000000..61f0e9b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilePathsIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\FilePathsIterator; + +class FilePathsIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getSubPathData + */ + public function testSubPath($baseDir, array $paths, array $subPaths, array $subPathnames) + { + $iterator = new FilePathsIterator($paths, $baseDir); + + foreach ($iterator as $index => $file) { + $this->assertEquals($paths[$index], $file->getPathname()); + $this->assertEquals($subPaths[$index], $iterator->getSubPath()); + $this->assertEquals($subPathnames[$index], $iterator->getSubPathname()); + } + } + + public function getSubPathData() + { + $tmpDir = sys_get_temp_dir().'/symfony2_finder'; + + return array( + array( + $tmpDir, + array( // paths + $tmpDir.DIRECTORY_SEPARATOR.'.git' => $tmpDir.DIRECTORY_SEPARATOR.'.git', + $tmpDir.DIRECTORY_SEPARATOR.'test.py' => $tmpDir.DIRECTORY_SEPARATOR.'test.py', + $tmpDir.DIRECTORY_SEPARATOR.'foo' => $tmpDir.DIRECTORY_SEPARATOR.'foo', + $tmpDir.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'bar.tmp' => $tmpDir.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'bar.tmp', + $tmpDir.DIRECTORY_SEPARATOR.'test.php' => $tmpDir.DIRECTORY_SEPARATOR.'test.php', + $tmpDir.DIRECTORY_SEPARATOR.'toto' => $tmpDir.DIRECTORY_SEPARATOR.'toto' + ), + array( // subPaths + $tmpDir.DIRECTORY_SEPARATOR.'.git' => '', + $tmpDir.DIRECTORY_SEPARATOR.'test.py' => '', + $tmpDir.DIRECTORY_SEPARATOR.'foo' => '', + $tmpDir.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'bar.tmp' => 'foo', + $tmpDir.DIRECTORY_SEPARATOR.'test.php' => '', + $tmpDir.DIRECTORY_SEPARATOR.'toto' => '' + ), + array( // subPathnames + $tmpDir.DIRECTORY_SEPARATOR.'.git' => '.git', + $tmpDir.DIRECTORY_SEPARATOR.'test.py' => 'test.py', + $tmpDir.DIRECTORY_SEPARATOR.'foo' => 'foo', + $tmpDir.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'bar.tmp' => 'foo'.DIRECTORY_SEPARATOR.'bar.tmp', + $tmpDir.DIRECTORY_SEPARATOR.'test.php' => 'test.php', + $tmpDir.DIRECTORY_SEPARATOR.'toto' => 'toto' + ), + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FileTypeFilterIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FileTypeFilterIteratorTest.php new file mode 100644 index 0000000..b2432ca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FileTypeFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\FileTypeFilterIterator; + +class FileTypeFilterIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($mode, $expected) + { + $inner = new InnerTypeIterator(self::$files); + + $iterator = new FileTypeFilterIterator($inner, $mode); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + $onlyFiles = array( + 'test.py', + 'foo/bar.tmp', + 'test.php', + '.bar', + '.foo/.bar', + '.foo/bar', + 'foo bar', + ); + + $onlyDirectories = array( + '.git', + 'foo', + 'toto', + '.foo', + ); + + return array( + array(FileTypeFilterIterator::ONLY_FILES, $this->toAbsolute($onlyFiles)), + array(FileTypeFilterIterator::ONLY_DIRECTORIES, $this->toAbsolute($onlyDirectories)), + ); + } +} + +class InnerTypeIterator extends \ArrayIterator +{ + public function current() + { + return new \SplFileInfo(parent::current()); + } + + public function isFile() + { + return $this->current()->isFile(); + } + + public function isDir() + { + return $this->current()->isDir(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilecontentFilterIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilecontentFilterIteratorTest.php new file mode 100644 index 0000000..5f2b398 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilecontentFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\FilecontentFilterIterator; +use Symfony\Component\Finder\Tests\Iterator\MockSplFileInfo; +use Symfony\Component\Finder\Tests\Iterator\MockFileListIterator; + +class FilecontentFilterIteratorTest extends IteratorTestCase +{ + + public function testAccept() + { + $inner = new MockFileListIterator(array('test.txt')); + $iterator = new FilecontentFilterIterator($inner, array(), array()); + $this->assertIterator(array('test.txt'), $iterator); + } + + public function testDirectory() + { + $inner = new MockFileListIterator(array('directory')); + $iterator = new FilecontentFilterIterator($inner, array('directory'), array()); + $this->assertIterator(array(), $iterator); + } + + public function testUnreadableFile() + { + $inner = new MockFileListIterator(array('file r-')); + $iterator = new FilecontentFilterIterator($inner, array('file r-'), array()); + $this->assertIterator(array(), $iterator); + } + + /** + * @dataProvider getTestFilterData + */ + public function testFilter(\Iterator $inner, array $matchPatterns, array $noMatchPatterns, array $resultArray) + { + $iterator = new FilecontentFilterIterator($inner, $matchPatterns, $noMatchPatterns); + $this->assertIterator($resultArray, $iterator); + } + + public function getTestFilterData() + { + $inner = new MockFileListIterator(); + + $inner[] = new MockSplFileInfo(array( + 'name' => 'a.txt', + 'contents' => 'Lorem ipsum...', + 'type' => 'file', + 'mode' => 'r+') + ); + + $inner[] = new MockSplFileInfo(array( + 'name' => 'b.yml', + 'contents' => 'dolor sit...', + 'type' => 'file', + 'mode' => 'r+') + ); + + $inner[] = new MockSplFileInfo(array( + 'name' => 'some/other/dir/third.php', + 'contents' => 'amet...', + 'type' => 'file', + 'mode' => 'r+') + ); + + $inner[] = new MockSplFileInfo(array( + 'name' => 'unreadable-file.txt', + 'contents' => false, + 'type' => 'file', + 'mode' => 'r+') + ); + + return array( + array($inner, array('.'), array(), array('a.txt', 'b.yml', 'some/other/dir/third.php')), + array($inner, array('ipsum'), array(), array('a.txt')), + array($inner, array('i', 'amet'), array('Lorem', 'amet'), array('b.yml')), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilenameFilterIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilenameFilterIteratorTest.php new file mode 100644 index 0000000..c4b9795 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilenameFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\FilenameFilterIterator; + +class FilenameFilterIteratorTest extends IteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($matchPatterns, $noMatchPatterns, $expected) + { + $inner = new InnerNameIterator(array('test.php', 'test.py', 'foo.php')); + + $iterator = new FilenameFilterIterator($inner, $matchPatterns, $noMatchPatterns); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + return array( + array(array('test.*'), array(), array('test.php', 'test.py')), + array(array(), array('test.*'), array('foo.php')), + array(array('*.php'), array('test.*'), array('foo.php')), + array(array('*.php', '*.py'), array('foo.*'), array('test.php', 'test.py')), + array(array('/\.php$/'), array(), array('test.php', 'foo.php')), + array(array(), array('/\.php$/'), array('test.py')), + ); + } +} + +class InnerNameIterator extends \ArrayIterator +{ + public function current() + { + return new \SplFileInfo(parent::current()); + } + + public function getFilename() + { + return parent::current(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilterIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilterIteratorTest.php new file mode 100644 index 0000000..029a266 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/FilterIteratorTest.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\Finder\Tests\Iterator; + +/** + * @author Alex Bogomazov + */ +class FilterIteratorTest extends RealIteratorTestCase +{ + public function testFilterFilesystemIterators() + { + $i = new \FilesystemIterator($this->toAbsolute()); + + // it is expected that there are test.py test.php in the tmpDir + $i = $this->getMockForAbstractClass('Symfony\Component\Finder\Iterator\FilterIterator', array($i)); + $i->expects($this->any()) + ->method('accept') + ->will($this->returnCallback(function () use ($i) { + return (bool) preg_match('/\.php/', (string) $i->current()); + }) + ); + + $c = 0; + foreach ($i as $item) { + $c++; + } + + $this->assertEquals(1, $c); + + $i->rewind(); + + $c = 0; + foreach ($i as $item) { + $c++; + } + + // This would fail with \FilterIterator but works with Symfony\Component\Finder\Iterator\FilterIterator + // see https://bugs.php.net/bug.php?id=49104 + $this->assertEquals(1, $c); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/Iterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/Iterator.php new file mode 100644 index 0000000..7ffd382 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/Iterator.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\Finder\Tests\Iterator; + +class Iterator implements \Iterator +{ + protected $values; + + public function __construct(array $values = array()) + { + $this->values = array(); + foreach ($values as $value) { + $this->attach(new \SplFileInfo($value)); + } + $this->rewind(); + } + + public function attach(\SplFileInfo $fileinfo) + { + $this->values[] = $fileinfo; + } + + public function rewind() + { + reset($this->values); + } + + public function valid() + { + return false !== $this->current(); + } + + public function next() + { + next($this->values); + } + + public function current() + { + return current($this->values); + } + + public function key() + { + return key($this->values); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/IteratorTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/IteratorTestCase.php new file mode 100644 index 0000000..504cfb0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/IteratorTestCase.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\Finder\Tests\Iterator; + +abstract class IteratorTestCase extends \PHPUnit_Framework_TestCase +{ + protected function assertIterator($expected, \Traversable $iterator) + { + // set iterator_to_array $use_key to false to avoid values merge + // this made FinderTest::testAppendWithAnArray() failed with GnuFinderAdapter + $values = array_map(function (\SplFileInfo $fileinfo) { return str_replace('/', DIRECTORY_SEPARATOR, $fileinfo->getPathname()); }, iterator_to_array($iterator, false)); + + $expected = array_map(function ($path) { return str_replace('/', DIRECTORY_SEPARATOR, $path); }, $expected); + + sort($values); + sort($expected); + + $this->assertEquals($expected, array_values($values)); + } + + protected function assertOrderedIterator($expected, \Traversable $iterator) + { + $values = array_map(function (\SplFileInfo $fileinfo) { return $fileinfo->getPathname(); }, iterator_to_array($iterator)); + + $this->assertEquals($expected, array_values($values)); + } + + /** + * Same as IteratorTestCase::assertIterator with foreach usage + * + * @param array $expected + * @param \Traversable $iterator + */ + protected function assertIteratorInForeach($expected, \Traversable $iterator) + { + $values = array(); + foreach ($iterator as $file) { + $this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file); + $values[] = $file->getPathname(); + } + + sort($values); + sort($expected); + + $this->assertEquals($expected, array_values($values)); + } + + /** + * Same as IteratorTestCase::assertOrderedIterator with foreach usage + * + * @param array $expected + * @param \Traversable $iterator + */ + protected function assertOrderedIteratorInForeach($expected, \Traversable $iterator) + { + $values = array(); + foreach ($iterator as $file) { + $this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file); + $values[] = $file->getPathname(); + } + + $this->assertEquals($expected, array_values($values)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/MockFileListIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/MockFileListIterator.php new file mode 100644 index 0000000..6d8ae39 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/MockFileListIterator.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\Finder\Tests\Iterator; + +class MockFileListIterator extends \ArrayIterator +{ + public function __construct(array $filesArray = array()) + { + $files = array_map(function($file){ return new MockSplFileInfo($file); }, $filesArray); + parent::__construct($files); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/MockSplFileInfo.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/MockSplFileInfo.php new file mode 100644 index 0000000..a50e4c9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/MockSplFileInfo.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\Finder\Tests\Iterator; + +class MockSplFileInfo extends \SplFileInfo +{ + const TYPE_DIRECTORY = 1; + const TYPE_FILE = 2; + const TYPE_UNKNOWN = 3; + + private $contents = null; + private $mode = null; + private $type = null; + private $relativePath = null; + private $relativePathname = null; + + public function __construct($param) + { + if (is_string($param)) { + parent::__construct($param); + } elseif (is_array($param)) { + $defaults = array( + 'name' => 'file.txt', + 'contents' => null, + 'mode' => null, + 'type' => null, + 'relativePath' => null, + 'relativePathname' => null, + ); + $defaults = array_merge($defaults, $param); + parent::__construct($defaults['name']); + $this->setContents($defaults['contents']); + $this->setMode($defaults['mode']); + $this->setType($defaults['type']); + $this->setRelativePath($defaults['relativePath']); + $this->setRelativePathname($defaults['relativePathname']); + } else { + throw new \RuntimeException(sprintf('Incorrect parameter "%s"', $param)); + } + } + + public function isFile() + { + if ($this->type === null) { + return preg_match('/file/', $this->getFilename()); + }; + + return self::TYPE_FILE === $this->type; + } + + public function isDir() + { + if ($this->type === null) { + return preg_match('/directory/', $this->getFilename()); + } + + return self::TYPE_DIRECTORY === $this->type; + } + + public function isReadable() + { + if ($this->mode === null) { + return preg_match('/r\+/', $this->getFilename()); + } + + return preg_match('/r\+/', $this->mode); + } + + public function getContents() + { + return $this->contents; + } + + public function setContents($contents) + { + $this->contents = $contents; + } + + public function setMode($mode) + { + $this->mode = $mode; + } + + public function setType($type) + { + if (is_string($type)) { + switch ($type) { + case 'directory': + $this->type = self::TYPE_DIRECTORY; + case 'd': + $this->type = self::TYPE_DIRECTORY; + break; + case 'file': + $this->type = self::TYPE_FILE; + case 'f': + $this->type = self::TYPE_FILE; + break; + default: + $this->type = self::TYPE_UNKNOWN; + } + } else { + $this->type = $type; + } + } + + public function setRelativePath($relativePath) + { + $this->relativePath = $relativePath; + } + + public function setRelativePathname($relativePathname) + { + $this->relativePathname = $relativePathname; + } + + public function getRelativePath() + { + return $this->relativePath; + } + + public function getRelativePathname() + { + return $this->relativePathname; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php new file mode 100644 index 0000000..89d8edb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/MultiplePcreFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\MultiplePcreFilterIterator; + +class MultiplePcreFilterIteratorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getIsRegexFixtures + */ + public function testIsRegex($string, $isRegex, $message) + { + $testIterator = new TestMultiplePcreFilterIterator(); + $this->assertEquals($isRegex, $testIterator->isRegex($string), $message); + } + + public function getIsRegexFixtures() + { + return array( + array('foo', false, 'string'), + array(' foo ', false, '" " is not a valid delimiter'), + array('\\foo\\', false, '"\\" is not a valid delimiter'), + array('afooa', false, '"a" is not a valid delimiter'), + array('//', false, 'the pattern should contain at least 1 character'), + array('/a/', true, 'valid regex'), + array('/foo/', true, 'valid regex'), + array('/foo/i', true, 'valid regex with a single modifier'), + array('/foo/imsxu', true, 'valid regex with multiple modifiers'), + array('#foo#', true, '"#" is a valid delimiter'), + array('{foo}', true, '"{,}" is a valid delimiter pair'), + array('*foo.*', false, '"*" is not considered as a valid delimiter'), + array('?foo.?', false, '"?" is not considered as a valid delimiter'), + ); + } +} + +class TestMultiplePcreFilterIterator extends MultiplePcreFilterIterator +{ + public function __construct() + { + } + + public function accept() + { + throw new \BadFunctionCallException('Not implemented'); + } + + public function isRegex($str) + { + return parent::isRegex($str); + } + + public function toRegex($str) + { + throw new \BadFunctionCallException('Not implemented'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/PathFilterIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/PathFilterIteratorTest.php new file mode 100644 index 0000000..6f83e44 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/PathFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\PathFilterIterator; + +class PathFilterIteratorTest extends IteratorTestCase +{ + + /** + * @dataProvider getTestFilterData + */ + public function testFilter(\Iterator $inner, array $matchPatterns, array $noMatchPatterns, array $resultArray) + { + $iterator = new PathFilterIterator($inner, $matchPatterns, $noMatchPatterns); + $this->assertIterator($resultArray, $iterator); + } + + public function getTestFilterData() + { + $inner = new MockFileListIterator(); + + //PATH: A/B/C/abc.dat + $inner[] = new MockSplFileInfo(array( + 'name' => 'abc.dat', + 'relativePathname' => 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat', + )); + + //PATH: A/B/ab.dat + $inner[] = new MockSplFileInfo(array( + 'name' => 'ab.dat', + 'relativePathname' => 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat', + )); + + //PATH: A/a.dat + $inner[] = new MockSplFileInfo(array( + 'name' => 'a.dat', + 'relativePathname' => 'A'.DIRECTORY_SEPARATOR.'a.dat', + )); + + //PATH: copy/A/B/C/abc.dat.copy + $inner[] = new MockSplFileInfo(array( + 'name' => 'abc.dat.copy', + 'relativePathname' => 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat', + )); + + //PATH: copy/A/B/ab.dat.copy + $inner[] = new MockSplFileInfo(array( + 'name' => 'ab.dat.copy', + 'relativePathname' => 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat', + )); + + //PATH: copy/A/a.dat.copy + $inner[] = new MockSplFileInfo(array( + 'name' => 'a.dat.copy', + 'relativePathname' => 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'a.dat', + )); + + return array( + array($inner, array('/^A/'), array(), array('abc.dat', 'ab.dat', 'a.dat')), + array($inner, array('/^A\/B/'), array(), array('abc.dat', 'ab.dat')), + array($inner, array('/^A\/B\/C/'), array(), array('abc.dat')), + array($inner, array('/A\/B\/C/'), array(), array('abc.dat', 'abc.dat.copy')), + + array($inner, array('A'), array(), array('abc.dat', 'ab.dat', 'a.dat', 'abc.dat.copy', 'ab.dat.copy', 'a.dat.copy')), + array($inner, array('A/B'), array(), array('abc.dat', 'ab.dat', 'abc.dat.copy', 'ab.dat.copy')), + array($inner, array('A/B/C'), array(), array('abc.dat', 'abc.dat.copy')), + + array($inner, array('copy/A'), array(), array('abc.dat.copy', 'ab.dat.copy', 'a.dat.copy')), + array($inner, array('copy/A/B'), array(), array('abc.dat.copy', 'ab.dat.copy')), + array($inner, array('copy/A/B/C'), array(), array('abc.dat.copy')), + + ); + } + +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/RealIteratorTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/RealIteratorTestCase.php new file mode 100644 index 0000000..f044c34 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/RealIteratorTestCase.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\Finder\Tests\Iterator; + +abstract class RealIteratorTestCase extends IteratorTestCase +{ + + protected static $tmpDir; + protected static $files; + + public static function setUpBeforeClass() + { + self::$tmpDir = realpath(sys_get_temp_dir()).DIRECTORY_SEPARATOR.'symfony2_finder'; + + self::$files = array( + '.git/', + '.foo/', + '.foo/.bar', + '.foo/bar', + '.bar', + 'test.py', + 'foo/', + 'foo/bar.tmp', + 'test.php', + 'toto/', + 'foo bar' + ); + + self::$files = self::toAbsolute(self::$files); + + if (is_dir(self::$tmpDir)) { + self::tearDownAfterClass(); + } else { + mkdir(self::$tmpDir); + } + + foreach (self::$files as $file) { + if (DIRECTORY_SEPARATOR === $file[strlen($file) - 1]) { + mkdir($file); + } else { + touch($file); + } + } + + file_put_contents(self::toAbsolute('test.php'), str_repeat(' ', 800)); + file_put_contents(self::toAbsolute('test.py'), str_repeat(' ', 2000)); + + touch(self::toAbsolute('foo/bar.tmp'), strtotime('2005-10-15')); + touch(self::toAbsolute('test.php'), strtotime('2005-10-15')); + } + + public static function tearDownAfterClass() + { + foreach (array_reverse(self::$files) as $file) { + if (DIRECTORY_SEPARATOR === $file[strlen($file) - 1]) { + @rmdir($file); + } else { + @unlink($file); + } + } + } + + protected static function toAbsolute($files = null) + { + /* + * Without the call to setUpBeforeClass() property can be null. + */ + if (!self::$tmpDir) { + self::$tmpDir = realpath(sys_get_temp_dir()).DIRECTORY_SEPARATOR.'symfony2_finder'; + } + + if (is_array($files)) { + $f = array(); + foreach ($files as $file) { + $f[] = self::$tmpDir.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $file); + } + + return $f; + } + + if (is_string($files)) { + + return self::$tmpDir.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $files); + } + + return self::$tmpDir; + } + + protected static function toAbsoluteFixtures($files) + { + $f = array(); + foreach ($files as $file) { + $f[] = realpath(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.$file); + } + + return $f; + } + +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php new file mode 100644 index 0000000..f762514 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator; + +class RecursiveDirectoryIteratorTest extends IteratorTestCase +{ + /** + * @dataProvider getPaths + * + * @param string $path + * @param Boolean $seekable + * @param Boolean $supports + * @param string $message + */ + public function testRewind($path, $seekable, $contains, $message = null) + { + try { + $i = new RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); + } catch (\UnexpectedValueException $e) { + $this->markTestSkipped(sprintf('Unsupported stream "%s".', $path)); + } + + $i->rewind(); + + $this->assertTrue(true, $message); + } + + /** + * @dataProvider getPaths + * + * @param string $path + * @param Boolean $seekable + * @param Boolean $supports + * @param string $message + */ + public function testSeek($path, $seekable, $contains, $message = null) + { + try { + $i = new RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); + } catch (\UnexpectedValueException $e) { + $this->markTestSkipped(sprintf('Unsupported stream "%s".', $path)); + } + + $actual = array(); + + $i->seek(0); + $actual[] = $i->getPathname(); + + $i->seek(1); + $actual[] = $i->getPathname(); + + $i->seek(2); + $actual[] = $i->getPathname(); + + $this->assertEquals($contains, $actual); + } + + public function getPaths() + { + $data = array(); + + // ftp + $contains = array( + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'README', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'index.html', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'pub', + ); + $data[] = array('ftp://ftp.mozilla.org/', false, $contains); + + return $data; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/SizeRangeFilterIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/SizeRangeFilterIteratorTest.php new file mode 100644 index 0000000..726df9e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/SizeRangeFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator; +use Symfony\Component\Finder\Comparator\NumberComparator; + +class SizeRangeFilterIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($size, $expected) + { + $inner = new InnerSizeIterator(self::$files); + + $iterator = new SizeRangeFilterIterator($inner, $size); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + $lessThan1KGreaterThan05K = array( + '.foo', + '.git', + 'foo', + 'test.php', + 'toto', + ); + + return array( + array(array(new NumberComparator('< 1K'), new NumberComparator('> 0.5K')), $this->toAbsolute($lessThan1KGreaterThan05K)), + ); + } +} + +class InnerSizeIterator extends \ArrayIterator +{ + public function current() + { + return new \SplFileInfo(parent::current()); + } + + public function getFilename() + { + return parent::current(); + } + + public function isFile() + { + return $this->current()->isFile(); + } + + public function getSize() + { + return $this->current()->getSize(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/SortableIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/SortableIteratorTest.php new file mode 100644 index 0000000..64e1e3e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/Tests/Iterator/SortableIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\SortableIterator; + +class SortableIteratorTest extends RealIteratorTestCase +{ + public function testConstructor() + { + try { + new SortableIterator(new Iterator(array()), 'foobar'); + $this->fail('__construct() throws an \InvalidArgumentException exception if the mode is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException exception if the mode is not valid'); + } + } + + /** + * @dataProvider getAcceptData + */ + public function testAccept($mode, $expected) + { + $inner = new Iterator(self::$files); + + $iterator = new SortableIterator($inner, $mode); + + $this->assertOrderedIterator($expected, $iterator); + } + + public function getAcceptData() + { + + $sortByName = array( + '.bar', + '.foo', + '.foo/.bar', + '.foo/bar', + '.git', + 'foo', + 'foo bar', + 'foo/bar.tmp', + 'test.php', + 'test.py', + 'toto', + ); + + $sortByType = array( + '.foo', + '.git', + 'foo', + 'toto', + '.bar', + '.foo/.bar', + '.foo/bar', + 'foo bar', + 'foo/bar.tmp', + 'test.php', + 'test.py', + ); + + $customComparison = array( + '.bar', + '.foo', + '.foo/.bar', + '.foo/bar', + '.git', + 'foo', + 'foo bar', + 'foo/bar.tmp', + 'test.php', + 'test.py', + 'toto', + ); + + return array( + array(SortableIterator::SORT_BY_NAME, $this->toAbsolute($sortByName)), + array(SortableIterator::SORT_BY_TYPE, $this->toAbsolute($sortByType)), + array(function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealpath(), $b->getRealpath()); }, $this->toAbsolute($customComparison)), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/composer.json b/vendor/symfony/symfony/src/Symfony/Component/Finder/composer.json new file mode 100644 index 0000000..7480b3e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/finder", + "type": "library", + "description": "Symfony Finder Component", + "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.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Finder\\": "" } + }, + "target-dir": "Symfony/Component/Finder", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Finder/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/Finder/phpunit.xml.dist new file mode 100644 index 0000000..2327223 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Finder/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractExtension.php new file mode 100644 index 0000000..4db77b9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractExtension.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\Form; + +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * @author Bernhard Schussek + */ +abstract class AbstractExtension implements FormExtensionInterface +{ + /** + * The types provided by this extension + * @var FormTypeInterface[] An array of FormTypeInterface + */ + private $types; + + /** + * The type extensions provided by this extension + * @var FormTypeExtensionInterface[] An array of FormTypeExtensionInterface + */ + private $typeExtensions; + + /** + * The type guesser provided by this extension + * @var FormTypeGuesserInterface + */ + private $typeGuesser; + + /** + * Whether the type guesser has been loaded + * @var Boolean + */ + private $typeGuesserLoaded = false; + + /** + * {@inheritdoc} + */ + public function getType($name) + { + if (null === $this->types) { + $this->initTypes(); + } + + 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) + { + if (null === $this->types) { + $this->initTypes(); + } + + return isset($this->types[$name]); + } + + /** + * {@inheritdoc} + */ + public function getTypeExtensions($name) + { + if (null === $this->typeExtensions) { + $this->initTypeExtensions(); + } + + return isset($this->typeExtensions[$name]) + ? $this->typeExtensions[$name] + : array(); + } + + /** + * {@inheritdoc} + */ + public function hasTypeExtensions($name) + { + if (null === $this->typeExtensions) { + $this->initTypeExtensions(); + } + + return isset($this->typeExtensions[$name]) && count($this->typeExtensions[$name]) > 0; + } + + /** + * {@inheritdoc} + */ + public function getTypeGuesser() + { + if (!$this->typeGuesserLoaded) { + $this->initTypeGuesser(); + } + + return $this->typeGuesser; + } + + /** + * Registers the types. + * + * @return FormTypeInterface[] An array of FormTypeInterface instances + */ + protected function loadTypes() + { + return array(); + } + + /** + * Registers the type extensions. + * + * @return FormTypeExtensionInterface[] An array of FormTypeExtensionInterface instances + */ + protected function loadTypeExtensions() + { + return array(); + } + + /** + * Registers the type guesser. + * + * @return FormTypeGuesserInterface|null A type guesser + */ + protected function loadTypeGuesser() + { + return null; + } + + /** + * Initializes the types. + * + * @throws UnexpectedTypeException if any registered type is not an instance of FormTypeInterface + */ + private function initTypes() + { + $this->types = array(); + + foreach ($this->loadTypes() as $type) { + if (!$type instanceof FormTypeInterface) { + throw new UnexpectedTypeException($type, 'Symfony\Component\Form\FormTypeInterface'); + } + + $this->types[$type->getName()] = $type; + } + } + + /** + * Initializes the type extensions. + * + * @throws UnexpectedTypeException if any registered type extension is not + * an instance of FormTypeExtensionInterface + */ + private function initTypeExtensions() + { + $this->typeExtensions = array(); + + foreach ($this->loadTypeExtensions() as $extension) { + if (!$extension instanceof FormTypeExtensionInterface) { + throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormTypeExtensionInterface'); + } + + $type = $extension->getExtendedType(); + + $this->typeExtensions[$type][] = $extension; + } + } + + /** + * Initializes the type guesser. + * + * @throws UnexpectedTypeException if the type guesser is not an instance of FormTypeGuesserInterface + */ + private function initTypeGuesser() + { + $this->typeGuesserLoaded = true; + + $this->typeGuesser = $this->loadTypeGuesser(); + if (null !== $this->typeGuesser && !$this->typeGuesser instanceof FormTypeGuesserInterface) { + throw new UnexpectedTypeException($this->typeGuesser, 'Symfony\Component\Form\FormTypeGuesserInterface'); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractRendererEngine.php b/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractRendererEngine.php new file mode 100644 index 0000000..347be10 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractRendererEngine.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\Form; + +/** + * Default implementation of {@link FormRendererEngineInterface}. + * + * @author Bernhard Schussek + */ +abstract class AbstractRendererEngine implements FormRendererEngineInterface +{ + /** + * The variable in {@link FormView} used as cache key. + */ + const CACHE_KEY_VAR = 'cache_key'; + + /** + * @var array + */ + protected $defaultThemes; + + /** + * @var array + */ + protected $themes = array(); + + /** + * @var array + */ + protected $resources = array(); + + /** + * @var array + */ + private $resourceHierarchyLevels = array(); + + /** + * Creates a new renderer engine. + * + * @param array $defaultThemes The default themes. The type of these + * themes is open to the implementation. + */ + public function __construct(array $defaultThemes = array()) + { + $this->defaultThemes = $defaultThemes; + } + + /** + * {@inheritdoc} + */ + public function setTheme(FormView $view, $themes) + { + $cacheKey = $view->vars[self::CACHE_KEY_VAR]; + + // Do not cast, as casting turns objects into arrays of properties + $this->themes[$cacheKey] = is_array($themes) ? $themes : array($themes); + + // Unset instead of resetting to an empty array, in order to allow + // implementations (like TwigRendererEngine) to check whether $cacheKey + // is set at all. + unset($this->resources[$cacheKey]); + unset($this->resourceHierarchyLevels[$cacheKey]); + } + + /** + * {@inheritdoc} + */ + public function getResourceForBlockName(FormView $view, $blockName) + { + $cacheKey = $view->vars[self::CACHE_KEY_VAR]; + + if (!isset($this->resources[$cacheKey][$blockName])) { + $this->loadResourceForBlockName($cacheKey, $view, $blockName); + } + + return $this->resources[$cacheKey][$blockName]; + } + + /** + * {@inheritdoc} + */ + public function getResourceForBlockNameHierarchy(FormView $view, array $blockNameHierarchy, $hierarchyLevel) + { + $cacheKey = $view->vars[self::CACHE_KEY_VAR]; + $blockName = $blockNameHierarchy[$hierarchyLevel]; + + if (!isset($this->resources[$cacheKey][$blockName])) { + $this->loadResourceForBlockNameHierarchy($cacheKey, $view, $blockNameHierarchy, $hierarchyLevel); + } + + return $this->resources[$cacheKey][$blockName]; + } + + /** + * {@inheritdoc} + */ + public function getResourceHierarchyLevel(FormView $view, array $blockNameHierarchy, $hierarchyLevel) + { + $cacheKey = $view->vars[self::CACHE_KEY_VAR]; + $blockName = $blockNameHierarchy[$hierarchyLevel]; + + if (!isset($this->resources[$cacheKey][$blockName])) { + $this->loadResourceForBlockNameHierarchy($cacheKey, $view, $blockNameHierarchy, $hierarchyLevel); + } + + // If $block was previously rendered loaded with loadTemplateForBlock(), the template + // is cached but the hierarchy level is not. In this case, we know that the block + // exists at this very hierarchy level, so we can just set it. + if (!isset($this->resourceHierarchyLevels[$cacheKey][$blockName])) { + $this->resourceHierarchyLevels[$cacheKey][$blockName] = $hierarchyLevel; + } + + return $this->resourceHierarchyLevels[$cacheKey][$blockName]; + } + + /** + * Loads the cache with the resource for a given block name. + * + * @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 Boolean True if the resource could be loaded, false otherwise. + */ + abstract protected function loadResourceForBlockName($cacheKey, FormView $view, $blockName); + + /** + * Loads the cache with the resource for a specific level of a block hierarchy. + * + * @see getResourceForBlockHierarchy() + * + * @param string $cacheKey The cache key used for storing the + * resource. + * @param FormView $view The form view for finding the applying + * themes. + * @param array $blockNameHierarchy The block hierarchy, with the most + * specific block name at the end. + * @param integer $hierarchyLevel The level in the block hierarchy that + * should be loaded. + * + * @return Boolean True if the resource could be loaded, false otherwise. + */ + private function loadResourceForBlockNameHierarchy($cacheKey, FormView $view, array $blockNameHierarchy, $hierarchyLevel) + { + $blockName = $blockNameHierarchy[$hierarchyLevel]; + + // Try to find a template for that block + if ($this->loadResourceForBlockName($cacheKey, $view, $blockName)) { + // If loadTemplateForBlock() returns true, it was able to populate the + // cache. The only missing thing is to set the hierarchy level at which + // the template was found. + $this->resourceHierarchyLevels[$cacheKey][$blockName] = $hierarchyLevel; + + return true; + } + + if ($hierarchyLevel > 0) { + $parentLevel = $hierarchyLevel - 1; + $parentBlockName = $blockNameHierarchy[$parentLevel]; + + // The next two if statements contain slightly duplicated code. This is by intention + // and tries to avoid execution of unnecessary checks in order to increase performance. + + if (isset($this->resources[$cacheKey][$parentBlockName])) { + // It may happen that the parent block is already loaded, but its level is not. + // In this case, the parent block must have been loaded by loadResourceForBlock(), + // which does not check the hierarchy of the block. Subsequently the block must have + // been found directly on the parent level. + if (!isset($this->resourceHierarchyLevels[$cacheKey][$parentBlockName])) { + $this->resourceHierarchyLevels[$cacheKey][$parentBlockName] = $parentLevel; + } + + // Cache the shortcuts for further accesses + $this->resources[$cacheKey][$blockName] = $this->resources[$cacheKey][$parentBlockName]; + $this->resourceHierarchyLevels[$cacheKey][$blockName] = $this->resourceHierarchyLevels[$cacheKey][$parentBlockName]; + + return true; + } + + if ($this->loadResourceForBlockNameHierarchy($cacheKey, $view, $blockNameHierarchy, $parentLevel)) { + // Cache the shortcuts for further accesses + $this->resources[$cacheKey][$blockName] = $this->resources[$cacheKey][$parentBlockName]; + $this->resourceHierarchyLevels[$cacheKey][$blockName] = $this->resourceHierarchyLevels[$cacheKey][$parentBlockName]; + + return true; + } + } + + // Cache the result for further accesses + $this->resources[$cacheKey][$blockName] = false; + $this->resourceHierarchyLevels[$cacheKey][$blockName] = false; + + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractType.php new file mode 100644 index 0000000..6f7f5da --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractType.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; + +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +abstract class AbstractType implements FormTypeInterface +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'form'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractTypeExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractTypeExtension.php new file mode 100644 index 0000000..351c800 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/AbstractTypeExtension.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; + +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +abstract class AbstractTypeExtension implements FormTypeExtensionInterface +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Button.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Button.php new file mode 100644 index 0000000..6e12ba1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Button.php @@ -0,0 +1,436 @@ + + * + * 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\AlreadySubmittedException; +use Symfony\Component\Form\Exception\BadMethodCallException; + +/** + * A form button. + * + * @author Bernhard Schussek + */ +class Button implements \IteratorAggregate, FormInterface +{ + /** + * @var FormInterface + */ + private $parent; + + /** + * @var FormConfigInterface + */ + private $config; + + /** + * @var Boolean + */ + private $submitted = false; + + /** + * Creates a new button from a form configuration. + * + * @param FormConfigInterface $config The button's configuration. + */ + public function __construct(FormConfigInterface $config) + { + $this->config = $config; + } + + /** + * Unsupported method. + * + * @param mixed $offset + * + * @return Boolean Always returns false. + */ + public function offsetExists($offset) + { + return false; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param mixed $offset + * + * @throws BadMethodCallException + */ + public function offsetGet($offset) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param mixed $offset + * @param mixed $value + * + * @throws BadMethodCallException + */ + public function offsetSet($offset, $value) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param mixed $offset + * + * @throws BadMethodCallException + */ + public function offsetUnset($offset) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * {@inheritdoc} + */ + public function setParent(FormInterface $parent = null) + { + $this->parent = $parent; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return $this->parent; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param int|string|FormInterface $child + * @param null $type + * @param array $options + * + * @throws BadMethodCallException + */ + public function add($child, $type = null, array $options = array()) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $name + * + * @throws BadMethodCallException + */ + public function get($name) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * @param string $name + * + * @return Boolean Always returns false. + */ + public function has($name) + { + return false; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $name + * + * @throws BadMethodCallException + */ + public function remove($name) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * {@inheritdoc} + */ + public function all() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getErrors() + { + return array(); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $modelData + * + * @throws BadMethodCallException + */ + public function setData($modelData) + { + throw new BadMethodCallException('Buttons cannot have data.'); + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getData() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getNormData() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getViewData() + { + return null; + } + + /** + * Unsupported method. + * + * @return array Always returns an empty array. + */ + public function getExtraData() + { + return array(); + } + + /** + * Returns the button's configuration. + * + * @return FormConfigInterface The configuration. + */ + public function getConfig() + { + return $this->config; + } + + /** + * Returns whether the button is submitted. + * + * @return Boolean true if the button was submitted. + */ + public function isSubmitted() + { + return $this->submitted; + } + + /** + * Returns the name by which the button is identified in forms. + * + * @return string The name of the button. + */ + public function getName() + { + return $this->config->getName(); + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getPropertyPath() + { + return null; + } + + /** + * Unsupported method. + * + * @param FormError $error + * + * @throws BadMethodCallException + */ + public function addError(FormError $error) + { + throw new BadMethodCallException('Buttons cannot have errors.'); + } + + /** + * Unsupported method. + * + * @return Boolean Always returns true. + */ + public function isValid() + { + return true; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function isRequired() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isDisabled() + { + return $this->config->getDisabled(); + } + + /** + * Unsupported method. + * + * @return Boolean Always returns true. + */ + public function isEmpty() + { + return true; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns true. + */ + public function isSynchronized() + { + return true; + } + + /** + * Unsupported method. + * + * @throws BadMethodCallException + */ + public function initialize() + { + throw new BadMethodCallException('Buttons cannot be initialized. Call initialize() on the root form instead.'); + } + + /** + * Unsupported method. + * + * @param mixed $request + * + * @throws BadMethodCallException + */ + public function handleRequest($request = null) + { + throw new BadMethodCallException('Buttons cannot handle requests. Call handleRequest() on the root form instead.'); + } + + /** + * Submits data to the button. + * + * @param null|string $submittedData The data. + * @param Boolean $clearMissing Not used. + * + * @return Button The button instance + * + * @throws Exception\AlreadySubmittedException If the button has already been submitted. + */ + public function submit($submittedData, $clearMissing = true) + { + if ($this->submitted) { + throw new AlreadySubmittedException('A form can only be submitted once'); + } + + $this->submitted = true; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getRoot() + { + return $this->parent ? $this->parent->getRoot() : $this; + } + + /** + * {@inheritdoc} + */ + public function isRoot() + { + return null === $this->parent; + } + + /** + * {@inheritdoc} + */ + public function createView(FormView $parent = null) + { + if (null === $parent && $this->parent) { + $parent = $this->parent->createView(); + } + + return $this->config->getType()->createView($this, $parent); + } + + /** + * Unsupported method. + * + * @return integer Always returns 0. + */ + public function count() + { + return 0; + } + + /** + * Unsupported method. + * + * @return \EmptyIterator Always returns an empty iterator. + */ + public function getIterator() + { + return new \EmptyIterator(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/ButtonBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Form/ButtonBuilder.php new file mode 100644 index 0000000..2a19d58 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/ButtonBuilder.php @@ -0,0 +1,864 @@ + + * + * 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\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Exception\BadMethodCallException; + +/** + * A builder for {@link Button} instances. + * + * @author Bernhard Schussek + */ +class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface +{ + /** + * @var Boolean + */ + protected $locked = false; + + /** + * @var Boolean + */ + private $disabled; + + /** + * @var ResolvedFormTypeInterface + */ + private $type; + + /** + * @var string + */ + private $name; + + /** + * @var array + */ + private $attributes = array(); + + /** + * @var array + */ + private $options; + + /** + * Creates a new button builder. + * + * @param string $name The name of the button. + * @param array $options The button's options. + * + * @throws InvalidArgumentException If the name is empty. + */ + public function __construct($name, array $options) + { + if (empty($name) && 0 != $name) { + throw new InvalidArgumentException('Buttons cannot have empty names.'); + } + + $this->name = (string) $name; + $this->options = $options; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string|integer|FormBuilderInterface $child + * @param string|FormTypeInterface $type + * @param array $options + * + * @throws BadMethodCallException + */ + public function add($child, $type = null, array $options = array()) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $name + * @param string|FormTypeInterface $type + * @param array $options + * + * @throws BadMethodCallException + */ + public function create($name, $type = null, array $options = array()) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $name + * + * @throws BadMethodCallException + */ + public function get($name) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $name + * + * @throws BadMethodCallException + */ + public function remove($name) + { + throw new BadMethodCallException('Buttons cannot have children.'); + } + + /** + * Unsupported method. + * + * @param string $name + * + * @return Boolean Always returns false. + */ + public function has($name) + { + return false; + } + + /** + * Returns the children. + * + * @return array Always returns an empty array. + */ + public function all() + { + return array(); + } + + /** + * Creates the button. + * + * @return Button The button + */ + public function getForm() + { + return new Button($this->getFormConfig()); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param string $eventName + * @param callable $listener + * @param integer $priority + * + * @throws BadMethodCallException + */ + public function addEventListener($eventName, $listener, $priority = 0) + { + throw new BadMethodCallException('Buttons do not support event listeners.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param EventSubscriberInterface $subscriber + * + * @throws BadMethodCallException + */ + public function addEventSubscriber(EventSubscriberInterface $subscriber) + { + throw new BadMethodCallException('Buttons do not support event subscribers.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param DataTransformerInterface $viewTransformer + * @param Boolean $forcePrepend + * + * @throws BadMethodCallException + */ + public function addViewTransformer(DataTransformerInterface $viewTransformer, $forcePrepend = false) + { + throw new BadMethodCallException('Buttons do not support data transformers.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @throws BadMethodCallException + */ + public function resetViewTransformers() + { + throw new BadMethodCallException('Buttons do not support data transformers.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param DataTransformerInterface $modelTransformer + * @param Boolean $forceAppend + * + * @throws BadMethodCallException + */ + public function addModelTransformer(DataTransformerInterface $modelTransformer, $forceAppend = false) + { + throw new BadMethodCallException('Buttons do not support data transformers.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @throws BadMethodCallException + */ + public function resetModelTransformers() + { + throw new BadMethodCallException('Buttons do not support data transformers.'); + } + + /** + * {@inheritdoc} + */ + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function setAttributes(array $attributes) + { + $this->attributes = $attributes; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param DataMapperInterface $dataMapper + * + * @throws BadMethodCallException + */ + public function setDataMapper(DataMapperInterface $dataMapper = null) + { + throw new BadMethodCallException('Buttons do not support data mappers.'); + } + + /** + * Set whether the button is disabled. + * + * @param Boolean $disabled Whether the button is disabled + * + * @return ButtonBuilder The button builder. + */ + public function setDisabled($disabled) + { + $this->disabled = $disabled; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param mixed $emptyData + * + * @throws BadMethodCallException + */ + public function setEmptyData($emptyData) + { + throw new BadMethodCallException('Buttons do not support empty data.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $errorBubbling + * + * @throws BadMethodCallException + */ + public function setErrorBubbling($errorBubbling) + { + throw new BadMethodCallException('Buttons do not support error bubbling.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $required + * + * @throws BadMethodCallException + */ + public function setRequired($required) + { + throw new BadMethodCallException('Buttons cannot be required.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param null $propertyPath + * + * @throws BadMethodCallException + */ + public function setPropertyPath($propertyPath) + { + throw new BadMethodCallException('Buttons do not support property paths.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $mapped + * + * @throws BadMethodCallException + */ + public function setMapped($mapped) + { + throw new BadMethodCallException('Buttons do not support data mapping.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $byReference + * + * @throws BadMethodCallException + */ + public function setByReference($byReference) + { + throw new BadMethodCallException('Buttons do not support data mapping.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $virtual + * + * @throws BadMethodCallException + */ + public function setVirtual($virtual) + { + throw new BadMethodCallException('Buttons cannot be virtual.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $compound + * + * @throws BadMethodCallException + */ + public function setCompound($compound) + { + throw new BadMethodCallException('Buttons cannot be compound.'); + } + + /** + * Sets the type of the button. + * + * @param ResolvedFormTypeInterface $type The type of the button. + * + * @return ButtonBuilder The button builder. + */ + public function setType(ResolvedFormTypeInterface $type) + { + $this->type = $type; + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param array $data + * + * @throws BadMethodCallException + */ + public function setData($data) + { + throw new BadMethodCallException('Buttons do not support data.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param Boolean $locked + * + * @throws BadMethodCallException + */ + public function setDataLocked($locked) + { + throw new BadMethodCallException('Buttons do not support data locking.'); + } + + /** + * Unsupported method. + * + * This method should not be invoked. + * + * @param FormFactoryInterface $formFactory + * + * @return void + * + * @throws BadMethodCallException + */ + public function setFormFactory(FormFactoryInterface $formFactory) + { + throw new BadMethodCallException('Buttons do not support form factories.'); + } + + /** + * Unsupported method. + * + * @param string $action + * + * @throws BadMethodCallException + */ + public function setAction($action) + { + throw new BadMethodCallException('Buttons do not support actions.'); + } + + /** + * Unsupported method. + * + * @param string $method + * + * @throws BadMethodCallException + */ + public function setMethod($method) + { + throw new BadMethodCallException('Buttons do not support methods.'); + } + + /** + * Unsupported method. + * + * @param RequestHandlerInterface $requestHandler + * + * @throws BadMethodCallException + */ + public function setRequestHandler(RequestHandlerInterface $requestHandler) + { + throw new BadMethodCallException('Buttons do not support form processors.'); + } + + /** + * Unsupported method. + * + * @param Boolean $initialize + * + * @throws BadMethodCallException + */ + public function setAutoInitialize($initialize) + { + if (true === $initialize) { + throw new BadMethodCallException('Buttons do not support automatic initialization.'); + } + + return $this; + } + + /** + * Unsupported method. + * + * @param Boolean $inheritData + * + * @throws BadMethodCallException + */ + public function setInheritData($inheritData) + { + throw new BadMethodCallException('Buttons do not support data inheritance.'); + } + + /** + * Builds and returns the button configuration. + * + * @return FormConfigInterface + */ + public function getFormConfig() + { + // This method should be idempotent, so clone the builder + $config = clone $this; + $config->locked = true; + + return $config; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getEventDispatcher() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getPropertyPath() + { + return null; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getMapped() + { + return false; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getByReference() + { + return false; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getVirtual() + { + return false; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getCompound() + { + return false; + } + + /** + * Returns the form type used to construct the button. + * + * @return ResolvedFormTypeInterface The button's type. + */ + public function getType() + { + return $this->type; + } + + /** + * Unsupported method. + * + * @return array Always returns an empty array. + */ + public function getViewTransformers() + { + return array(); + } + + /** + * Unsupported method. + * + * @return array Always returns an empty array. + */ + public function getModelTransformers() + { + return array(); + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getDataMapper() + { + return null; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getRequired() + { + return false; + } + + /** + * Returns whether the button is disabled. + * + * @return Boolean Whether the button is disabled. + */ + public function getDisabled() + { + return $this->disabled; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getErrorBubbling() + { + return false; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getEmptyData() + { + return null; + } + + /** + * Returns additional attributes of the button. + * + * @return array An array of key-value combinations. + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * Returns whether the attribute with the given name exists. + * + * @param string $name The attribute name. + * + * @return Boolean Whether the attribute exists. + */ + public function hasAttribute($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * Returns the value of the given attribute. + * + * @param string $name The attribute name. + * @param mixed $default The value returned if the attribute does not exist. + * + * @return mixed The attribute value. + */ + public function getAttribute($name, $default = null) + { + return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getData() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getDataClass() + { + return null; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getDataLocked() + { + return false; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getFormFactory() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getAction() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getMethod() + { + return null; + } + + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getRequestHandler() + { + return null; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getAutoInitialize() + { + return false; + } + + /** + * Unsupported method. + * + * @return Boolean Always returns false. + */ + public function getInheritData() + { + return false; + } + + /** + * Returns all options passed during the construction of the button. + * + * @return array The passed options. + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns whether a specific option exists. + * + * @param string $name The option name, + * + * @return Boolean Whether the option exists. + */ + public function hasOption($name) + { + return array_key_exists($name, $this->options); + } + + /** + * Returns the value of a specific option. + * + * @param string $name The option name. + * @param mixed $default The value returned if the option does not exist. + * + * @return mixed The option value. + */ + public function getOption($name, $default = null) + { + return array_key_exists($name, $this->options) ? $this->options[$name] : $default; + } + + /** + * Unsupported method. + * + * @return integer Always returns 0. + */ + public function count() + { + return 0; + } + + /** + * Unsupported method. + * + * @return \EmptyIterator Always returns an empty iterator. + */ + public function getIterator() + { + return new \EmptyIterator(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/ButtonTypeInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/ButtonTypeInterface.php new file mode 100644 index 0000000..dd5117c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/ButtonTypeInterface.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 Button} instance. + * + * @author Bernhard Schussek + */ +interface ButtonTypeInterface extends FormTypeInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/Form/CHANGELOG.md new file mode 100644 index 0000000..a696c7b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/CHANGELOG.md @@ -0,0 +1,238 @@ +CHANGELOG +========= + + +2.3.0 +------ + + * deprecated FormPerformanceTestCase and FormIntegrationTestCase in the Symfony\Component\Form\Tests namespace and moved them to the Symfony\Component\Form\Test namespace + * deprecated TypeTestCase in the Symfony\Component\Form\Tests\Extension\Core\Type namespace and moved it to the Symfony\Component\Form\Test namespace + * changed FormRenderer::humanize() to humanize also camel cased field name + * added RequestHandlerInterface and FormInterface::handleRequest() + * deprecated passing a Request instance to FormInterface::bind() + * added options "method" and "action" to FormType + * deprecated option "virtual" in favor "inherit_data" + * deprecated VirtualFormAwareIterator in favor of InheritDataAwareIterator + * [BC BREAK] removed the "array" type hint from DataMapperInterface + * improved forms inheriting their parent data to actually return that data from getData(), getNormData() and getViewData() + * added component-level exceptions for various SPL exceptions + changed all uses of the deprecated Exception class to use more specialized exceptions instead + removed NotInitializedException, NotValidException, TypeDefinitionException, TypeLoaderException, CreationException + * added events PRE_SUBMIT, SUBMIT and POST_SUBMIT + * deprecated events PRE_BIND, BIND and POST_BIND + * [BC BREAK] renamed bind() and isBound() in FormInterface to submit() and isSubmitted() + * added methods submit() and isSubmitted() to Form + * deprecated bind() and isBound() in Form + * deprecated AlreadyBoundException in favor of AlreadySubmittedException + * added support for PATCH requests + * [BC BREAK] added initialize() to FormInterface + * [BC BREAK] added getAutoInitialize() to FormConfigInterface + * [BC BREAK] added setAutoInitialize() to FormConfigBuilderInterface + * [BC BREAK] initialization for Form instances added to a form tree must be manually disabled + * PRE_SET_DATA is now guaranteed to be called after children were added by the form builder, + unless FormInterface::setData() is called manually + * fixed CSRF error message to be translated + * custom CSRF error messages can now be set through the "csrf_message" option + * fixed: expanded single-choice fields now show a radio button for the empty value + +2.2.0 +----- + + * TrimListener now removes unicode whitespaces + * deprecated getParent(), setParent() and hasParent() in FormBuilderInterface + * FormInterface::add() now accepts a FormInterface instance OR a field's name, type and options + * removed special characters between the choice or text fields of DateType unless + the option "format" is set to a custom value + * deprecated FormException and introduced ExceptionInterface instead + * [BC BREAK] FormException is now an interface + * protected FormBuilder methods from being called when it is turned into a FormConfigInterface with getFormConfig() + * [BC BREAK] inserted argument `$message` in the constructor of `FormError` + * the PropertyPath class and related classes were moved to a dedicated + PropertyAccess component. During the move, InvalidPropertyException was + renamed to NoSuchPropertyException. FormUtil was split: FormUtil::singularify() + can now be found in Symfony\Component\PropertyAccess\StringUtil. The methods + getValue() and setValue() from PropertyPath were extracted into a new class + PropertyAccessor. + * added an optional PropertyAccessorInterface parameter to FormType, + ObjectChoiceList and PropertyPathMapper + * [BC BREAK] PropertyPathMapper and FormType now have a constructor + * [BC BREAK] setting the option "validation_groups" to ``false`` now disables validation + instead of assuming group "Default" + +2.1.0 +----- + + * [BC BREAK] ``read_only`` field attribute now renders as ``readonly="readonly"``, use ``disabled`` instead + * [BC BREAK] child forms now aren't validated anymore by default + * made validation of form children configurable (new option: cascade_validation) + * added support for validation groups as callbacks + * made the translation catalogue configurable via the "translation_domain" option + * added Form::getErrorsAsString() to help debugging forms + * allowed setting different options for RepeatedType fields (like the label) + * added support for empty form name at root level, this enables rendering forms + without form name prefix in field names + * [BC BREAK] form and field names must start with a letter, digit or underscore + and only contain letters, digits, underscores, hyphens and colons + * [BC BREAK] changed default name of the prototype in the "collection" type + from "$$name$$" to "\__name\__". No dollars are appended/prepended to custom + names anymore. + * [BC BREAK] improved ChoiceListInterface + * [BC BREAK] added SimpleChoiceList and LazyChoiceList as replacement of + ArrayChoiceList + * added ChoiceList and ObjectChoiceList to use objects as choices + * [BC BREAK] removed EntitiesToArrayTransformer and EntityToIdTransformer. + The former has been replaced by CollectionToArrayTransformer in combination + with EntityChoiceList, the latter is not required in the core anymore. + * [BC BREAK] renamed + * ArrayToBooleanChoicesTransformer to ChoicesToBooleanArrayTransformer + * ScalarToBooleanChoicesTransformer to ChoiceToBooleanArrayTransformer + * ArrayToChoicesTransformer to ChoicesToValuesTransformer + * ScalarToChoiceTransformer to ChoiceToValueTransformer + to be consistent with the naming in ChoiceListInterface. + They were merged into ChoiceList and have no public equivalent anymore. + * choice fields now throw a FormException if neither the "choices" nor the + "choice_list" option is set + * the radio type is now a child of the checkbox type + * the collection, choice (with multiple selection) and entity (with multiple + selection) types now make use of addXxx() and removeXxx() methods in your + model if you set "by_reference" to false. For a custom, non-recognized + singular form, set the "property_path" option like this: "plural|singular" + * forms now don't create an empty object anymore if they are completely + empty and not required. The empty value for such forms is null. + * added constant Guess::VERY_HIGH_CONFIDENCE + * [BC BREAK] The methods `add`, `remove`, `setParent`, `bind` and `setData` + in class Form now throw an exception if the form is already bound + * fields of constrained classes without a NotBlank or NotNull constraint are + set to not required now, as stated in the docs + * fixed TimeType and DateTimeType to not display seconds when "widget" is + "single_text" unless "with_seconds" is set to true + * checkboxes of in an expanded multiple-choice field don't include the choice + in their name anymore. Their names terminate with "[]" now. + * deprecated FormValidatorInterface and substituted its implementations + by event subscribers + * simplified CSRF protection and removed the csrf type + * deprecated FieldType and merged it into FormType + * added new option "compound" that lets you switch between field and form behavior + * [BC BREAK] renamed theme blocks + * "field_*" to "form_*" + * "field_widget" to "form_widget_simple" + * "widget_choice_options" to "choice_widget_options" + * "generic_label" to "form_label" + * added theme blocks "form_widget_compound", "choice_widget_expanded" and + "choice_widget_collapsed" to make theming more modular + * ValidatorTypeGuesser now guesses "collection" for array type constraint + * added method `guessPattern` to FormTypeGuesserInterface to guess which pattern to use in the HTML5 attribute "pattern" + * deprecated method `guessMinLength` in favor of `guessPattern` + * labels don't display field attributes anymore. Label attributes can be + passed in the "label_attr" option/variable + * added option "mapped" which should be used instead of setting "property_path" to false + * [BC BREAK] "data_class" now *must* be set if a form maps to an object and should be left empty otherwise + * improved error mapping on forms + * dot (".") rules are now allowed to map errors assigned to a form to + one of its children + * errors are not mapped to unsynchronized forms anymore + * [BC BREAK] changed Form constructor to accept a single `FormConfigInterface` object + * [BC BREAK] changed argument order in the FormBuilder constructor + * added Form method `getViewData` + * deprecated Form methods + * `getTypes` + * `getErrorBubbling` + * `getNormTransformers` + * `getClientTransformers` + * `getAttribute` + * `hasAttribute` + * `getClientData` + * added FormBuilder methods + * `getTypes` + * `addViewTransformer` + * `getViewTransformers` + * `resetViewTransformers` + * `addModelTransformer` + * `getModelTransformers` + * `resetModelTransformers` + * deprecated FormBuilder methods + * `prependClientTransformer` + * `appendClientTransformer` + * `getClientTransformers` + * `resetClientTransformers` + * `prependNormTransformer` + * `appendNormTransformer` + * `getNormTransformers` + * `resetNormTransformers` + * deprecated the option "validation_constraint" in favor of the new + option "constraints" + * removed superfluous methods from DataMapperInterface + * `mapFormToData` + * `mapDataToForm` + * added `setDefaultOptions` to FormTypeInterface and FormTypeExtensionInterface + which accepts an OptionsResolverInterface instance + * deprecated the methods `getDefaultOptions` and `getAllowedOptionValues` + in FormTypeInterface and FormTypeExtensionInterface + * options passed during construction can now be accessed from FormConfigInterface + * added FormBuilderInterface and FormConfigEditorInterface + * [BC BREAK] the method `buildForm` in FormTypeInterface and FormTypeExtensionInterface + now receives a FormBuilderInterface instead of a FormBuilder instance + * [BC BREAK] the method `buildViewBottomUp` was renamed to `finishView` in + FormTypeInterface and FormTypeExtensionInterface + * [BC BREAK] the options array is now passed as last argument of the + methods + * `buildView` + * `finishView` + in FormTypeInterface and FormTypeExtensionInterface + * [BC BREAK] no options are passed to `getParent` of FormTypeInterface anymore + * deprecated DataEvent and FilterDataEvent in favor of the new FormEvent which is + now passed to all events thrown by the component + * FormEvents::BIND now replaces FormEvents::BIND_NORM_DATA + * FormEvents::PRE_SET_DATA now replaces FormEvents::SET_DATA + * FormEvents::PRE_BIND now replaces FormEvents::BIND_CLIENT_DATA + * deprecated FormEvents::SET_DATA, FormEvents::BIND_CLIENT_DATA and + FormEvents::BIND_NORM_DATA + * [BC BREAK] reversed the order of the first two arguments to `createNamed` + and `createNamedBuilder` in `FormFactoryInterface` + * deprecated `getChildren` in Form and FormBuilder in favor of `all` + * deprecated `hasChildren` in Form and FormBuilder in favor of `count` + * FormBuilder now implements \IteratorAggregate + * [BC BREAK] compound forms now always need a data mapper + * FormBuilder now maintains the order when explicitly adding form builders as children + * ChoiceType now doesn't add the empty value anymore if the choices already contain an empty element + * DateType, TimeType and DateTimeType now show empty values again if not required + * [BC BREAK] fixed rendering of errors for DateType, BirthdayType and similar ones + * [BC BREAK] fixed: form constraints are only validated if they belong to the validated group + * deprecated `bindRequest` in `Form` and replaced it by a listener to FormEvents::PRE_BIND + * fixed: the "data" option supersedes default values from the model + * changed DateType to refer to the "format" option for calculating the year and day choices instead + of padding them automatically + * [BC BREAK] DateType defaults to the format "yyyy-MM-dd" now if the widget is + "single_text", in order to support the HTML 5 date field out of the box + * added the option "format" to DateTimeType + * [BC BREAK] DateTimeType now outputs RFC 3339 dates by default, as generated and + consumed by HTML5 browsers, if the widget is "single_text" + * deprecated the options "data_timezone" and "user_timezone" in DateType, DateTimeType and TimeType + and renamed them to "model_timezone" and "view_timezone" + * fixed: TransformationFailedExceptions thrown in the model transformer are now caught by the form + * added FormRegistryInterface, ResolvedFormTypeInterface and ResolvedFormTypeFactoryInterface + * deprecated FormFactory methods + * `addType` + * `hasType` + * `getType` + * [BC BREAK] FormFactory now expects a FormRegistryInterface and a ResolvedFormTypeFactoryInterface as constructor argument + * [BC BREAK] The method `createBuilder` in FormTypeInterface is not supported anymore for performance reasons + * [BC BREAK] Removed `setTypes` from FormBuilder + * deprecated AbstractType methods + * `getExtensions` + * `setExtensions` + * ChoiceType now caches its created choice lists to improve performance + * [BC BREAK] Rows of a collection field cannot be themed individually anymore. All rows in the collection + field now have the same block names, which contains "entry" where it previously contained the row index. + * [BC BREAK] When registering a type through the DI extension, the tag alias has to match the actual type name. + * added FormRendererInterface, FormRendererEngineInterface and implementations of these interfaces + * [BC BREAK] removed the following methods from FormUtil: + * `toArrayKey` + * `toArrayKeys` + * `isChoiceGroup` + * `isChoiceSelected` + * [BC BREAK] renamed method `renderBlock` in FormHelper to `block` and changed its signature + * made FormView properties public and deprecated their accessor methods + * made the normalized data of a form accessible in the template through the variable "form.vars.data" + * made the original data of a choice accessible in the template through the property "choice.data" + * added convenience class Forms and FormFactoryBuilderInterface diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/CallbackTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/CallbackTransformer.php new file mode 100644 index 0000000..1dfe890 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/CallbackTransformer.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; + +class CallbackTransformer implements DataTransformerInterface +{ + /** + * The callback used for forward transform + * @var \Closure + */ + private $transform; + + /** + * The callback used for reverse transform + * @var \Closure + */ + private $reverseTransform; + + /** + * Constructor. + * + * @param \Closure $transform The forward transform callback + * @param \Closure $reverseTransform The reverse transform callback + */ + public function __construct(\Closure $transform, \Closure $reverseTransform) + { + $this->transform = $transform; + $this->reverseTransform = $reverseTransform; + } + + /** + * Transforms a value from the original representation to a transformed representation. + * + * @param mixed $data The value in the original representation + * + * @return mixed The value in the transformed representation + * + * @throws UnexpectedTypeException when the argument is not a string + * @throws TransformationFailedException when the transformation fails + */ + public function transform($data) + { + return call_user_func($this->transform, $data); + } + + /** + * Transforms a value from the transformed representation to its original + * representation. + * + * @param mixed $data The value in the transformed representation + * + * @return mixed The value in the original representation + * + * @throws UnexpectedTypeException when the argument is not of the expected type + * @throws TransformationFailedException when the transformation fails + */ + public function reverseTransform($data) + { + return call_user_func($this->reverseTransform, $data); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/ClickableInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/ClickableInterface.php new file mode 100644 index 0000000..893f02d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/ClickableInterface.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; + +/** + * A clickable form element. + * + * @author Bernhard Schussek + */ +interface ClickableInterface +{ + /** + * Returns whether this element was clicked. + * + * @return Boolean Whether this element was clicked. + */ + public function isClicked(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/DataMapperInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/DataMapperInterface.php new file mode 100644 index 0000000..6e03168 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/DataMapperInterface.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; + +/** + * @author Bernhard Schussek + */ +interface DataMapperInterface +{ + /** + * Maps properties of some data to a list of forms. + * + * @param mixed $data Structured data. + * @param FormInterface[] $forms A list of {@link FormInterface} instances. + * + * @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported. + */ + public function mapDataToForms($data, $forms); + + /** + * Maps the data of a list of forms into the properties of some data. + * + * @param FormInterface[] $forms A list of {@link FormInterface} instances. + * @param mixed $data Structured data. + * + * @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported. + */ + public function mapFormsToData($forms, &$data); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/DataTransformerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/DataTransformerInterface.php new file mode 100644 index 0000000..6567da2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/DataTransformerInterface.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; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms a value between different representations. + * + * @author Bernhard Schussek + */ +interface DataTransformerInterface +{ + /** + * Transforms a value from the original representation to a transformed representation. + * + * This method is called on two occasions inside a form field: + * + * 1. When the form field is initialized with the data attached from the datasource (object or array). + * 2. When data from a request is submitted using {@link Form::submit()} to transform the new input data + * back into the renderable format. For example if you have a date field and submit '2009-10-10' + * you might accept this value because its easily parsed, but the transformer still writes back + * "2009/10/10" onto the form field (for further displaying or other purposes). + * + * This method must be able to deal with empty values. Usually this will + * be NULL, but depending on your implementation other empty values are + * possible as well (such as empty strings). The reasoning behind this is + * that value transformers must be chainable. If the transform() method + * of the first value transformer outputs NULL, the second value transformer + * must be able to process that value. + * + * By convention, transform() should return an empty string if NULL is + * passed. + * + * @param mixed $value The value in the original representation + * + * @return mixed The value in the transformed representation + * + * @throws TransformationFailedException When the transformation fails. + */ + public function transform($value); + + /** + * Transforms a value from the transformed representation to its original + * representation. + * + * This method is called when {@link Form::submit()} is called to transform the requests tainted data + * into an acceptable format for your data processing/model layer. + * + * This method must be able to deal with empty values. Usually this will + * be an empty string, but depending on your implementation other empty + * values are possible as well (such as empty strings). The reasoning behind + * this is that value transformers must be chainable. If the + * reverseTransform() method of the first value transformer outputs an + * empty string, the second value transformer must be able to process that + * value. + * + * By convention, reverseTransform() should return NULL if an empty string + * is passed. + * + * @param mixed $value The value in the transformed representation + * + * @return mixed The value in the original representation + * + * @throws TransformationFailedException When the transformation fails. + */ + public function reverseTransform($value); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/AlreadyBoundException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/AlreadyBoundException.php new file mode 100644 index 0000000..7ef0ca0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/AlreadyBoundException.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\Exception; + +/** + * Alias of {@link AlreadySubmittedException}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link AlreadySubmittedException} instead. + */ +class AlreadyBoundException extends LogicException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/AlreadySubmittedException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/AlreadySubmittedException.php new file mode 100644 index 0000000..7be2124 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/AlreadySubmittedException.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\Exception; + +/** + * Thrown when an operation is called that is not acceptable after submitting + * a form. + * + * @author Bernhard Schussek + */ +class AlreadySubmittedException extends AlreadyBoundException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/BadMethodCallException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/BadMethodCallException.php new file mode 100644 index 0000000..27649dd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/BadMethodCallException.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\Exception; + +/** + * Base BadMethodCallException for the Form component. + * + * @author Bernhard Schussek + */ +class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/ErrorMappingException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/ErrorMappingException.php new file mode 100644 index 0000000..a696849 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/ErrorMappingException.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\Exception; + +class ErrorMappingException extends RuntimeException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/ExceptionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/ExceptionInterface.php new file mode 100644 index 0000000..d455932 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/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\Form\Exception; + +/** + * Base ExceptionInterface for the Form component. + * + * @author Bernhard Schussek + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/InvalidArgumentException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..a270e0c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/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\Form\Exception; + +/** + * Base InvalidArgumentException for the Form component. + * + * @author Bernhard Schussek + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/InvalidConfigurationException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/InvalidConfigurationException.php new file mode 100644 index 0000000..daa0c42 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/InvalidConfigurationException.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\Exception; + +class InvalidConfigurationException extends InvalidArgumentException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/LogicException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/LogicException.php new file mode 100644 index 0000000..8487802 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/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\Form\Exception; + +/** + * Base LogicException for Form component. + * + * @author Alexander Kotynia + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/OutOfBoundsException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/OutOfBoundsException.php new file mode 100644 index 0000000..44d3116 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/OutOfBoundsException.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\Exception; + +/** + * Base OutOfBoundsException for Form component. + * + * @author Alexander Kotynia + */ +class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/RuntimeException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/RuntimeException.php new file mode 100644 index 0000000..0af48a4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/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\Form\Exception; + +/** + * Base RuntimeException for the Form component. + * + * @author Bernhard Schussek + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/StringCastException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/StringCastException.php new file mode 100644 index 0000000..f9b51d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/StringCastException.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\Exception; + +class StringCastException extends RuntimeException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/TransformationFailedException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/TransformationFailedException.php new file mode 100644 index 0000000..d32896e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/TransformationFailedException.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\Exception; + +/** + * Indicates a value transformation error. + * + * @author Bernhard Schussek + */ +class TransformationFailedException extends RuntimeException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/UnexpectedTypeException.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Exception/UnexpectedTypeException.php new file mode 100644 index 0000000..474e244 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/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\Form\Exception; + +class UnexpectedTypeException extends InvalidArgumentException +{ + 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/Form/Extension/Core/ChoiceList/ChoiceList.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php new file mode 100644 index 0000000..f9d381c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php @@ -0,0 +1,510 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\ChoiceList; + +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\Exception\InvalidConfigurationException; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +/** + * A choice list for choices of arbitrary data types. + * + * Choices and labels are passed in two arrays. The indices of the choices + * and the labels should match. Choices may also be given as hierarchy of + * unlimited depth by creating nested arrays. The title of the sub-hierarchy + * can be stored in the array key pointing to the nested array. The topmost + * level of the hierarchy may also be a \Traversable. + * + * + * $choices = array(true, false); + * $labels = array('Agree', 'Disagree'); + * $choiceList = new ChoiceList($choices, $labels); + * + * + * @author Bernhard Schussek + */ +class ChoiceList implements ChoiceListInterface +{ + /** + * The choices with their indices as keys. + * + * @var array + */ + private $choices = array(); + + /** + * The choice values with the indices of the matching choices as keys. + * + * @var array + */ + private $values = array(); + + /** + * The preferred view objects as hierarchy containing also the choice groups + * with the indices of the matching choices as bottom-level keys. + * + * @var array + */ + private $preferredViews = array(); + + /** + * The non-preferred view objects as hierarchy containing also the choice + * groups with the indices of the matching choices as bottom-level keys. + * + * @var array + */ + private $remainingViews = array(); + + /** + * Creates a new choice list. + * + * @param array|\Traversable $choices The array of choices. Choices may also be given + * as hierarchy of unlimited depth. Hierarchies are + * created by creating nested arrays. The title of + * the sub-hierarchy can be stored in the array + * key pointing to the nested array. The topmost + * level of the hierarchy may also be a \Traversable. + * @param array $labels The array of labels. The structure of this array + * should match the structure of $choices. + * @param array $preferredChoices A flat array of choices that should be + * presented to the user with priority. + * + * @throws UnexpectedTypeException If the choices are not an array or \Traversable. + */ + public function __construct($choices, array $labels, array $preferredChoices = array()) + { + if (!is_array($choices) && !$choices instanceof \Traversable) { + throw new UnexpectedTypeException($choices, 'array or \Traversable'); + } + + $this->initialize($choices, $labels, $preferredChoices); + } + + /** + * Initializes the list with choices. + * + * Safe to be called multiple times. The list is cleared on every call. + * + * @param array|\Traversable $choices The choices to write into the list. + * @param array $labels The labels belonging to the choices. + * @param array $preferredChoices The choices to display with priority. + */ + protected function initialize($choices, array $labels, array $preferredChoices) + { + $this->choices = array(); + $this->values = array(); + $this->preferredViews = array(); + $this->remainingViews = array(); + + $this->addChoices( + $this->preferredViews, + $this->remainingViews, + $choices, + $labels, + $preferredChoices + ); + } + + /** + * {@inheritdoc} + */ + public function getChoices() + { + return $this->choices; + } + + /** + * {@inheritdoc} + */ + public function getValues() + { + return $this->values; + } + + /** + * {@inheritdoc} + */ + public function getPreferredViews() + { + return $this->preferredViews; + } + + /** + * {@inheritdoc} + */ + public function getRemainingViews() + { + return $this->remainingViews; + } + + /** + * {@inheritdoc} + */ + public function getChoicesForValues(array $values) + { + $values = $this->fixValues($values); + $choices = array(); + + foreach ($values as $j => $givenValue) { + foreach ($this->values as $i => $value) { + if ($value === $givenValue) { + $choices[] = $this->choices[$i]; + unset($values[$j]); + + if (0 === count($values)) { + break 2; + } + } + } + } + + return $choices; + } + + /** + * {@inheritdoc} + */ + public function getValuesForChoices(array $choices) + { + $choices = $this->fixChoices($choices); + $values = array(); + + foreach ($this->choices as $i => $choice) { + foreach ($choices as $j => $givenChoice) { + if ($choice === $givenChoice) { + $values[] = $this->values[$i]; + unset($choices[$j]); + + if (0 === count($choices)) { + break 2; + } + } + } + } + + return $values; + } + + /** + * {@inheritdoc} + */ + public function getIndicesForChoices(array $choices) + { + $choices = $this->fixChoices($choices); + $indices = array(); + + foreach ($this->choices as $i => $choice) { + foreach ($choices as $j => $givenChoice) { + if ($choice === $givenChoice) { + $indices[] = $i; + unset($choices[$j]); + + if (0 === count($choices)) { + break 2; + } + } + } + } + + return $indices; + } + + /** + * {@inheritdoc} + */ + public function getIndicesForValues(array $values) + { + $values = $this->fixValues($values); + $indices = array(); + + foreach ($this->values as $i => $value) { + foreach ($values as $j => $givenValue) { + if ($value === $givenValue) { + $indices[] = $i; + unset($values[$j]); + + if (0 === count($values)) { + break 2; + } + } + } + } + + return $indices; + } + + /** + * Recursively adds the given choices to the list. + * + * @param array $bucketForPreferred The bucket where to store the preferred + * view objects. + * @param array $bucketForRemaining The bucket where to store the + * non-preferred view objects. + * @param array|\Traversable $choices The list of choices. + * @param array $labels The labels corresponding to the choices. + * @param array $preferredChoices The preferred choices. + * + * @throws InvalidArgumentException If the structures of the choices and labels array do not match. + * @throws InvalidConfigurationException If no valid value or index could be created for a choice. + */ + protected function addChoices(array &$bucketForPreferred, array &$bucketForRemaining, $choices, array $labels, array $preferredChoices) + { + // Add choices to the nested buckets + foreach ($choices as $group => $choice) { + if (!array_key_exists($group, $labels)) { + throw new InvalidArgumentException('The structures of the choices and labels array do not match.'); + } + + if (is_array($choice)) { + // Don't do the work if the array is empty + if (count($choice) > 0) { + $this->addChoiceGroup( + $group, + $bucketForPreferred, + $bucketForRemaining, + $choice, + $labels[$group], + $preferredChoices + ); + } + } else { + $this->addChoice( + $bucketForPreferred, + $bucketForRemaining, + $choice, + $labels[$group], + $preferredChoices + ); + } + } + } + + /** + * Recursively adds a choice group. + * + * @param string $group The name of the group. + * @param array $bucketForPreferred The bucket where to store the preferred + * view objects. + * @param array $bucketForRemaining The bucket where to store the + * non-preferred view objects. + * @param array $choices The list of choices in the group. + * @param array $labels The labels corresponding to the choices in the group. + * @param array $preferredChoices The preferred choices. + * + * @throws InvalidConfigurationException If no valid value or index could be created for a choice. + */ + protected function addChoiceGroup($group, array &$bucketForPreferred, array &$bucketForRemaining, array $choices, array $labels, array $preferredChoices) + { + // If this is a choice group, create a new level in the choice + // key hierarchy + $bucketForPreferred[$group] = array(); + $bucketForRemaining[$group] = array(); + + $this->addChoices( + $bucketForPreferred[$group], + $bucketForRemaining[$group], + $choices, + $labels, + $preferredChoices + ); + + // Remove child levels if empty + if (empty($bucketForPreferred[$group])) { + unset($bucketForPreferred[$group]); + } + if (empty($bucketForRemaining[$group])) { + unset($bucketForRemaining[$group]); + } + } + + /** + * Adds a new choice. + * + * @param array $bucketForPreferred The bucket where to store the preferred + * view objects. + * @param array $bucketForRemaining The bucket where to store the + * non-preferred view objects. + * @param mixed $choice The choice to add. + * @param string $label The label for the choice. + * @param array $preferredChoices The preferred choices. + * + * @throws InvalidConfigurationException If no valid value or index could be created. + */ + protected function addChoice(array &$bucketForPreferred, array &$bucketForRemaining, $choice, $label, array $preferredChoices) + { + $index = $this->createIndex($choice); + + if ('' === $index || null === $index || !FormConfigBuilder::isValidName((string) $index)) { + throw new InvalidConfigurationException(sprintf('The index "%s" created by the choice list is invalid. It should be a valid, non-empty Form name.', $index)); + } + + $value = $this->createValue($choice); + + if (!is_string($value)) { + throw new InvalidConfigurationException(sprintf('The value created by the choice list is of type "%s", but should be a string.', gettype($value))); + } + + $view = new ChoiceView($choice, $value, $label); + + $this->choices[$index] = $this->fixChoice($choice); + $this->values[$index] = $value; + + if ($this->isPreferred($choice, $preferredChoices)) { + $bucketForPreferred[$index] = $view; + } else { + $bucketForRemaining[$index] = $view; + } + } + + /** + * Returns whether the given choice should be preferred judging by the + * given array of preferred choices. + * + * Extension point to optimize performance by changing the structure of the + * $preferredChoices array. + * + * @param mixed $choice The choice to test. + * @param array $preferredChoices An array of preferred choices. + * + * @return Boolean Whether the choice is preferred. + */ + protected function isPreferred($choice, array $preferredChoices) + { + return false !== array_search($choice, $preferredChoices, true); + } + + /** + * Creates a new unique index for this choice. + * + * Extension point to change the indexing strategy. + * + * @param mixed $choice The choice to create an index for + * + * @return integer|string A unique index containing only ASCII letters, + * digits and underscores. + */ + protected function createIndex($choice) + { + return count($this->choices); + } + + /** + * Creates a new unique value for this choice. + * + * By default, an integer is generated since it cannot be guaranteed that + * all values in the list are convertible to (unique) strings. Subclasses + * can override this behaviour if they can guarantee this property. + * + * @param mixed $choice The choice to create a value for + * + * @return string A unique string. + */ + protected function createValue($choice) + { + return (string) count($this->values); + } + + /** + * Fixes the data type of the given choice value to avoid comparison + * problems. + * + * @param mixed $value The choice value. + * + * @return string The value as string. + */ + protected function fixValue($value) + { + return (string) $value; + } + + /** + * Fixes the data types of the given choice values to avoid comparison + * problems. + * + * @param array $values The choice values. + * + * @return array The values as strings. + */ + protected function fixValues(array $values) + { + foreach ($values as $i => $value) { + $values[$i] = $this->fixValue($value); + } + + return $values; + } + + /** + * Fixes the data type of the given choice index to avoid comparison + * problems. + * + * @param mixed $index The choice index. + * + * @return integer|string The index as PHP array key. + */ + protected function fixIndex($index) + { + if (is_bool($index) || (string) (int) $index === (string) $index) { + return (int) $index; + } + + return (string) $index; + } + + /** + * Fixes the data types of the given choice indices to avoid comparison + * problems. + * + * @param array $indices The choice indices. + * + * @return array The indices as strings. + */ + protected function fixIndices(array $indices) + { + foreach ($indices as $i => $index) { + $indices[$i] = $this->fixIndex($index); + } + + return $indices; + } + + /** + * Fixes the data type of the given choice to avoid comparison problems. + * + * Extension point. In this implementation, choices are guaranteed to + * always maintain their type and thus can be typesafely compared. + * + * @param mixed $choice The choice. + * + * @return mixed The fixed choice. + */ + protected function fixChoice($choice) + { + return $choice; + } + + /** + * Fixes the data type of the given choices to avoid comparison problems. + * + * @param array $choices The choices. + * + * @return array The fixed choices. + * + * @see fixChoice + */ + protected function fixChoices(array $choices) + { + return $choices; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.php new file mode 100644 index 0000000..099ace8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.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\Extension\Core\ChoiceList; + +/** + * Contains choices that can be selected in a form field. + * + * Each choice has three different properties: + * + * - Choice: The choice that should be returned to the application by the + * choice field. Can be any scalar value or an object, but no + * array. + * - Label: A text representing the choice that is displayed to the user. + * - Value: A uniquely identifying value that can contain arbitrary + * characters, but no arrays or objects. This value is displayed + * in the HTML "value" attribute. + * + * @author Bernhard Schussek + */ +interface ChoiceListInterface +{ + /** + * Returns the list of choices + * + * @return array The choices with their indices as keys + */ + public function getChoices(); + + /** + * Returns the values for the choices + * + * @return array The values with the corresponding choice indices as keys + */ + public function getValues(); + + /** + * Returns the choice views of the preferred choices as nested array with + * the choice groups as top-level keys. + * + * Example: + * + * + * array( + * 'Group 1' => array( + * 10 => ChoiceView object, + * 20 => ChoiceView object, + * ), + * 'Group 2' => array( + * 30 => ChoiceView object, + * ), + * ) + * + * + * @return array A nested array containing the views with the corresponding + * choice indices as keys on the lowest levels and the choice + * group names in the keys of the higher levels + */ + public function getPreferredViews(); + + /** + * Returns the choice views of the choices that are not preferred as nested + * array with the choice groups as top-level keys. + * + * Example: + * + * + * array( + * 'Group 1' => array( + * 10 => ChoiceView object, + * 20 => ChoiceView object, + * ), + * 'Group 2' => array( + * 30 => ChoiceView object, + * ), + * ) + * + * + * @return array A nested array containing the views with the corresponding + * choice indices as keys on the lowest levels and the choice + * group names in the keys of the higher levels + * + * @see getPreferredValues + */ + public function getRemainingViews(); + + /** + * Returns the choices corresponding to the given values. + * + * The choices can have any data type. + * + * @param array $values An array of choice values. Not existing values in + * this array are ignored + * + * @return array An array of choices with ascending, 0-based numeric keys + */ + public function getChoicesForValues(array $values); + + /** + * Returns the values corresponding to the given choices. + * + * The values must be strings. + * + * @param array $choices An array of choices. Not existing choices in this + * array are ignored + * + * @return array An array of choice values with ascending, 0-based numeric + * keys + */ + public function getValuesForChoices(array $choices); + + /** + * Returns the indices corresponding to the given choices. + * + * The indices must be positive integers or strings accepted by + * {@link FormConfigBuilder::validateName()}. + * + * The index "placeholder" is internally reserved. + * + * @param array $choices An array of choices. Not existing choices in this + * array are ignored + * + * @return array An array of indices with ascending, 0-based numeric keys + */ + public function getIndicesForChoices(array $choices); + + /** + * Returns the indices corresponding to the given values. + * + * The indices must be positive integers or strings accepted by + * {@link FormConfigBuilder::validateName()}. + * + * The index "placeholder" is internally reserved. + * + * @param array $values An array of choice values. Not existing values in + * this array are ignored + * + * @return array An array of indices with ascending, 0-based numeric keys + */ + public function getIndicesForValues(array $values); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.php new file mode 100644 index 0000000..996f900 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.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\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Exception\InvalidArgumentException; + +/** + * A choice list that is loaded lazily + * + * This list loads itself as soon as any of the getters is accessed for the + * first time. You should implement loadChoiceList() in your child classes, + * which should return a ChoiceListInterface instance. + * + * @author Bernhard Schussek + */ +abstract class LazyChoiceList implements ChoiceListInterface +{ + /** + * The loaded choice list + * + * @var ChoiceListInterface + */ + private $choiceList; + + /** + * {@inheritdoc} + */ + public function getChoices() + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getChoices(); + } + + /** + * {@inheritdoc} + */ + public function getValues() + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getValues(); + } + + /** + * {@inheritdoc} + */ + public function getPreferredViews() + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getPreferredViews(); + } + + /** + * {@inheritdoc} + */ + public function getRemainingViews() + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getRemainingViews(); + } + + /** + * {@inheritdoc} + */ + public function getChoicesForValues(array $values) + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getChoicesForValues($values); + } + + /** + * {@inheritdoc} + */ + public function getValuesForChoices(array $choices) + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getValuesForChoices($choices); + } + + /** + * {@inheritdoc} + */ + public function getIndicesForChoices(array $choices) + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getIndicesForChoices($choices); + } + + /** + * {@inheritdoc} + */ + public function getIndicesForValues(array $values) + { + if (!$this->choiceList) { + $this->load(); + } + + return $this->choiceList->getIndicesForValues($values); + } + + /** + * Loads the choice list + * + * Should be implemented by child classes. + * + * @return ChoiceListInterface The loaded choice list + */ + abstract protected function loadChoiceList(); + + private function load() + { + $choiceList = $this->loadChoiceList(); + + if (!$choiceList instanceof ChoiceListInterface) { + throw new InvalidArgumentException(sprintf('loadChoiceList() should return a ChoiceListInterface instance. Got %s', gettype($choiceList))); + } + + $this->choiceList = $choiceList; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php new file mode 100644 index 0000000..0a15388 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.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\Form\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Exception\StringCastException; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * A choice list for object choices. + * + * Supports generation of choice labels, choice groups and choice values + * by calling getters of the object (or associated objects). + * + * + * $choices = array($user1, $user2); + * + * // call getName() to determine the choice labels + * $choiceList = new ObjectChoiceList($choices, 'name'); + * + * + * @author Bernhard Schussek + */ +class ObjectChoiceList extends ChoiceList +{ + /** + * @var PropertyAccessorInterface + */ + private $propertyAccessor; + + /** + * The property path used to obtain the choice label. + * + * @var PropertyPath + */ + private $labelPath; + + /** + * The property path used for object grouping. + * + * @var PropertyPath + */ + private $groupPath; + + /** + * The property path used to obtain the choice value. + * + * @var PropertyPath + */ + private $valuePath; + + /** + * Creates a new object choice list. + * + * @param array|\Traversable $choices The array of choices. Choices may also be given + * as hierarchy of unlimited depth by creating nested + * arrays. The title of the sub-hierarchy can be + * stored in the array key pointing to the nested + * array. The topmost level of the hierarchy may also + * be a \Traversable. + * @param string $labelPath A property path pointing to the property used + * for the choice labels. The value is obtained + * by calling the getter on the object. If the + * path is NULL, the object's __toString() method + * is used instead. + * @param array $preferredChoices A flat array of choices that should be + * presented to the user with priority. + * @param string $groupPath A property path pointing to the property used + * to group the choices. Only allowed if + * the choices are given as flat array. + * @param string $valuePath A property path pointing to the property used + * for the choice values. If not given, integers + * are generated instead. + * @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths. + */ + public function __construct($choices, $labelPath = null, array $preferredChoices = array(), $groupPath = null, $valuePath = null, PropertyAccessorInterface $propertyAccessor = null) + { + $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor(); + $this->labelPath = null !== $labelPath ? new PropertyPath($labelPath) : null; + $this->groupPath = null !== $groupPath ? new PropertyPath($groupPath) : null; + $this->valuePath = null !== $valuePath ? new PropertyPath($valuePath) : null; + + parent::__construct($choices, array(), $preferredChoices); + } + + /** + * Initializes the list with choices. + * + * Safe to be called multiple times. The list is cleared on every call. + * + * @param array|\Traversable $choices The choices to write into the list. + * @param array $labels Ignored. + * @param array $preferredChoices The choices to display with priority. + * + * @throws InvalidArgumentException When passing a hierarchy of choices and using + * the "groupPath" option at the same time. + */ + protected function initialize($choices, array $labels, array $preferredChoices) + { + if (null !== $this->groupPath) { + $groupedChoices = array(); + + foreach ($choices as $i => $choice) { + if (is_array($choice)) { + throw new InvalidArgumentException('You should pass a plain object array (without groups) when using the "groupPath" option.'); + } + + try { + $group = $this->propertyAccessor->getValue($choice, $this->groupPath); + } catch (NoSuchPropertyException $e) { + // Don't group items whose group property does not exist + // see https://github.com/symfony/symfony/commit/d9b7abb7c7a0f28e0ce970afc5e305dce5dccddf + $group = null; + } + + if (null === $group) { + $groupedChoices[$i] = $choice; + } else { + if (!isset($groupedChoices[$group])) { + $groupedChoices[$group] = array(); + } + + $groupedChoices[$group][$i] = $choice; + } + } + + $choices = $groupedChoices; + } + + $labels = array(); + + $this->extractLabels($choices, $labels); + + parent::initialize($choices, $labels, $preferredChoices); + } + + /** + * Creates a new unique value for this choice. + * + * If a property path for the value was given at object creation, + * the getter behind that path is now called to obtain a new value. + * Otherwise a new integer is generated. + * + * @param mixed $choice The choice to create a value for + * + * @return integer|string A unique value without character limitations. + */ + protected function createValue($choice) + { + if ($this->valuePath) { + return (string) $this->propertyAccessor->getValue($choice, $this->valuePath); + } + + return parent::createValue($choice); + } + + private function extractLabels($choices, array &$labels) + { + foreach ($choices as $i => $choice) { + if (is_array($choice)) { + $labels[$i] = array(); + $this->extractLabels($choice, $labels[$i]); + } elseif ($this->labelPath) { + $labels[$i] = $this->propertyAccessor->getValue($choice, $this->labelPath); + } elseif (method_exists($choice, '__toString')) { + $labels[$i] = (string) $choice; + } else { + throw new StringCastException(sprintf('A "__toString()" method was not found on the objects of type "%s" passed to the choice field. To read a custom getter instead, set the argument $labelPath to the desired property path.', get_class($choice))); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.php new file mode 100644 index 0000000..914dbe5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.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\Form\Extension\Core\ChoiceList; + +/** + * A choice list for choices of type string or integer. + * + * Choices and their associated labels can be passed in a single array. Since + * choices are passed as array keys, only strings or integer choices are + * allowed. Choices may also be given as hierarchy of unlimited depth by + * creating nested arrays. The title of the sub-hierarchy can be stored in the + * array key pointing to the nested array. + * + * + * $choiceList = new SimpleChoiceList(array( + * 'creditcard' => 'Credit card payment', + * 'cash' => 'Cash payment', + * )); + * + * + * @author Bernhard Schussek + */ +class SimpleChoiceList extends ChoiceList +{ + /** + * Creates a new simple choice list. + * + * @param array $choices The array of choices with the choices as keys and + * the labels as values. Choices may also be given + * as hierarchy of unlimited depth by creating nested + * arrays. The title of the sub-hierarchy is stored + * in the array key pointing to the nested array. + * @param array $preferredChoices A flat array of choices that should be + * presented to the user with priority. + */ + public function __construct(array $choices, array $preferredChoices = array()) + { + // Flip preferred choices to speed up lookup + parent::__construct($choices, $choices, array_flip($preferredChoices)); + } + + /** + * {@inheritdoc} + */ + public function getChoicesForValues(array $values) + { + $values = $this->fixValues($values); + + // The values are identical to the choices, so we can just return them + // to improve performance a little bit + return $this->fixChoices(array_intersect($values, $this->getValues())); + } + + /** + * {@inheritdoc} + */ + public function getValuesForChoices(array $choices) + { + $choices = $this->fixChoices($choices); + + // The choices are identical to the values, so we can just return them + // to improve performance a little bit + return $this->fixValues(array_intersect($choices, $this->getValues())); + } + + /** + * Recursively adds the given choices to the list. + * + * Takes care of splitting the single $choices array passed in the + * constructor into choices and labels. + * + * @param array $bucketForPreferred The bucket where to store the preferred + * view objects. + * @param array $bucketForRemaining The bucket where to store the + * non-preferred view objects. + * @param array|\Traversable $choices The list of choices. + * @param array $labels Ignored. + * @param array $preferredChoices The preferred choices. + */ + protected function addChoices(array &$bucketForPreferred, array &$bucketForRemaining, $choices, array $labels, array $preferredChoices) + { + // Add choices to the nested buckets + foreach ($choices as $choice => $label) { + if (is_array($label)) { + // Don't do the work if the array is empty + if (count($label) > 0) { + $this->addChoiceGroup( + $choice, + $bucketForPreferred, + $bucketForRemaining, + $label, + $label, + $preferredChoices + ); + } + } else { + $this->addChoice( + $bucketForPreferred, + $bucketForRemaining, + $choice, + $label, + $preferredChoices + ); + } + } + } + + /** + * Returns whether the given choice should be preferred judging by the + * given array of preferred choices. + * + * Optimized for performance by treating the preferred choices as array + * where choices are stored in the keys. + * + * @param mixed $choice The choice to test. + * @param array $preferredChoices An array of preferred choices. + * + * @return Boolean Whether the choice is preferred. + */ + protected function isPreferred($choice, array $preferredChoices) + { + // Optimize performance over the default implementation + return isset($preferredChoices[$choice]); + } + + /** + * Converts the choice to a valid PHP array key. + * + * @param mixed $choice The choice. + * + * @return string|integer A valid PHP array key. + */ + protected function fixChoice($choice) + { + return $this->fixIndex($choice); + } + + /** + * {@inheritdoc} + */ + protected function fixChoices(array $choices) + { + return $this->fixIndices($choices); + } + + /** + * {@inheritdoc} + */ + protected function createValue($choice) + { + // Choices are guaranteed to be unique and scalar, so we can simply + // convert them to strings + return (string) $choice; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/CoreExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/CoreExtension.php new file mode 100644 index 0000000..bbcac4b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/CoreExtension.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\Extension\Core; + +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\PropertyAccess\PropertyAccess; + +/** + * Represents the main form extension, which loads the core functionality. + * + * @author Bernhard Schussek + */ +class CoreExtension extends AbstractExtension +{ + protected function loadTypes() + { + return array( + new Type\FormType(PropertyAccess::getPropertyAccessor()), + new Type\BirthdayType(), + new Type\CheckboxType(), + new Type\ChoiceType(), + new Type\CollectionType(), + new Type\CountryType(), + new Type\DateType(), + new Type\DateTimeType(), + new Type\EmailType(), + new Type\HiddenType(), + new Type\IntegerType(), + new Type\LanguageType(), + new Type\LocaleType(), + new Type\MoneyType(), + new Type\NumberType(), + new Type\PasswordType(), + new Type\PercentType(), + new Type\RadioType(), + new Type\RepeatedType(), + new Type\SearchType(), + new Type\TextareaType(), + new Type\TextType(), + new Type\TimeType(), + new Type\TimezoneType(), + new Type\UrlType(), + new Type\FileType(), + new Type\ButtonType(), + new Type\SubmitType(), + new Type\ResetType(), + new Type\CurrencyType(), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php new file mode 100644 index 0000000..d8bd9c7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.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\Form\Extension\Core\DataMapper; + +use Symfony\Component\Form\DataMapperInterface; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * A data mapper using property paths to read/write data. + * + * @author Bernhard Schussek + */ +class PropertyPathMapper implements DataMapperInterface +{ + /** + * @var PropertyAccessorInterface + */ + private $propertyAccessor; + + /** + * Creates a new property path mapper. + * + * @param PropertyAccessorInterface $propertyAccessor + */ + public function __construct(PropertyAccessorInterface $propertyAccessor = null) + { + $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor(); + } + + /** + * {@inheritdoc} + */ + public function mapDataToForms($data, $forms) + { + if (null === $data || array() === $data) { + return; + } + + if (!is_array($data) && !is_object($data)) { + throw new UnexpectedTypeException($data, 'object, array or empty'); + } + + foreach ($forms as $form) { + $propertyPath = $form->getPropertyPath(); + $config = $form->getConfig(); + + if (null !== $propertyPath && $config->getMapped()) { + $form->setData($this->propertyAccessor->getValue($data, $propertyPath)); + } + } + } + + /** + * {@inheritdoc} + */ + public function mapFormsToData($forms, &$data) + { + if (null === $data) { + return; + } + + if (!is_array($data) && !is_object($data)) { + throw new UnexpectedTypeException($data, 'object, array or empty'); + } + + foreach ($forms as $form) { + $propertyPath = $form->getPropertyPath(); + $config = $form->getConfig(); + + // Write-back is disabled if the form is not synchronized (transformation failed) + // and if the form is disabled (modification not allowed) + if (null !== $propertyPath && $config->getMapped() && $form->isSynchronized() && !$form->isDisabled()) { + // If the data is identical to the value in $data, we are + // dealing with a reference + if (!is_object($data) || !$config->getByReference() || $form->getData() !== $this->propertyAccessor->getValue($data, $propertyPath)) { + $this->propertyAccessor->setValue($data, $propertyPath, $form->getData()); + } + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ArrayToPartsTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ArrayToPartsTransformer.php new file mode 100644 index 0000000..fc080f2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ArrayToPartsTransformer.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\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * @author Bernhard Schussek + */ +class ArrayToPartsTransformer implements DataTransformerInterface +{ + private $partMapping; + + public function __construct(array $partMapping) + { + $this->partMapping = $partMapping; + } + + public function transform($array) + { + if (null === $array) { + $array = array(); + } + + if (!is_array($array) ) { + throw new TransformationFailedException('Expected an array.'); + } + + $result = array(); + + foreach ($this->partMapping as $partKey => $originalKeys) { + if (empty($array)) { + $result[$partKey] = null; + } else { + $result[$partKey] = array_intersect_key($array, array_flip($originalKeys)); + } + } + + return $result; + } + + public function reverseTransform($array) + { + if (!is_array($array) ) { + throw new TransformationFailedException('Expected an array.'); + } + + $result = array(); + $emptyKeys = array(); + + foreach ($this->partMapping as $partKey => $originalKeys) { + if (!empty($array[$partKey])) { + foreach ($originalKeys as $originalKey) { + if (isset($array[$partKey][$originalKey])) { + $result[$originalKey] = $array[$partKey][$originalKey]; + } + } + } else { + $emptyKeys[] = $partKey; + } + } + + if (count($emptyKeys) > 0) { + if (count($emptyKeys) === count($this->partMapping)) { + // All parts empty + return null; + } + + throw new TransformationFailedException( + sprintf('The keys "%s" should not be empty', implode('", "', $emptyKeys) + )); + } + + return $result; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php new file mode 100644 index 0000000..e4e8932 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.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\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +abstract class BaseDateTimeTransformer implements DataTransformerInterface +{ + protected static $formats = array( + \IntlDateFormatter::NONE, + \IntlDateFormatter::FULL, + \IntlDateFormatter::LONG, + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::SHORT, + ); + + protected $inputTimezone; + + protected $outputTimezone; + + /** + * Constructor. + * + * @param string $inputTimezone The name of the input timezone + * @param string $outputTimezone The name of the output timezone + * + * @throws UnexpectedTypeException if a timezone is not a string + */ + public function __construct($inputTimezone = null, $outputTimezone = null) + { + if (!is_string($inputTimezone) && null !== $inputTimezone) { + throw new UnexpectedTypeException($inputTimezone, 'string'); + } + + if (!is_string($outputTimezone) && null !== $outputTimezone) { + throw new UnexpectedTypeException($outputTimezone, 'string'); + } + + $this->inputTimezone = $inputTimezone ?: date_default_timezone_get(); + $this->outputTimezone = $outputTimezone ?: date_default_timezone_get(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php new file mode 100644 index 0000000..95e7332 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.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\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between a Boolean and a string. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class BooleanToStringTransformer implements DataTransformerInterface +{ + /** + * The value emitted upon transform if the input is true + * @var string + */ + private $trueValue; + + /** + * Sets the value emitted upon transform if the input is true. + * + * @param string $trueValue + */ + public function __construct($trueValue) + { + $this->trueValue = $trueValue; + } + + /** + * Transforms a Boolean into a string. + * + * @param Boolean $value Boolean value. + * + * @return string String value. + * + * @throws TransformationFailedException If the given value is not a Boolean. + */ + public function transform($value) + { + if (null === $value) { + return null; + } + + if (!is_bool($value)) { + throw new TransformationFailedException('Expected a Boolean.'); + } + + return true === $value ? $this->trueValue : null; + } + + /** + * Transforms a string into a Boolean. + * + * @param string $value String value. + * + * @return Boolean Boolean value. + * + * @throws TransformationFailedException If the given value is not a string. + */ + public function reverseTransform($value) + { + if (null === $value) { + return false; + } + + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + return true; + } + +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToBooleanArrayTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToBooleanArrayTransformer.php new file mode 100644 index 0000000..79b3f7a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToBooleanArrayTransformer.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\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * @author Bernhard Schussek + */ +class ChoiceToBooleanArrayTransformer implements DataTransformerInterface +{ + private $choiceList; + + private $placeholderPresent; + + /** + * Constructor. + * + * @param ChoiceListInterface $choiceList + * @param Boolean $placeholderPresent + */ + public function __construct(ChoiceListInterface $choiceList, $placeholderPresent) + { + $this->choiceList = $choiceList; + $this->placeholderPresent = $placeholderPresent; + } + + /** + * Transforms a single choice to a format appropriate for the nested + * checkboxes/radio buttons. + * + * The result is an array with the options as keys and true/false as values, + * depending on whether a given option is selected. If this field is rendered + * as select tag, the value is not modified. + * + * @param mixed $choice An array if "multiple" is set to true, a scalar + * value otherwise. + * + * @return mixed An array + * + * @throws TransformationFailedException If the given value is not scalar or + * if the choices can not be retrieved. + */ + public function transform($choice) + { + try { + $values = $this->choiceList->getValues(); + } catch (\Exception $e) { + throw new TransformationFailedException('Can not get the choice list', $e->getCode(), $e); + } + + $index = current($this->choiceList->getIndicesForChoices(array($choice))); + + foreach ($values as $i => $value) { + $values[$i] = $i === $index; + } + + if ($this->placeholderPresent) { + $values['placeholder'] = false === $index; + } + + return $values; + } + + /** + * Transforms a checkbox/radio button array to a single choice. + * + * The input value is an array with the choices as keys and true/false as + * values, depending on whether a given choice is selected. The output + * is the selected choice. + * + * @param array $values An array of values + * + * @return mixed A scalar value + * + * @throws TransformationFailedException If the given value is not an array, + * if the recuperation of the choices + * fails or if some choice can't be + * found. + */ + public function reverseTransform($values) + { + if (!is_array($values)) { + throw new TransformationFailedException('Expected an array.'); + } + + try { + $choices = $this->choiceList->getChoices(); + } catch (\Exception $e) { + throw new TransformationFailedException('Can not get the choice list', $e->getCode(), $e); + } + + foreach ($values as $i => $selected) { + if ($selected) { + if (isset($choices[$i])) { + return $choices[$i] === '' ? null : $choices[$i]; + } elseif ($this->placeholderPresent && 'placeholder' === $i) { + return null; + } else { + throw new TransformationFailedException(sprintf('The choice "%s" does not exist', $i)); + } + } + } + + return null; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php new file mode 100644 index 0000000..5a81855 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToValueTransformer.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\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; + +/** + * @author Bernhard Schussek + */ +class ChoiceToValueTransformer implements DataTransformerInterface +{ + private $choiceList; + + /** + * Constructor. + * + * @param ChoiceListInterface $choiceList + */ + public function __construct(ChoiceListInterface $choiceList) + { + $this->choiceList = $choiceList; + } + + public function transform($choice) + { + return (string) current($this->choiceList->getValuesForChoices(array($choice))); + } + + public function reverseTransform($value) + { + if (null !== $value && !is_scalar($value)) { + throw new TransformationFailedException('Expected a scalar.'); + } + + // These are now valid ChoiceList values, so we can return null + // right away + if ('' === $value || null === $value) { + return null; + } + + $choices = $this->choiceList->getChoicesForValues(array($value)); + + if (1 !== count($choices)) { + throw new TransformationFailedException(sprintf('The choice "%s" does not exist or is not unique', $value)); + } + + $choice = current($choices); + + return '' === $choice ? null : $choice; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToBooleanArrayTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToBooleanArrayTransformer.php new file mode 100644 index 0000000..a13c0d4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToBooleanArrayTransformer.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\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * @author Bernhard Schussek + */ +class ChoicesToBooleanArrayTransformer implements DataTransformerInterface +{ + private $choiceList; + + public function __construct(ChoiceListInterface $choiceList) + { + $this->choiceList = $choiceList; + } + + /** + * Transforms an array of choices to a format appropriate for the nested + * checkboxes/radio buttons. + * + * The result is an array with the options as keys and true/false as values, + * depending on whether a given option is selected. If this field is rendered + * as select tag, the value is not modified. + * + * @param mixed $array An array + * + * @return mixed An array + * + * @throws TransformationFailedException If the given value is not an array + * or if the choices can not be retrieved. + */ + public function transform($array) + { + if (null === $array) { + return array(); + } + + if (!is_array($array)) { + throw new TransformationFailedException('Expected an array.'); + } + + try { + $values = $this->choiceList->getValues(); + } catch (\Exception $e) { + throw new TransformationFailedException('Can not get the choice list', $e->getCode(), $e); + } + + $indexMap = array_flip($this->choiceList->getIndicesForChoices($array)); + + foreach ($values as $i => $value) { + $values[$i] = isset($indexMap[$i]); + } + + return $values; + } + + /** + * Transforms a checkbox/radio button array to an array of choices. + * + * The input value is an array with the choices as keys and true/false as + * values, depending on whether a given choice is selected. The output + * is an array with the selected choices. + * + * @param mixed $values An array + * + * @return mixed An array + * + * @throws TransformationFailedException If the given value is not an array, + * if the recuperation of the choices + * fails or if some choice can't be + * found. + */ + public function reverseTransform($values) + { + if (!is_array($values)) { + throw new TransformationFailedException('Expected an array.'); + } + + try { + $choices = $this->choiceList->getChoices(); + } catch (\Exception $e) { + throw new TransformationFailedException('Can not get the choice list', $e->getCode(), $e); + } + + $result = array(); + $unknown = array(); + + foreach ($values as $i => $selected) { + if ($selected) { + if (isset($choices[$i])) { + $result[] = $choices[$i]; + } else { + $unknown[] = $i; + } + } + } + + if (count($unknown) > 0) { + throw new TransformationFailedException(sprintf('The choices "%s" were not found', implode('", "', $unknown))); + } + + return $result; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php new file mode 100644 index 0000000..4492865 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.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\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; + +/** + * @author Bernhard Schussek + */ +class ChoicesToValuesTransformer implements DataTransformerInterface +{ + private $choiceList; + + /** + * Constructor. + * + * @param ChoiceListInterface $choiceList + */ + public function __construct(ChoiceListInterface $choiceList) + { + $this->choiceList = $choiceList; + } + + /** + * @param array $array + * + * @return array + * + * @throws TransformationFailedException If the given value is not an array. + */ + public function transform($array) + { + if (null === $array) { + return array(); + } + + if (!is_array($array)) { + throw new TransformationFailedException('Expected an array.'); + } + + return $this->choiceList->getValuesForChoices($array); + } + + /** + * @param array $array + * + * @return array + * + * @throws TransformationFailedException If the given value is not an array + * or if no matching choice could be + * found for some given value. + */ + public function reverseTransform($array) + { + if (null === $array) { + return array(); + } + + if (!is_array($array)) { + throw new TransformationFailedException('Expected an array.'); + } + + $choices = $this->choiceList->getChoicesForValues($array); + + if (count($choices) !== count($array)) { + throw new TransformationFailedException('Could not find all matching choices for the given values'); + } + + return $choices; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DataTransformerChain.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DataTransformerChain.php new file mode 100644 index 0000000..9cc185e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DataTransformerChain.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\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Passes a value through multiple value transformers + * + * @author Bernhard Schussek + */ +class DataTransformerChain implements DataTransformerInterface +{ + /** + * The value transformers + * @var DataTransformerInterface[] + */ + protected $transformers; + + /** + * Uses the given value transformers to transform values + * + * @param array $transformers + */ + public function __construct(array $transformers) + { + $this->transformers = $transformers; + } + + /** + * Passes the value through the transform() method of all nested transformers + * + * The transformers receive the value in the same order as they were passed + * to the constructor. Each transformer receives the result of the previous + * transformer as input. The output of the last transformer is returned + * by this method. + * + * @param mixed $value The original value + * + * @return mixed The transformed value + * + * @throws TransformationFailedException + */ + public function transform($value) + { + foreach ($this->transformers as $transformer) { + $value = $transformer->transform($value); + } + + return $value; + } + + /** + * Passes the value through the reverseTransform() method of all nested + * transformers + * + * The transformers receive the value in the reverse order as they were passed + * to the constructor. Each transformer receives the result of the previous + * transformer as input. The output of the last transformer is returned + * by this method. + * + * @param mixed $value The transformed value + * + * @return mixed The reverse-transformed value + * + * @throws TransformationFailedException + */ + public function reverseTransform($value) + { + for ($i = count($this->transformers) - 1; $i >= 0; --$i) { + $value = $this->transformers[$i]->reverseTransform($value); + } + + return $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php new file mode 100644 index 0000000..34af282 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.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\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * Transforms between a normalized time and a localized time string/array. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class DateTimeToArrayTransformer extends BaseDateTimeTransformer +{ + private $pad; + + private $fields; + + /** + * Constructor. + * + * @param string $inputTimezone The input timezone + * @param string $outputTimezone The output timezone + * @param array $fields The date fields + * @param Boolean $pad Whether to use padding + * + * @throws UnexpectedTypeException if a timezone is not a string + */ + public function __construct($inputTimezone = null, $outputTimezone = null, array $fields = null, $pad = false) + { + parent::__construct($inputTimezone, $outputTimezone); + + if (null === $fields) { + $fields = array('year', 'month', 'day', 'hour', 'minute', 'second'); + } + + $this->fields = $fields; + $this->pad = (Boolean) $pad; + } + + /** + * Transforms a normalized date into a localized date. + * + * @param \DateTime $dateTime Normalized date. + * + * @return array Localized date. + * + * @throws TransformationFailedException If the given value is not an + * instance of \DateTime or if the + * output timezone is not supported. + */ + public function transform($dateTime) + { + if (null === $dateTime) { + return array_intersect_key(array( + 'year' => '', + 'month' => '', + 'day' => '', + 'hour' => '', + 'minute' => '', + 'second' => '', + ), array_flip($this->fields)); + } + + if (!$dateTime instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + $dateTime = clone $dateTime; + if ($this->inputTimezone !== $this->outputTimezone) { + try { + $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + } + + $result = array_intersect_key(array( + 'year' => $dateTime->format('Y'), + 'month' => $dateTime->format('m'), + 'day' => $dateTime->format('d'), + 'hour' => $dateTime->format('H'), + 'minute' => $dateTime->format('i'), + 'second' => $dateTime->format('s'), + ), array_flip($this->fields)); + + if (!$this->pad) { + foreach ($result as &$entry) { + // remove leading zeros + $entry = (string) (int) $entry; + } + } + + return $result; + } + + /** + * Transforms a localized date into a normalized date. + * + * @param array $value Localized date + * + * @return \DateTime Normalized date + * + * @throws TransformationFailedException If the given value is not an array, + * if the value could not be transformed + * or if the input timezone is not + * supported. + */ + public function reverseTransform($value) + { + if (null === $value) { + return null; + } + + if (!is_array($value)) { + throw new TransformationFailedException('Expected an array.'); + } + + if ('' === implode('', $value)) { + return null; + } + + $emptyFields = array(); + + foreach ($this->fields as $field) { + if (!isset($value[$field])) { + $emptyFields[] = $field; + } + } + + if (count($emptyFields) > 0) { + throw new TransformationFailedException( + sprintf('The fields "%s" should not be empty', implode('", "', $emptyFields) + )); + } + + if (isset($value['month']) && !ctype_digit($value['month']) && !is_int($value['month'])) { + throw new TransformationFailedException('This month is invalid'); + } + + if (isset($value['day']) && !ctype_digit($value['day']) && !is_int($value['day'])) { + throw new TransformationFailedException('This day is invalid'); + } + + if (isset($value['year']) && !ctype_digit($value['year']) && !is_int($value['year'])) { + throw new TransformationFailedException('This year is invalid'); + } + + if (!empty($value['month']) && !empty($value['day']) && !empty($value['year']) && false === checkdate($value['month'], $value['day'], $value['year'])) { + throw new TransformationFailedException('This is an invalid date'); + } + + try { + $dateTime = new \DateTime(sprintf( + '%s-%s-%s %s:%s:%s %s', + empty($value['year']) ? '1970' : $value['year'], + empty($value['month']) ? '1' : $value['month'], + empty($value['day']) ? '1' : $value['day'], + empty($value['hour']) ? '0' : $value['hour'], + empty($value['minute']) ? '0' : $value['minute'], + empty($value['second']) ? '0' : $value['second'], + $this->outputTimezone + )); + + if ($this->inputTimezone !== $this->outputTimezone) { + $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone)); + } + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + return $dateTime; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php new file mode 100644 index 0000000..d755e48 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.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\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * Transforms between a normalized time and a localized time string + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer +{ + private $dateFormat; + private $timeFormat; + private $pattern; + private $calendar; + + /** + * Constructor. + * + * @see BaseDateTimeTransformer::formats for available format options + * + * @param string $inputTimezone The name of the input timezone + * @param string $outputTimezone The name of the output timezone + * @param integer $dateFormat The date format + * @param integer $timeFormat The time format + * @param integer $calendar One of the \IntlDateFormatter calendar constants + * @param string $pattern A pattern to pass to \IntlDateFormatter + * + * @throws UnexpectedTypeException If a format is not supported or if a timezone is not a string + */ + public function __construct($inputTimezone = null, $outputTimezone = null, $dateFormat = null, $timeFormat = null, $calendar = \IntlDateFormatter::GREGORIAN, $pattern = null) + { + parent::__construct($inputTimezone, $outputTimezone); + + if (null === $dateFormat) { + $dateFormat = \IntlDateFormatter::MEDIUM; + } + + if (null === $timeFormat) { + $timeFormat = \IntlDateFormatter::SHORT; + } + + if (!in_array($dateFormat, self::$formats, true)) { + throw new UnexpectedTypeException($dateFormat, implode('", "', self::$formats)); + } + + if (!in_array($timeFormat, self::$formats, true)) { + throw new UnexpectedTypeException($timeFormat, implode('", "', self::$formats)); + } + + $this->dateFormat = $dateFormat; + $this->timeFormat = $timeFormat; + $this->calendar = $calendar; + $this->pattern = $pattern; + } + + /** + * Transforms a normalized date into a localized date string/array. + * + * @param \DateTime $dateTime Normalized date. + * + * @return string|array Localized date string/array. + * + * @throws TransformationFailedException If the given value is not an instance + * of \DateTime or if the date could not + * be transformed. + */ + public function transform($dateTime) + { + if (null === $dateTime) { + return ''; + } + + if (!$dateTime instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + // convert time to UTC before passing it to the formatter + $dateTime = clone $dateTime; + if ('UTC' !== $this->inputTimezone) { + $dateTime->setTimezone(new \DateTimeZone('UTC')); + } + + $value = $this->getIntlDateFormatter()->format((int) $dateTime->format('U')); + + if (intl_get_error_code() != 0) { + throw new TransformationFailedException(intl_get_error_message()); + } + + return $value; + } + + /** + * Transforms a localized date string/array into a normalized date. + * + * @param string|array $value Localized date string/array + * + * @return \DateTime Normalized date + * + * @throws TransformationFailedException if the given value is not a string, + * if the date could not be parsed or + * if the input timezone is not supported + */ + public function reverseTransform($value) + { + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + if ('' === $value) { + return null; + } + + $timestamp = $this->getIntlDateFormatter()->parse($value); + + if (intl_get_error_code() != 0) { + throw new TransformationFailedException(intl_get_error_message()); + } + + try { + // read timestamp into DateTime object - the formatter delivers in UTC + $dateTime = new \DateTime(sprintf('@%s UTC', $timestamp)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + if ('UTC' !== $this->inputTimezone) { + try { + $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + } + + return $dateTime; + } + + /** + * Returns a preconfigured IntlDateFormatter instance + * + * @return \IntlDateFormatter + */ + protected function getIntlDateFormatter() + { + $dateFormat = $this->dateFormat; + $timeFormat = $this->timeFormat; + $timezone = $this->outputTimezone; + $calendar = $this->calendar; + $pattern = $this->pattern; + + $intlDateFormatter = new \IntlDateFormatter(\Locale::getDefault(), $dateFormat, $timeFormat, $timezone, $calendar, $pattern); + $intlDateFormatter->setLenient(false); + + return $intlDateFormatter; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php new file mode 100644 index 0000000..0eb0742 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.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\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * @author Bernhard Schussek + */ +class DateTimeToRfc3339Transformer extends BaseDateTimeTransformer +{ + /** + * {@inheritDoc} + */ + public function transform($dateTime) + { + if (null === $dateTime) { + return ''; + } + + if (!$dateTime instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + if ($this->inputTimezone !== $this->outputTimezone) { + $dateTime = clone $dateTime; + $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone)); + } + + return preg_replace('/\+00:00$/', 'Z', $dateTime->format('c')); + } + + /** + * {@inheritDoc} + */ + public function reverseTransform($rfc3339) + { + if (!is_string($rfc3339)) { + throw new TransformationFailedException('Expected a string.'); + } + + if ('' === $rfc3339) { + return null; + } + + try { + $dateTime = new \DateTime($rfc3339); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + if ($this->outputTimezone !== $this->inputTimezone) { + try { + $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + } + + if (preg_match('/(\d{4})-(\d{2})-(\d{2})/', $rfc3339, $matches)) { + if (!checkdate($matches[2], $matches[3], $matches[1])) { + throw new TransformationFailedException(sprintf( + 'The date "%s-%s-%s" is not a valid date.', + $matches[1], + $matches[2], + $matches[3] + )); + } + } + + return $dateTime; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php new file mode 100644 index 0000000..131f45c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php @@ -0,0 +1,231 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * Transforms between a date string and a DateTime object + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class DateTimeToStringTransformer extends BaseDateTimeTransformer +{ + /** + * Format used for generating strings + * @var string + */ + private $generateFormat; + + /** + * Format used for parsing strings + * + * Different than the {@link $generateFormat} because formats for parsing + * support additional characters in PHP that are not supported for + * generating strings. + * + * @var string + */ + private $parseFormat; + + /** + * Whether to parse by appending a pipe "|" to the parse format. + * + * This only works as of PHP 5.3.7. + * + * @var Boolean + */ + private $parseUsingPipe; + + /** + * Transforms a \DateTime instance to a string + * + * @see \DateTime::format() for supported formats + * + * @param string $inputTimezone The name of the input timezone + * @param string $outputTimezone The name of the output timezone + * @param string $format The date format + * @param Boolean $parseUsingPipe Whether to parse by appending a pipe "|" to the parse format + * + * @throws UnexpectedTypeException if a timezone is not a string + */ + public function __construct($inputTimezone = null, $outputTimezone = null, $format = 'Y-m-d H:i:s', $parseUsingPipe = null) + { + parent::__construct($inputTimezone, $outputTimezone); + + $this->generateFormat = $this->parseFormat = $format; + + // The pipe in the parser pattern only works as of PHP 5.3.7 + // See http://bugs.php.net/54316 + $this->parseUsingPipe = null === $parseUsingPipe + ? version_compare(phpversion(), '5.3.7', '>=') + : $parseUsingPipe; + + // See http://php.net/manual/en/datetime.createfromformat.php + // The character "|" in the format makes sure that the parts of a date + // that are *not* specified in the format are reset to the corresponding + // values from 1970-01-01 00:00:00 instead of the current time. + // Without "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 12:32:47", + // where the time corresponds to the current server time. + // With "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 00:00:00", + // which is at least deterministic and thus used here. + if ($this->parseUsingPipe && false === strpos($this->parseFormat, '|')) { + $this->parseFormat .= '|'; + } + } + + /** + * Transforms a DateTime object into a date string with the configured format + * and timezone + * + * @param \DateTime $value A DateTime object + * + * @return string A value as produced by PHP's date() function + * + * @throws TransformationFailedException If the given value is not a \DateTime + * instance or if the output timezone + * is not supported. + */ + public function transform($value) + { + if (null === $value) { + return ''; + } + + if (!$value instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + $value = clone $value; + try { + $value->setTimezone(new \DateTimeZone($this->outputTimezone)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + return $value->format($this->generateFormat); + } + + /** + * Transforms a date string in the configured timezone into a DateTime object. + * + * @param string $value A value as produced by PHP's date() function + * + * @return \DateTime An instance of \DateTime + * + * @throws TransformationFailedException If the given value is not a string, + * if the date could not be parsed or + * if the input timezone is not supported. + */ + public function reverseTransform($value) + { + if (empty($value)) { + return null; + } + + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + try { + $outputTz = new \DateTimeZone($this->outputTimezone); + $dateTime = \DateTime::createFromFormat($this->parseFormat, $value, $outputTz); + + $lastErrors = \DateTime::getLastErrors(); + + if (0 < $lastErrors['warning_count'] || 0 < $lastErrors['error_count']) { + throw new TransformationFailedException( + implode(', ', array_merge( + array_values($lastErrors['warnings']), + array_values($lastErrors['errors']) + )) + ); + } + + // On PHP versions < 5.3.7 we need to emulate the pipe operator + // and reset parts not given in the format to their equivalent + // of the UNIX base timestamp. + if (!$this->parseUsingPipe) { + list($year, $month, $day, $hour, $minute, $second) = explode('-', $dateTime->format('Y-m-d-H-i-s')); + + // Check which of the date parts are present in the pattern + preg_match( + '/(' . + '(?P[djDl])|' . + '(?P[FMmn])|' . + '(?P[Yy])|' . + '(?P[ghGH])|' . + '(?Pi)|' . + '(?Ps)|' . + '(?Pz)|' . + '(?PU)|' . + '[^djDlFMmnYyghGHiszU]' . + ')*/', + $this->parseFormat, + $matches + ); + + // preg_match() does not guarantee to set all indices, so + // set them unless given + $matches = array_merge(array( + 'day' => false, + 'month' => false, + 'year' => false, + 'hour' => false, + 'minute' => false, + 'second' => false, + 'dayofyear' => false, + 'timestamp' => false, + ), $matches); + + // Reset all parts that don't exist in the format to the + // corresponding part of the UNIX base timestamp + if (!$matches['timestamp']) { + if (!$matches['dayofyear']) { + if (!$matches['day']) { + $day = 1; + } + if (!$matches['month']) { + $month = 1; + } + } + if (!$matches['year']) { + $year = 1970; + } + if (!$matches['hour']) { + $hour = 0; + } + if (!$matches['minute']) { + $minute = 0; + } + if (!$matches['second']) { + $second = 0; + } + $dateTime->setDate($year, $month, $day); + $dateTime->setTime($hour, $minute, $second); + } + } + + if ($this->inputTimezone !== $this->outputTimezone) { + $dateTime->setTimeZone(new \DateTimeZone($this->inputTimezone)); + } + } catch (TransformationFailedException $e) { + throw $e; + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + return $dateTime; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php new file mode 100644 index 0000000..d2ca660 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.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\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between a timestamp and a DateTime object + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class DateTimeToTimestampTransformer extends BaseDateTimeTransformer +{ + /** + * Transforms a DateTime object into a timestamp in the configured timezone. + * + * @param \DateTime $value A \DateTime object + * + * @return integer A timestamp + * + * @throws TransformationFailedException If the given value is not an instance + * of \DateTime or if the output + * timezone is not supported. + */ + public function transform($value) + { + if (null === $value) { + return null; + } + + if (!$value instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + $value = clone $value; + try { + $value->setTimezone(new \DateTimeZone($this->outputTimezone)); + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + return (int) $value->format('U'); + } + + /** + * Transforms a timestamp in the configured timezone into a DateTime object + * + * @param string $value A timestamp + * + * @return \DateTime A \DateTime object + * + * @throws TransformationFailedException If the given value is not a timestamp + * or if the given timestamp is invalid. + */ + public function reverseTransform($value) + { + if (null === $value) { + return null; + } + + if (!is_numeric($value)) { + throw new TransformationFailedException('Expected a numeric.'); + } + + try { + $dateTime = new \DateTime(); + $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone)); + $dateTime->setTimestamp($value); + + if ($this->inputTimezone !== $this->outputTimezone) { + $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone)); + } + } catch (\Exception $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } + + return $dateTime; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php new file mode 100644 index 0000000..6bb48a9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.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\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between an integer and a localized number with grouping + * (each thousand) and comma separators. + * + * @author Bernhard Schussek + */ +class IntegerToLocalizedStringTransformer extends NumberToLocalizedStringTransformer +{ + /** + * {@inheritDoc} + */ + public function reverseTransform($value) + { + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + if ('' === $value) { + return null; + } + + if ('NaN' === $value) { + throw new TransformationFailedException('"NaN" is not a valid integer'); + } + + $formatter = $this->getNumberFormatter(); + $value = $formatter->parse( + $value, + PHP_INT_SIZE == 8 ? $formatter::TYPE_INT64 : $formatter::TYPE_INT32 + ); + + if (intl_is_failure($formatter->getErrorCode())) { + throw new TransformationFailedException($formatter->getErrorMessage()); + } + + return $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php new file mode 100644 index 0000000..5b8e9d9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.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\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between a normalized format and a localized money string. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class MoneyToLocalizedStringTransformer extends NumberToLocalizedStringTransformer +{ + + private $divisor; + + public function __construct($precision = null, $grouping = null, $roundingMode = null, $divisor = null) + { + if (null === $grouping) { + $grouping = true; + } + + if (null === $precision) { + $precision = 2; + } + + parent::__construct($precision, $grouping, $roundingMode); + + if (null === $divisor) { + $divisor = 1; + } + + $this->divisor = $divisor; + } + + /** + * Transforms a normalized format into a localized money string. + * + * @param number $value Normalized number + * + * @return string Localized money string. + * + * @throws TransformationFailedException If the given value is not numeric or + * if the value can not be transformed. + */ + public function transform($value) + { + if (null !== $value) { + if (!is_numeric($value)) { + throw new TransformationFailedException('Expected a numeric.'); + } + + $value /= $this->divisor; + } + + return parent::transform($value); + } + + /** + * Transforms a localized money string into a normalized format. + * + * @param string $value Localized money string + * + * @return number Normalized number + * + * @throws TransformationFailedException If the given value is not a string + * or if the value can not be transformed. + */ + public function reverseTransform($value) + { + $value = parent::reverseTransform($value); + + if (null !== $value) { + $value *= $this->divisor; + } + + return $value; + } + +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php new file mode 100644 index 0000000..689c74a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.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\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between a number type and a localized number with grouping + * (each thousand) and comma separators. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class NumberToLocalizedStringTransformer implements DataTransformerInterface +{ + const ROUND_FLOOR = \NumberFormatter::ROUND_FLOOR; + const ROUND_DOWN = \NumberFormatter::ROUND_DOWN; + const ROUND_HALFDOWN = \NumberFormatter::ROUND_HALFDOWN; + const ROUND_HALFEVEN = \NumberFormatter::ROUND_HALFEVEN; + const ROUND_HALFUP = \NumberFormatter::ROUND_HALFUP; + const ROUND_UP = \NumberFormatter::ROUND_UP; + const ROUND_CEILING = \NumberFormatter::ROUND_CEILING; + + protected $precision; + + protected $grouping; + + protected $roundingMode; + + public function __construct($precision = null, $grouping = null, $roundingMode = null) + { + if (null === $grouping) { + $grouping = false; + } + + if (null === $roundingMode) { + $roundingMode = self::ROUND_HALFUP; + } + + $this->precision = $precision; + $this->grouping = $grouping; + $this->roundingMode = $roundingMode; + } + + /** + * Transforms a number type into localized number. + * + * @param integer|float $value Number value. + * + * @return string Localized value. + * + * @throws TransformationFailedException If the given value is not numeric + * or if the value can not be transformed. + */ + public function transform($value) + { + if (null === $value) { + return ''; + } + + if (!is_numeric($value)) { + throw new TransformationFailedException('Expected a numeric.'); + } + + $formatter = $this->getNumberFormatter(); + $value = $formatter->format($value); + + if (intl_is_failure($formatter->getErrorCode())) { + throw new TransformationFailedException($formatter->getErrorMessage()); + } + + // Convert fixed spaces to normal ones + $value = str_replace("\xc2\xa0", ' ', $value); + + return $value; + } + + /** + * Transforms a localized number into an integer or float + * + * @param string $value The localized value + * + * @return integer|float The numeric value + * + * @throws TransformationFailedException If the given value is not a string + * or if the value can not be transformed. + */ + public function reverseTransform($value) + { + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + if ('' === $value) { + return null; + } + + if ('NaN' === $value) { + throw new TransformationFailedException('"NaN" is not a valid number'); + } + + $position = 0; + $formatter = $this->getNumberFormatter(); + $groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL); + $decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); + + if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) { + $value = str_replace('.', $decSep, $value); + } + + if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) { + $value = str_replace(',', $decSep, $value); + } + + $result = $formatter->parse($value, \NumberFormatter::TYPE_DOUBLE, $position); + + if (intl_is_failure($formatter->getErrorCode())) { + throw new TransformationFailedException($formatter->getErrorMessage()); + } + + if ($result >= INF || $result <= -INF) { + throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like'); + } + + if (function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($value)) { + $strlen = function ($string) use ($encoding) { + return mb_strlen($string, $encoding); + }; + $substr = function ($string, $offset, $length) use ($encoding) { + return mb_substr($string, $offset, $length, $encoding); + }; + } else { + $strlen = 'strlen'; + $substr = 'substr'; + } + + $length = $strlen($value); + + // After parsing, position holds the index of the character where the + // parsing stopped + if ($position < $length) { + // Check if there are unrecognized characters at the end of the + // number (excluding whitespace characters) + $remainder = trim($substr($value, $position, $length), " \t\n\r\0\x0b\xc2\xa0"); + + if ('' !== $remainder) { + throw new TransformationFailedException( + sprintf('The number contains unrecognized characters: "%s"', $remainder) + ); + } + } + + return $result; + } + + /** + * Returns a preconfigured \NumberFormatter instance + * + * @return \NumberFormatter + */ + protected function getNumberFormatter() + { + $formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL); + + if (null !== $this->precision) { + $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->precision); + $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $this->roundingMode); + } + + $formatter->setAttribute(\NumberFormatter::GROUPING_USED, $this->grouping); + + return $formatter; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php new file mode 100644 index 0000000..e099d43 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.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\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * Transforms between a normalized format (integer or float) and a percentage value. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + */ +class PercentToLocalizedStringTransformer implements DataTransformerInterface +{ + const FRACTIONAL = 'fractional'; + const INTEGER = 'integer'; + + protected static $types = array( + self::FRACTIONAL, + self::INTEGER, + ); + + private $type; + + private $precision; + + /** + * Constructor. + * + * @see self::$types for a list of supported types + * + * @param integer $precision The precision + * @param string $type One of the supported types + * + * @throws UnexpectedTypeException if the given value of type is unknown + */ + public function __construct($precision = null, $type = null) + { + if (null === $precision) { + $precision = 0; + } + + if (null === $type) { + $type = self::FRACTIONAL; + } + + if (!in_array($type, self::$types, true)) { + throw new UnexpectedTypeException($type, implode('", "', self::$types)); + } + + $this->type = $type; + $this->precision = $precision; + } + + /** + * Transforms between a normalized format (integer or float) into a percentage value. + * + * @param number $value Normalized value + * + * @return number Percentage value + * + * @throws TransformationFailedException If the given value is not numeric or + * if the value could not be transformed. + */ + public function transform($value) + { + if (null === $value) { + return ''; + } + + if (!is_numeric($value)) { + throw new TransformationFailedException('Expected a numeric.'); + } + + if (self::FRACTIONAL == $this->type) { + $value *= 100; + } + + $formatter = $this->getNumberFormatter(); + $value = $formatter->format($value); + + if (intl_is_failure($formatter->getErrorCode())) { + throw new TransformationFailedException($formatter->getErrorMessage()); + } + + // replace the UTF-8 non break spaces + return $value; + } + + /** + * Transforms between a percentage value into a normalized format (integer or float). + * + * @param number $value Percentage value. + * + * @return number Normalized value. + * + * @throws TransformationFailedException If the given value is not a string or + * if the value could not be transformed. + */ + public function reverseTransform($value) + { + if (!is_string($value)) { + throw new TransformationFailedException('Expected a string.'); + } + + if ('' === $value) { + return null; + } + + $formatter = $this->getNumberFormatter(); + // replace normal spaces so that the formatter can read them + $value = $formatter->parse(str_replace(' ', ' ', $value)); + + if (intl_is_failure($formatter->getErrorCode())) { + throw new TransformationFailedException($formatter->getErrorMessage()); + } + + if (self::FRACTIONAL == $this->type) { + $value /= 100; + } + + return $value; + } + + /** + * Returns a preconfigured \NumberFormatter instance + * + * @return \NumberFormatter + */ + protected function getNumberFormatter() + { + $formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL); + + $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->precision); + + return $formatter; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php new file mode 100644 index 0000000..c34a013 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.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\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * @author Bernhard Schussek + */ +class ValueToDuplicatesTransformer implements DataTransformerInterface +{ + private $keys; + + public function __construct(array $keys) + { + $this->keys = $keys; + } + + /** + * Duplicates the given value through the array. + * + * @param mixed $value The value + * + * @return array The array + */ + public function transform($value) + { + $result = array(); + + foreach ($this->keys as $key) { + $result[$key] = $value; + } + + return $result; + } + + /** + * Extracts the duplicated value from an array. + * + * @param array $array + * + * @return mixed The value + * + * @throws TransformationFailedException If the given value is not an array or + * if the given array can not be transformed. + */ + public function reverseTransform($array) + { + if (!is_array($array)) { + throw new TransformationFailedException('Expected an array.'); + } + + $result = current($array); + $emptyKeys = array(); + + foreach ($this->keys as $key) { + if (!empty($array[$key])) { + if ($array[$key] !== $result) { + throw new TransformationFailedException( + 'All values in the array should be the same' + ); + } + } else { + $emptyKeys[] = $key; + } + } + + if (count($emptyKeys) > 0) { + if (count($emptyKeys) == count($this->keys)) { + // All keys empty + return null; + } + + throw new TransformationFailedException( + sprintf('The keys "%s" should not be empty', implode('", "', $emptyKeys) + )); + } + + return $result; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.php new file mode 100644 index 0000000..1f62e06 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.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\Form\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; + +/** + * Takes care of converting the input from a list of checkboxes to a correctly + * indexed array. + * + * @author Bernhard Schussek + */ +class FixCheckboxInputListener implements EventSubscriberInterface +{ + private $choiceList; + + /** + * Constructor. + * + * @param ChoiceListInterface $choiceList + */ + public function __construct(ChoiceListInterface $choiceList) + { + $this->choiceList = $choiceList; + } + + public function preSubmit(FormEvent $event) + { + $values = (array) $event->getData(); + $indices = $this->choiceList->getIndicesForValues($values); + + $event->setData(count($indices) > 0 ? array_combine($indices, $values) : array()); + } + + /** + * Alias of {@link preSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link preSubmit()} instead. + */ + public function preBind(FormEvent $event) + { + $this->preSubmit($event); + } + + public static function getSubscribedEvents() + { + return array(FormEvents::PRE_SUBMIT => 'preSubmit'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/FixRadioInputListener.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/FixRadioInputListener.php new file mode 100644 index 0000000..bf03792 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/FixRadioInputListener.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\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; + +/** + * Takes care of converting the input from a single radio button + * to an array. + * + * @author Bernhard Schussek + */ +class FixRadioInputListener implements EventSubscriberInterface +{ + private $choiceList; + + private $placeholderPresent; + + /** + * Constructor. + * + * @param ChoiceListInterface $choiceList + * @param Boolean $placeholderPresent + */ + public function __construct(ChoiceListInterface $choiceList, $placeholderPresent) + { + $this->choiceList = $choiceList; + $this->placeholderPresent = $placeholderPresent; + } + + public function preSubmit(FormEvent $event) + { + $value = $event->getData(); + $index = current($this->choiceList->getIndicesForValues(array($value))); + + $event->setData(false !== $index ? array($index => $value) : ($this->placeholderPresent ? array('placeholder' => '') : array())) ; + } + + /** + * Alias of {@link preSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link preSubmit()} instead. + */ + public function preBind(FormEvent $event) + { + $this->preSubmit($event); + } + + public static function getSubscribedEvents() + { + return array(FormEvents::PRE_SUBMIT => 'preSubmit'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php new file mode 100644 index 0000000..e25dacf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.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\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Adds a protocol to a URL if it doesn't already have one. + * + * @author Bernhard Schussek + */ +class FixUrlProtocolListener implements EventSubscriberInterface +{ + private $defaultProtocol; + + public function __construct($defaultProtocol = 'http') + { + $this->defaultProtocol = $defaultProtocol; + } + + public function onSubmit(FormEvent $event) + { + $data = $event->getData(); + + if ($this->defaultProtocol && $data && !preg_match('~^\w+://~', $data)) { + $event->setData($this->defaultProtocol.'://'.$data); + } + } + + /** + * Alias of {@link onSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link onSubmit()} instead. + */ + public function onBind(FormEvent $event) + { + $this->onSubmit($event); + } + + public static function getSubscribedEvents() + { + return array(FormEvents::SUBMIT => 'onSubmit'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php new file mode 100644 index 0000000..b6f8fb7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.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\Form\Extension\Core\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +/** + * @author Bernhard Schussek + */ +class MergeCollectionListener implements EventSubscriberInterface +{ + /** + * Whether elements may be added to the collection + * @var Boolean + */ + private $allowAdd; + + /** + * Whether elements may be removed from the collection + * @var Boolean + */ + private $allowDelete; + + /** + * Creates a new listener. + * + * @param Boolean $allowAdd Whether values might be added to the + * collection. + * @param Boolean $allowDelete Whether values might be removed from the + * collection. + */ + public function __construct($allowAdd = false, $allowDelete = false) + { + $this->allowAdd = $allowAdd; + $this->allowDelete = $allowDelete; + } + + public static function getSubscribedEvents() + { + return array( + FormEvents::SUBMIT => 'onSubmit', + ); + } + + public function onSubmit(FormEvent $event) + { + $dataToMergeInto = $event->getForm()->getNormData(); + $data = $event->getData(); + + if (null === $data) { + $data = array(); + } + + if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) { + throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)'); + } + + if (null !== $dataToMergeInto && !is_array($dataToMergeInto) && !($dataToMergeInto instanceof \Traversable && $dataToMergeInto instanceof \ArrayAccess)) { + throw new UnexpectedTypeException($dataToMergeInto, 'array or (\Traversable and \ArrayAccess)'); + } + + // If we are not allowed to change anything, return immediately + if ((!$this->allowAdd && !$this->allowDelete) || $data === $dataToMergeInto) { + $event->setData($dataToMergeInto); + + return; + } + + if (!$dataToMergeInto) { + // No original data was set. Set it if allowed + if ($this->allowAdd) { + $dataToMergeInto = $data; + } + } else { + // Calculate delta + $itemsToAdd = is_object($data) ? clone $data : $data; + $itemsToDelete = array(); + + foreach ($dataToMergeInto as $beforeKey => $beforeItem) { + foreach ($data as $afterKey => $afterItem) { + if ($afterItem === $beforeItem) { + // Item found, next original item + unset($itemsToAdd[$afterKey]); + continue 2; + } + } + + // Item not found, remember for deletion + $itemsToDelete[] = $beforeKey; + } + + // Remove deleted items before adding to free keys that are to be + // replaced + if ($this->allowDelete) { + foreach ($itemsToDelete as $key) { + unset($dataToMergeInto[$key]); + } + } + + // Add remaining items + if ($this->allowAdd) { + foreach ($itemsToAdd as $key => $item) { + if (!isset($dataToMergeInto[$key])) { + $dataToMergeInto[$key] = $item; + } else { + $dataToMergeInto[] = $item; + } + } + } + } + + $event->setData($dataToMergeInto); + } + + + /** + * Alias of {@link onSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link onSubmit()} instead. + */ + public function onBind(FormEvent $event) + { + $this->onSubmit($event); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php new file mode 100644 index 0000000..fabeb36 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Resize a collection form element based on the data sent from the client. + * + * @author Bernhard Schussek + */ +class ResizeFormListener implements EventSubscriberInterface +{ + /** + * @var string + */ + protected $type; + + /** + * @var array + */ + protected $options; + + /** + * Whether children could be added to the group + * @var Boolean + */ + protected $allowAdd; + + /** + * Whether children could be removed from the group + * @var Boolean + */ + protected $allowDelete; + + public function __construct($type, array $options = array(), $allowAdd = false, $allowDelete = false) + { + $this->type = $type; + $this->allowAdd = $allowAdd; + $this->allowDelete = $allowDelete; + $this->options = $options; + } + + public static function getSubscribedEvents() + { + return array( + FormEvents::PRE_SET_DATA => 'preSetData', + FormEvents::PRE_SUBMIT => 'preSubmit', + // (MergeCollectionListener, MergeDoctrineCollectionListener) + FormEvents::SUBMIT => array('onSubmit', 50), + ); + } + + public function preSetData(FormEvent $event) + { + $form = $event->getForm(); + $data = $event->getData(); + + if (null === $data) { + $data = array(); + } + + if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) { + throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)'); + } + + // First remove all rows + foreach ($form as $name => $child) { + $form->remove($name); + } + + // Then add all rows again in the correct order + foreach ($data as $name => $value) { + $form->add($name, $this->type, array_replace(array( + 'property_path' => '['.$name.']', + ), $this->options)); + } + } + + public function preSubmit(FormEvent $event) + { + $form = $event->getForm(); + $data = $event->getData(); + + if (null === $data || '' === $data) { + $data = array(); + } + + if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) { + throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)'); + } + + // Remove all empty rows + if ($this->allowDelete) { + foreach ($form as $name => $child) { + if (!isset($data[$name])) { + $form->remove($name); + } + } + } + + // Add all additional rows + if ($this->allowAdd) { + foreach ($data as $name => $value) { + if (!$form->has($name)) { + $form->add($name, $this->type, array_replace(array( + 'property_path' => '['.$name.']', + ), $this->options)); + } + } + } + } + + public function onSubmit(FormEvent $event) + { + $form = $event->getForm(); + $data = $event->getData(); + + if (null === $data) { + $data = array(); + } + + if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) { + throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)'); + } + + // The data mapper only adds, but does not remove items, so do this + // here + if ($this->allowDelete) { + foreach ($data as $name => $child) { + if (!$form->has($name)) { + unset($data[$name]); + } + } + } + + $event->setData($data); + } + + + /** + * Alias of {@link preSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link preSubmit()} instead. + */ + public function preBind(FormEvent $event) + { + $this->preSubmit($event); + } + + /** + * Alias of {@link onSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link onSubmit()} instead. + */ + public function onBind(FormEvent $event) + { + $this->onSubmit($event); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php new file mode 100644 index 0000000..cbe6e0a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.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\Form\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Trims string data + * + * @author Bernhard Schussek + */ +class TrimListener implements EventSubscriberInterface +{ + public function preSubmit(FormEvent $event) + { + $data = $event->getData(); + + if (!is_string($data)) { + return; + } + + if (null !== $result = @preg_replace('/^[\pZ\p{Cc}]+|[\pZ\p{Cc}]+$/u', '', $data)) { + $event->setData($result); + } else { + $event->setData(trim($data)); + } + } + + /** + * Alias of {@link preSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link preSubmit()} instead. + */ + public function preBind(FormEvent $event) + { + $this->preSubmit($event); + } + + public static function getSubscribedEvents() + { + return array(FormEvents::PRE_SUBMIT => 'preSubmit'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/BaseType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/BaseType.php new file mode 100644 index 0000000..79333a6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/BaseType.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\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * Encapsulates common logic of {@link FormType} and {@link ButtonType}. + * + * This type does not appear in the form's type inheritance chain and as such + * cannot be extended (via {@link FormTypeExtension}s) nor themed. + * + * @author Bernhard Schussek + */ +abstract class BaseType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->setDisabled($options['disabled']); + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $name = $form->getName(); + $blockName = $options['block_name'] ?: $form->getName(); + $translationDomain = $options['translation_domain']; + + if ($view->parent) { + if ('' !== ($parentFullName = $view->parent->vars['full_name'])) { + $id = sprintf('%s_%s', $view->parent->vars['id'], $name); + $fullName = sprintf('%s[%s]', $parentFullName, $name); + $uniqueBlockPrefix = sprintf('%s_%s', $view->parent->vars['unique_block_prefix'], $blockName); + } else { + $id = $name; + $fullName = $name; + $uniqueBlockPrefix = '_'.$blockName; + } + + if (!$translationDomain) { + $translationDomain = $view->parent->vars['translation_domain']; + } + } else { + $id = $name; + $fullName = $name; + $uniqueBlockPrefix = '_'.$blockName; + + // Strip leading underscores and digits. These are allowed in + // form names, but not in HTML4 ID attributes. + // http://www.w3.org/TR/html401/struct/global.html#adef-id + $id = ltrim($id, '_0123456789'); + } + + $blockPrefixes = array(); + for ($type = $form->getConfig()->getType(); null !== $type; $type = $type->getParent()) { + array_unshift($blockPrefixes, $type->getName()); + } + $blockPrefixes[] = $uniqueBlockPrefix; + + if (!$translationDomain) { + $translationDomain = 'messages'; + } + + $view->vars = array_replace($view->vars, array( + 'form' => $view, + 'id' => $id, + 'name' => $name, + 'full_name' => $fullName, + 'disabled' => $form->isDisabled(), + 'label' => $options['label'], + 'multipart' => false, + 'attr' => $options['attr'], + 'block_prefixes' => $blockPrefixes, + 'unique_block_prefix' => $uniqueBlockPrefix, + 'translation_domain' => $translationDomain, + // Using the block name here speeds up performance in collection + // forms, where each entry has the same full block name. + // Including the type is important too, because if rows of a + // collection form have different types (dynamically), they should + // be rendered differently. + // https://github.com/symfony/symfony/issues/5038 + 'cache_key' => $uniqueBlockPrefix.'_'.$form->getConfig()->getType()->getName(), + )); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'block_name' => null, + 'disabled' => false, + 'label' => null, + 'attr' => array(), + 'translation_domain' => null, + )); + + $resolver->setAllowedTypes(array( + 'attr' => 'array', + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/BirthdayType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/BirthdayType.php new file mode 100644 index 0000000..5314c14 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/BirthdayType.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\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class BirthdayType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'years' => range(date('Y') - 120, date('Y')), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'date'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'birthday'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/ButtonType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/ButtonType.php new file mode 100644 index 0000000..3569963 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/ButtonType.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\Extension\Core\Type; + +use Symfony\Component\Form\ButtonTypeInterface; + +/** + * A form button. + * + * @author Bernhard Schussek + */ +class ButtonType extends BaseType implements ButtonTypeInterface +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'button'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php new file mode 100644 index 0000000..214e581 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\BooleanToStringTransformer; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class CheckboxType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->addViewTransformer(new BooleanToStringTransformer($options['value'])) + ; + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars = array_replace($view->vars, array( + 'value' => $options['value'], + 'checked' => null !== $form->getViewData(), + )); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $emptyData = function (FormInterface $form, $clientData) { + return $clientData; + }; + + $resolver->setDefaults(array( + 'value' => '1', + 'empty_data' => $emptyData, + 'compound' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'checkbox'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php new file mode 100644 index 0000000..9a3fdef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; +use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener; +use Symfony\Component\Form\Extension\Core\EventListener\FixCheckboxInputListener; +use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToBooleanArrayTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToBooleanArrayTransformer; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class ChoiceType extends AbstractType +{ + /** + * Caches created choice lists. + * @var array + */ + private $choiceListCache = array(); + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + if (!$options['choice_list'] && !is_array($options['choices']) && !$options['choices'] instanceof \Traversable) { + throw new LogicException('Either the option "choices" or "choice_list" must be set.'); + } + + if ($options['expanded']) { + // Initialize all choices before doing the index check below. + // This helps in cases where index checks are optimized for non + // initialized choice lists. For example, when using an SQL driver, + // the index check would read in one SQL query and the initialization + // requires another SQL query. When the initialization is done first, + // one SQL query is sufficient. + $preferredViews = $options['choice_list']->getPreferredViews(); + $remainingViews = $options['choice_list']->getRemainingViews(); + + // Check if the choices already contain the empty value + // Only add the empty value option if this is not the case + if (null !== $options['empty_value'] && 0 === count($options['choice_list']->getIndicesForValues(array('')))) { + $placeholderView = new ChoiceView(null, '', $options['empty_value']); + + // "placeholder" is a reserved index + // see also ChoiceListInterface::getIndicesForChoices() + $this->addSubForms($builder, array('placeholder' => $placeholderView), $options); + } + + $this->addSubForms($builder, $preferredViews, $options); + $this->addSubForms($builder, $remainingViews, $options); + + if ($options['multiple']) { + $builder->addViewTransformer(new ChoicesToBooleanArrayTransformer($options['choice_list'])); + $builder->addEventSubscriber(new FixCheckboxInputListener($options['choice_list']), 10); + } else { + $builder->addViewTransformer(new ChoiceToBooleanArrayTransformer($options['choice_list'], $builder->has('placeholder'))); + $builder->addEventSubscriber(new FixRadioInputListener($options['choice_list'], $builder->has('placeholder')), 10); + } + } else { + if ($options['multiple']) { + $builder->addViewTransformer(new ChoicesToValuesTransformer($options['choice_list'])); + } else { + $builder->addViewTransformer(new ChoiceToValueTransformer($options['choice_list'])); + } + } + + if ($options['multiple'] && $options['by_reference']) { + // Make sure the collection created during the client->norm + // transformation is merged back into the original collection + $builder->addEventSubscriber(new MergeCollectionListener(true, true)); + } + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars = array_replace($view->vars, array( + 'multiple' => $options['multiple'], + 'expanded' => $options['expanded'], + 'preferred_choices' => $options['choice_list']->getPreferredViews(), + 'choices' => $options['choice_list']->getRemainingViews(), + 'separator' => '-------------------', + 'empty_value' => null, + )); + + // The decision, whether a choice is selected, is potentially done + // thousand of times during the rendering of a template. Provide a + // closure here that is optimized for the value of the form, to + // avoid making the type check inside the closure. + if ($options['multiple']) { + $view->vars['is_selected'] = function ($choice, array $values) { + return false !== array_search($choice, $values, true); + }; + } else { + $view->vars['is_selected'] = function ($choice, $value) { + return $choice === $value; + }; + } + + // Check if the choices already contain the empty value + // Only add the empty value option if this is not the case + if (null !== $options['empty_value'] && 0 === count($options['choice_list']->getIndicesForValues(array('')))) { + $view->vars['empty_value'] = $options['empty_value']; + } + + if ($options['multiple'] && !$options['expanded']) { + // Add "[]" to the name in case a select tag with multiple options is + // displayed. Otherwise only one of the selected options is sent in the + // POST request. + $view->vars['full_name'] = $view->vars['full_name'].'[]'; + } + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + if ($options['expanded']) { + // Radio buttons should have the same name as the parent + $childName = $view->vars['full_name']; + + // Checkboxes should append "[]" to allow multiple selection + if ($options['multiple']) { + $childName .= '[]'; + } + + foreach ($view as $childView) { + $childView->vars['full_name'] = $childName; + } + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $choiceListCache =& $this->choiceListCache; + + $choiceList = function (Options $options) use (&$choiceListCache) { + // Harden against NULL values (like in EntityType and ModelType) + $choices = null !== $options['choices'] ? $options['choices'] : array(); + + // Reuse existing choice lists in order to increase performance + $hash = md5(json_encode(array($choices, $options['preferred_choices']))); + + if (!isset($choiceListCache[$hash])) { + $choiceListCache[$hash] = new SimpleChoiceList($choices, $options['preferred_choices']); + } + + return $choiceListCache[$hash]; + }; + + $emptyData = function (Options $options) { + if ($options['multiple'] || $options['expanded']) { + return array(); + } + + return ''; + }; + + $emptyValue = function (Options $options) { + return $options['required'] ? null : ''; + }; + + $emptyValueNormalizer = function (Options $options, $emptyValue) { + if ($options['multiple']) { + // never use an empty value for this case + return null; + } elseif (false === $emptyValue) { + // an empty value should be added but the user decided otherwise + return null; + } elseif ($options['expanded'] && '' === $emptyValue) { + // never use an empty label for radio buttons + return 'None'; + } + + // empty value has been set explicitly + return $emptyValue; + }; + + $compound = function (Options $options) { + return $options['expanded']; + }; + + $resolver->setDefaults(array( + 'multiple' => false, + 'expanded' => false, + 'choice_list' => $choiceList, + 'choices' => array(), + 'preferred_choices' => array(), + 'empty_data' => $emptyData, + 'empty_value' => $emptyValue, + 'error_bubbling' => false, + 'compound' => $compound, + // The view data is always a string, even if the "data" option + // is manually set to an object. + // See https://github.com/symfony/symfony/pull/5582 + 'data_class' => null, + )); + + $resolver->setNormalizers(array( + 'empty_value' => $emptyValueNormalizer, + )); + + $resolver->setAllowedTypes(array( + 'choice_list' => array('null', 'Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface'), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'choice'; + } + + /** + * Adds the sub fields for an expanded choice field. + * + * @param FormBuilderInterface $builder The form builder. + * @param array $choiceViews The choice view objects. + * @param array $options The build options. + */ + private function addSubForms(FormBuilderInterface $builder, array $choiceViews, array $options) + { + foreach ($choiceViews as $i => $choiceView) { + if (is_array($choiceView)) { + // Flatten groups + $this->addSubForms($builder, $choiceView, $options); + } else { + $choiceOpts = array( + 'value' => $choiceView->value, + 'label' => $choiceView->label, + 'translation_domain' => $options['translation_domain'], + ); + + if ($options['multiple']) { + $choiceType = 'checkbox'; + // The user can check 0 or more checkboxes. If required + // is true, he is required to check all of them. + $choiceOpts['required'] = false; + } else { + $choiceType = 'radio'; + } + + $builder->add($i, $choiceType, $choiceOpts); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php new file mode 100644 index 0000000..0cb3af1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class CollectionType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + if ($options['allow_add'] && $options['prototype']) { + $prototype = $builder->create($options['prototype_name'], $options['type'], array_replace(array( + 'label' => $options['prototype_name'].'label__', + ), $options['options'])); + $builder->setAttribute('prototype', $prototype->getForm()); + } + + $resizeListener = new ResizeFormListener( + $options['type'], + $options['options'], + $options['allow_add'], + $options['allow_delete'] + ); + + $builder->addEventSubscriber($resizeListener); + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars = array_replace($view->vars, array( + 'allow_add' => $options['allow_add'], + 'allow_delete' => $options['allow_delete'], + )); + + if ($form->getConfig()->hasAttribute('prototype')) { + $view->vars['prototype'] = $form->getConfig()->getAttribute('prototype')->createView($view); + } + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + if ($form->getConfig()->hasAttribute('prototype') && $view->vars['prototype']->vars['multipart']) { + $view->vars['multipart'] = true; + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $optionsNormalizer = function (Options $options, $value) { + $value['block_name'] = 'entry'; + + return $value; + }; + + $resolver->setDefaults(array( + 'allow_add' => false, + 'allow_delete' => false, + 'prototype' => true, + 'prototype_name' => '__name__', + 'type' => 'text', + 'options' => array(), + )); + + $resolver->setNormalizers(array( + 'options' => $optionsNormalizer, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'collection'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php new file mode 100644 index 0000000..c9c979a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CountryType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Intl\Intl; +use Symfony\Component\Locale\Locale; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class CountryType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'choices' => Intl::getRegionBundle()->getCountryNames(), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'choice'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'country'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php new file mode 100644 index 0000000..07b820d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Intl\Intl; +use Symfony\Component\Locale\Locale; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class CurrencyType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'choices' => Intl::getCurrencyBundle()->getCurrencyNames(), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'choice'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'currency'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php new file mode 100644 index 0000000..a612b6f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php @@ -0,0 +1,281 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\ReversedTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToRfc3339Transformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransformer; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class DateTimeType extends AbstractType +{ + const DEFAULT_DATE_FORMAT = \IntlDateFormatter::MEDIUM; + + const DEFAULT_TIME_FORMAT = \IntlDateFormatter::MEDIUM; + + /** + * This is not quite the HTML5 format yet, because ICU lacks the + * capability of parsing and generating RFC 3339 dates, which + * are like the below pattern but with a timezone suffix. The + * timezone suffix is + * + * * "Z" for UTC + * * "(-|+)HH:mm" for other timezones (note the colon!) + * + * For more information see: + * + * http://userguide.icu-project.org/formatparse/datetime#TOC-Date-Time-Format-Syntax + * http://www.w3.org/TR/html-markup/input.datetime.html + * http://tools.ietf.org/html/rfc3339 + * + * An ICU ticket was created: + * http://icu-project.org/trac/ticket/9421 + * + * It was supposedly fixed, but is not available in all PHP installations + * yet. To temporarily circumvent this issue, DateTimeToRfc3339Transformer + * is used when the format matches this constant. + */ + const HTML5_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"; + + private static $acceptedFormats = array( + \IntlDateFormatter::FULL, + \IntlDateFormatter::LONG, + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::SHORT, + ); + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $parts = array('year', 'month', 'day', 'hour'); + $dateParts = array('year', 'month', 'day'); + $timeParts = array('hour'); + + if ($options['with_minutes']) { + $parts[] = 'minute'; + $timeParts[] = 'minute'; + } + + if ($options['with_seconds']) { + $parts[] = 'second'; + $timeParts[] = 'second'; + } + + $dateFormat = is_int($options['date_format']) ? $options['date_format'] : self::DEFAULT_DATE_FORMAT; + $timeFormat = self::DEFAULT_TIME_FORMAT; + $calendar = \IntlDateFormatter::GREGORIAN; + $pattern = is_string($options['format']) ? $options['format'] : null; + + if (!in_array($dateFormat, self::$acceptedFormats, true)) { + throw new InvalidOptionsException('The "date_format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format.'); + } + + if ('single_text' === $options['widget']) { + if (self::HTML5_FORMAT === $pattern) { + $builder->addViewTransformer(new DateTimeToRfc3339Transformer( + $options['model_timezone'], + $options['view_timezone'] + )); + } else { + $builder->addViewTransformer(new DateTimeToLocalizedStringTransformer( + $options['model_timezone'], + $options['view_timezone'], + $dateFormat, + $timeFormat, + $calendar, + $pattern + )); + } + } else { + // Only pass a subset of the options to children + $dateOptions = array_intersect_key($options, array_flip(array( + 'years', + 'months', + 'days', + 'empty_value', + 'required', + 'translation_domain', + ))); + + $timeOptions = array_intersect_key($options, array_flip(array( + 'hours', + 'minutes', + 'seconds', + 'with_minutes', + 'with_seconds', + 'empty_value', + 'required', + 'translation_domain', + ))); + + if (null !== $options['date_widget']) { + $dateOptions['widget'] = $options['date_widget']; + } + + if (null !== $options['time_widget']) { + $timeOptions['widget'] = $options['time_widget']; + } + + if (null !== $options['date_format']) { + $dateOptions['format'] = $options['date_format']; + } + + $dateOptions['input'] = $timeOptions['input'] = 'array'; + $dateOptions['error_bubbling'] = $timeOptions['error_bubbling'] = true; + + $builder + ->addViewTransformer(new DataTransformerChain(array( + new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts), + new ArrayToPartsTransformer(array( + 'date' => $dateParts, + 'time' => $timeParts, + )), + ))) + ->add('date', 'date', $dateOptions) + ->add('time', 'time', $timeOptions) + ; + } + + if ('string' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone']) + )); + } elseif ('timestamp' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone']) + )); + } elseif ('array' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], $parts) + )); + } + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['widget'] = $options['widget']; + + // Change the input to a HTML5 date input if + // * the widget is set to "single_text" + // * the format matches the one expected by HTML5 + if ('single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) { + $view->vars['type'] = 'datetime'; + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $compound = function (Options $options) { + return $options['widget'] !== 'single_text'; + }; + + // Defaults to the value of "widget" + $dateWidget = function (Options $options) { + return $options['widget']; + }; + + // Defaults to the value of "widget" + $timeWidget = function (Options $options) { + return $options['widget']; + }; + + $resolver->setDefaults(array( + 'input' => 'datetime', + 'model_timezone' => null, + 'view_timezone' => null, + 'format' => self::HTML5_FORMAT, + 'date_format' => null, + 'widget' => null, + 'date_widget' => $dateWidget, + 'time_widget' => $timeWidget, + 'with_minutes' => true, + 'with_seconds' => false, + // Don't modify \DateTime classes by reference, we treat + // them like immutable value objects + 'by_reference' => false, + 'error_bubbling' => false, + // If initialized with a \DateTime object, FormType initializes + // this option to "\DateTime". Since the internal, normalized + // representation is not \DateTime, but an array, we need to unset + // this option. + 'data_class' => null, + 'compound' => $compound, + )); + + // Don't add some defaults in order to preserve the defaults + // set in DateType and TimeType + $resolver->setOptional(array( + 'empty_value', + 'years', + 'months', + 'days', + 'hours', + 'minutes', + 'seconds', + )); + + $resolver->setAllowedValues(array( + 'input' => array( + 'datetime', + 'string', + 'timestamp', + 'array', + ), + 'date_widget' => array( + null, // inherit default from DateType + 'single_text', + 'text', + 'choice', + ), + 'time_widget' => array( + null, // inherit default from TimeType + 'single_text', + 'text', + 'choice', + ), + // This option will overwrite "date_widget" and "time_widget" options + 'widget' => array( + null, // default, don't overwrite options + 'single_text', + 'text', + 'choice', + ), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'datetime'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/DateType.php new file mode 100644 index 0000000..a4a55a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/DateType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; +use Symfony\Component\Form\ReversedTransformer; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; + +class DateType extends AbstractType +{ + const DEFAULT_FORMAT = \IntlDateFormatter::MEDIUM; + + const HTML5_FORMAT = 'yyyy-MM-dd'; + + private static $acceptedFormats = array( + \IntlDateFormatter::FULL, + \IntlDateFormatter::LONG, + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::SHORT, + ); + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $dateFormat = is_int($options['format']) ? $options['format'] : self::DEFAULT_FORMAT; + $timeFormat = \IntlDateFormatter::NONE; + $calendar = \IntlDateFormatter::GREGORIAN; + $pattern = is_string($options['format']) ? $options['format'] : null; + + if (!in_array($dateFormat, self::$acceptedFormats, true)) { + throw new InvalidOptionsException('The "format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format.'); + } + + if (null !== $pattern && (false === strpos($pattern, 'y') || false === strpos($pattern, 'M') || false === strpos($pattern, 'd'))) { + throw new InvalidOptionsException(sprintf('The "format" option should contain the letters "y", "M" and "d". Its current value is "%s".', $pattern)); + } + + if ('single_text' === $options['widget']) { + $builder->addViewTransformer(new DateTimeToLocalizedStringTransformer( + $options['model_timezone'], + $options['view_timezone'], + $dateFormat, + $timeFormat, + $calendar, + $pattern + )); + } else { + $yearOptions = $monthOptions = $dayOptions = array( + 'error_bubbling' => true, + ); + + $formatter = new \IntlDateFormatter( + \Locale::getDefault(), + $dateFormat, + $timeFormat, + 'UTC', + $calendar, + $pattern + ); + $formatter->setLenient(false); + + if ('choice' === $options['widget']) { + // Only pass a subset of the options to children + $yearOptions['choices'] = $this->formatTimestamps($formatter, '/y+/', $this->listYears($options['years'])); + $yearOptions['empty_value'] = $options['empty_value']['year']; + $monthOptions['choices'] = $this->formatTimestamps($formatter, '/[M|L]+/', $this->listMonths($options['months'])); + $monthOptions['empty_value'] = $options['empty_value']['month']; + $dayOptions['choices'] = $this->formatTimestamps($formatter, '/d+/', $this->listDays($options['days'])); + $dayOptions['empty_value'] = $options['empty_value']['day']; + } + + // Append generic carry-along options + foreach (array('required', 'translation_domain') as $passOpt) { + $yearOptions[$passOpt] = $monthOptions[$passOpt] = $dayOptions[$passOpt] = $options[$passOpt]; + } + + $builder + ->add('year', $options['widget'], $yearOptions) + ->add('month', $options['widget'], $monthOptions) + ->add('day', $options['widget'], $dayOptions) + ->addViewTransformer(new DateTimeToArrayTransformer( + $options['model_timezone'], $options['view_timezone'], array('year', 'month', 'day') + )) + ->setAttribute('formatter', $formatter) + ; + } + + if ('string' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'Y-m-d') + )); + } elseif ('timestamp' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone']) + )); + } elseif ('array' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], array('year', 'month', 'day')) + )); + } + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + $view->vars['widget'] = $options['widget']; + + // Change the input to a HTML5 date input if + // * the widget is set to "single_text" + // * the format matches the one expected by HTML5 + if ('single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) { + $view->vars['type'] = 'date'; + } + + if ($form->getConfig()->hasAttribute('formatter')) { + $pattern = $form->getConfig()->getAttribute('formatter')->getPattern(); + + // remove special characters unless the format was explicitly specified + if (!is_string($options['format'])) { + $pattern = preg_replace('/[^yMd]+/', '', $pattern); + } + + // set right order with respect to locale (e.g.: de_DE=dd.MM.yy; en_US=M/d/yy) + // lookup various formats at http://userguide.icu-project.org/formatparse/datetime + if (preg_match('/^([yMd]+).+([yMd]+).+([yMd]+)$/', $pattern)) { + $pattern = preg_replace(array('/y+/', '/M+/', '/d+/'), array('{{ year }}', '{{ month }}', '{{ day }}'), $pattern); + } else { + // default fallback + $pattern = '{{ year }}{{ month }}{{ day }}'; + } + + $view->vars['date_pattern'] = $pattern; + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $compound = function (Options $options) { + return $options['widget'] !== 'single_text'; + }; + + $emptyValue = $emptyValueDefault = function (Options $options) { + return $options['required'] ? null : ''; + }; + + $emptyValueNormalizer = function (Options $options, $emptyValue) use ($emptyValueDefault) { + if (is_array($emptyValue)) { + $default = $emptyValueDefault($options); + + return array_merge( + array('year' => $default, 'month' => $default, 'day' => $default), + $emptyValue + ); + } + + return array( + 'year' => $emptyValue, + 'month' => $emptyValue, + 'day' => $emptyValue + ); + }; + + $format = function (Options $options) { + return $options['widget'] === 'single_text' ? DateType::HTML5_FORMAT : DateType::DEFAULT_FORMAT; + }; + + $resolver->setDefaults(array( + 'years' => range(date('Y') - 5, date('Y') + 5), + 'months' => range(1, 12), + 'days' => range(1, 31), + 'widget' => 'choice', + 'input' => 'datetime', + 'format' => $format, + 'model_timezone' => null, + 'view_timezone' => null, + 'empty_value' => $emptyValue, + // Don't modify \DateTime classes by reference, we treat + // them like immutable value objects + 'by_reference' => false, + 'error_bubbling' => false, + // If initialized with a \DateTime object, FormType initializes + // this option to "\DateTime". Since the internal, normalized + // representation is not \DateTime, but an array, we need to unset + // this option. + 'data_class' => null, + 'compound' => $compound, + )); + + $resolver->setNormalizers(array( + 'empty_value' => $emptyValueNormalizer, + )); + + $resolver->setAllowedValues(array( + 'input' => array( + 'datetime', + 'string', + 'timestamp', + 'array', + ), + 'widget' => array( + 'single_text', + 'text', + 'choice', + ), + )); + + $resolver->setAllowedTypes(array( + 'format' => array('int', 'string'), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'date'; + } + + private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $timestamps) + { + $pattern = $formatter->getPattern(); + $timezone = $formatter->getTimezoneId(); + + if (version_compare(\PHP_VERSION, '5.5.0-dev', '>=')) { + $formatter->setTimeZone(\DateTimeZone::UTC); + } else { + $formatter->setTimeZoneId(\DateTimeZone::UTC); + } + + if (preg_match($regex, $pattern, $matches)) { + $formatter->setPattern($matches[0]); + + foreach ($timestamps as $key => $timestamp) { + $timestamps[$key] = $formatter->format($timestamp); + } + + // I'd like to clone the formatter above, but then we get a + // segmentation fault, so let's restore the old state instead + $formatter->setPattern($pattern); + } + + if (version_compare(\PHP_VERSION, '5.5.0-dev', '>=')) { + $formatter->setTimeZone($timezone); + } else { + $formatter->setTimeZoneId($timezone); + } + + return $timestamps; + } + + private function listYears(array $years) + { + $result = array(); + + foreach ($years as $year) { + $result[$year] = gmmktime(0, 0, 0, 6, 15, $year); + } + + return $result; + } + + private function listMonths(array $months) + { + $result = array(); + + foreach ($months as $month) { + $result[$month] = gmmktime(0, 0, 0, $month, 15); + } + + return $result; + } + + private function listDays(array $days) + { + $result = array(); + + foreach ($days as $day) { + $result[$day] = gmmktime(0, 0, 0, 5, $day); + } + + return $result; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/EmailType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/EmailType.php new file mode 100644 index 0000000..26652ef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/EmailType.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\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; + +class EmailType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'email'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/FileType.php new file mode 100644 index 0000000..2c09da6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/FileType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class FileType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars = array_replace($view->vars, array( + 'type' => 'file', + 'value' => '', + )); + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + $view + ->vars['multipart'] = true + ; + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'compound' => false, + 'data_class' => 'Symfony\Component\HttpFoundation\File\File', + 'empty_data' => null, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'file'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/FormType.php new file mode 100644 index 0000000..eb1897c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/FormType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Extension\Core\EventListener\TrimListener; +use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +class FormType extends BaseType +{ + /** + * @var PropertyAccessorInterface + */ + private $propertyAccessor; + + public function __construct(PropertyAccessorInterface $propertyAccessor = null) + { + $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor(); + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + parent::buildForm($builder, $options); + + $builder + ->setRequired($options['required']) + ->setErrorBubbling($options['error_bubbling']) + ->setEmptyData($options['empty_data']) + ->setPropertyPath($options['property_path']) + ->setMapped($options['mapped']) + ->setByReference($options['by_reference']) + ->setInheritData($options['inherit_data']) + ->setCompound($options['compound']) + ->setData(isset($options['data']) ? $options['data'] : null) + ->setDataLocked(isset($options['data'])) + ->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null) + ->setMethod($options['method']) + ->setAction($options['action']) + ->setAutoInitialize($options['auto_initialize']) + ; + + if ($options['trim']) { + $builder->addEventSubscriber(new TrimListener()); + } + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + parent::buildView($view, $form, $options); + + $name = $form->getName(); + $readOnly = $options['read_only']; + + if ($view->parent) { + if ('' === $name) { + throw new LogicException('Form node with empty name can be used only as root form node.'); + } + + // Complex fields are read-only if they themselves or their parents are. + if (!$readOnly) { + $readOnly = $view->parent->vars['read_only']; + } + } + + $view->vars = array_replace($view->vars, array( + 'read_only' => $readOnly, + 'errors' => $form->getErrors(), + 'valid' => $form->isSubmitted() ? $form->isValid() : true, + 'value' => $form->getViewData(), + 'data' => $form->getNormData(), + 'required' => $form->isRequired(), + 'max_length' => $options['max_length'], + 'pattern' => $options['pattern'], + 'size' => null, + 'label_attr' => $options['label_attr'], + 'compound' => $form->getConfig()->getCompound(), + 'method' => $form->getConfig()->getMethod(), + 'action' => $form->getConfig()->getAction(), + )); + } + + /** + * {@inheritdoc} + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + $multipart = false; + + foreach ($view->children as $child) { + if ($child->vars['multipart']) { + $multipart = true; + break; + } + } + + $view->vars['multipart'] = $multipart; + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + parent::setDefaultOptions($resolver); + + // Derive "data_class" option from passed "data" object + $dataClass = function (Options $options) { + return isset($options['data']) && is_object($options['data']) ? get_class($options['data']) : null; + }; + + // Derive "empty_data" closure from "data_class" option + $emptyData = function (Options $options) { + $class = $options['data_class']; + + if (null !== $class) { + return function (FormInterface $form) use ($class) { + return $form->isEmpty() && !$form->isRequired() ? null : new $class(); + }; + } + + return function (FormInterface $form) { + return $form->getConfig()->getCompound() ? array() : ''; + }; + }; + + // For any form that is not represented by a single HTML control, + // errors should bubble up by default + $errorBubbling = function (Options $options) { + return $options['compound']; + }; + + // BC with old "virtual" option + $inheritData = function (Options $options) { + if (null !== $options['virtual']) { + // Uncomment this as soon as the deprecation note should be shown + // trigger_error('The form option "virtual" is deprecated since version 2.3 and will be removed in 3.0. Use "inherit_data" instead.', E_USER_DEPRECATED); + + return $options['virtual']; + } + + return false; + }; + + // If data is given, the form is locked to that data + // (independent of its value) + $resolver->setOptional(array( + 'data', + )); + + $resolver->setDefaults(array( + 'data_class' => $dataClass, + 'empty_data' => $emptyData, + 'trim' => true, + 'required' => true, + 'read_only' => false, + 'max_length' => null, + 'pattern' => null, + 'property_path' => null, + 'mapped' => true, + 'by_reference' => true, + 'error_bubbling' => $errorBubbling, + 'label_attr' => array(), + 'virtual' => null, + 'inherit_data' => $inheritData, + 'compound' => true, + 'method' => 'POST', + // According to RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt) + // section 4.2., empty URIs are considered same-document references + 'action' => '', + 'auto_initialize' => true, + )); + + $resolver->setAllowedTypes(array( + 'label_attr' => 'array', + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'form'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/HiddenType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/HiddenType.php new file mode 100644 index 0000000..bd4fa89 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/HiddenType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class HiddenType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + // hidden fields cannot have a required attribute + 'required' => false, + // Pass errors to the parent + 'error_bubbling' => true, + 'compound' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'hidden'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php new file mode 100644 index 0000000..b224cac --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\IntegerToLocalizedStringTransformer; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class IntegerType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addViewTransformer( + new IntegerToLocalizedStringTransformer( + $options['precision'], + $options['grouping'], + $options['rounding_mode'] + )); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + // default precision is locale specific (usually around 3) + 'precision' => null, + 'grouping' => false, + // Integer cast rounds towards 0, so do the same when displaying fractions + 'rounding_mode' => \NumberFormatter::ROUND_DOWN, + 'compound' => false, + )); + + $resolver->setAllowedValues(array( + 'rounding_mode' => array( + \NumberFormatter::ROUND_FLOOR, + \NumberFormatter::ROUND_DOWN, + \NumberFormatter::ROUND_HALFDOWN, + \NumberFormatter::ROUND_HALFEVEN, + \NumberFormatter::ROUND_HALFUP, + \NumberFormatter::ROUND_UP, + \NumberFormatter::ROUND_CEILING, + ), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'integer'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php new file mode 100644 index 0000000..04994a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Intl\Intl; +use Symfony\Component\Locale\Locale; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class LanguageType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'choices' => Intl::getLanguageBundle()->getLanguageNames(), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'choice'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'language'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php new file mode 100644 index 0000000..c68c561 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Intl\Intl; +use Symfony\Component\Locale\Locale; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class LocaleType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'choices' => Intl::getLocaleBundle()->getLocaleNames(), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'choice'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'locale'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/MoneyType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/MoneyType.php new file mode 100644 index 0000000..9e36f9c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/MoneyType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\MoneyToLocalizedStringTransformer; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class MoneyType extends AbstractType +{ + protected static $patterns = array(); + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->addViewTransformer(new MoneyToLocalizedStringTransformer( + $options['precision'], + $options['grouping'], + null, + $options['divisor'] + )) + ; + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['money_pattern'] = self::getPattern($options['currency']); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'precision' => 2, + 'grouping' => false, + 'divisor' => 1, + 'currency' => 'EUR', + 'compound' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'money'; + } + + /** + * Returns the pattern for this locale + * + * The pattern contains the placeholder "{{ widget }}" where the HTML tag should + * be inserted + */ + protected static function getPattern($currency) + { + if (!$currency) { + return '{{ widget }}'; + } + + $locale = \Locale::getDefault(); + + if (!isset(self::$patterns[$locale])) { + self::$patterns[$locale] = array(); + } + + if (!isset(self::$patterns[$locale][$currency])) { + $format = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); + $pattern = $format->formatCurrency('123', $currency); + + // the spacings between currency symbol and number are ignored, because + // a single space leads to better readability in combination with input + // fields + + // the regex also considers non-break spaces (0xC2 or 0xA0 in UTF-8) + + preg_match('/^([^\s\xc2\xa0]*)[\s\xc2\xa0]*123(?:[,.]0+)?[\s\xc2\xa0]*([^\s\xc2\xa0]*)$/u', $pattern, $matches); + + if (!empty($matches[1])) { + self::$patterns[$locale][$currency] = $matches[1].' {{ widget }}'; + } elseif (!empty($matches[2])) { + self::$patterns[$locale][$currency] = '{{ widget }} '.$matches[2]; + } else { + self::$patterns[$locale][$currency] = '{{ widget }}'; + } + } + + return self::$patterns[$locale][$currency]; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/NumberType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/NumberType.php new file mode 100644 index 0000000..beb3c89 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/NumberType.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\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\NumberToLocalizedStringTransformer; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class NumberType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addViewTransformer(new NumberToLocalizedStringTransformer( + $options['precision'], + $options['grouping'], + $options['rounding_mode'] + )); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + // default precision is locale specific (usually around 3) + 'precision' => null, + 'grouping' => false, + 'rounding_mode' => \NumberFormatter::ROUND_HALFUP, + 'compound' => false, + )); + + $resolver->setAllowedValues(array( + 'rounding_mode' => array( + \NumberFormatter::ROUND_FLOOR, + \NumberFormatter::ROUND_DOWN, + \NumberFormatter::ROUND_HALFDOWN, + \NumberFormatter::ROUND_HALFEVEN, + \NumberFormatter::ROUND_HALFUP, + \NumberFormatter::ROUND_UP, + \NumberFormatter::ROUND_CEILING, + ), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'number'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/PasswordType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/PasswordType.php new file mode 100644 index 0000000..5a5b163 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/PasswordType.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\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class PasswordType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + if ($options['always_empty'] || !$form->isSubmitted()) { + $view->vars['value'] = ''; + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'always_empty' => true, + 'trim' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'password'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/PercentType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/PercentType.php new file mode 100644 index 0000000..b1df943 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/PercentType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\PercentToLocalizedStringTransformer; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class PercentType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addViewTransformer(new PercentToLocalizedStringTransformer($options['precision'], $options['type'])); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'precision' => 0, + 'type' => 'fractional', + 'compound' => false, + )); + + $resolver->setAllowedValues(array( + 'type' => array( + 'fractional', + 'integer', + ), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'percent'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/RadioType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/RadioType.php new file mode 100644 index 0000000..dfa7c7d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/RadioType.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\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; + +class RadioType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'checkbox'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'radio'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php new file mode 100644 index 0000000..9a3cd14 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/RepeatedType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\DataTransformer\ValueToDuplicatesTransformer; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class RepeatedType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + // Overwrite required option for child fields + $options['first_options']['required'] = $options['required']; + $options['second_options']['required'] = $options['required']; + + if (!isset($options['options']['error_bubbling'])) { + $options['options']['error_bubbling'] = $options['error_bubbling']; + } + + $builder + ->addViewTransformer(new ValueToDuplicatesTransformer(array( + $options['first_name'], + $options['second_name'], + ))) + ->add($options['first_name'], $options['type'], array_merge($options['options'], $options['first_options'])) + ->add($options['second_name'], $options['type'], array_merge($options['options'], $options['second_options'])) + ; + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'type' => 'text', + 'options' => array(), + 'first_options' => array(), + 'second_options' => array(), + 'first_name' => 'first', + 'second_name' => 'second', + 'error_bubbling' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'repeated'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/ResetType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/ResetType.php new file mode 100644 index 0000000..cf55f7c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/ResetType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\ButtonTypeInterface; + +/** + * A reset button. + * + * @author Bernhard Schussek + */ +class ResetType extends AbstractType implements ButtonTypeInterface +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'button'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'reset'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/SearchType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/SearchType.php new file mode 100644 index 0000000..bf82972 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/SearchType.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\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; + +class SearchType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'search'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/SubmitType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/SubmitType.php new file mode 100644 index 0000000..6d160b9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/SubmitType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\SubmitButtonTypeInterface; + +/** + * A submit button. + * + * @author Bernhard Schussek + */ +class SubmitType extends AbstractType implements SubmitButtonTypeInterface +{ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['clicked'] = $form->isClicked(); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'button'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'submit'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TextType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TextType.php new file mode 100644 index 0000000..1150326 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TextType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class TextType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'compound' => false, + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'text'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TextareaType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TextareaType.php new file mode 100644 index 0000000..0e749b1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TextareaType.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\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\FormInterface; + +class TextareaType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['pattern'] = null; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'textarea'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php new file mode 100644 index 0000000..d7a2a9e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TimeType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\ReversedTransformer; +use Symfony\Component\Form\Exception\InvalidConfigurationException; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class TimeType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $parts = array('hour'); + $format = 'H'; + + if ($options['with_seconds'] && !$options['with_minutes']) { + throw new InvalidConfigurationException('You can not disable minutes if you have enabled seconds.'); + } + + if ($options['with_minutes']) { + $format .= ':i'; + $parts[] = 'minute'; + } + + if ($options['with_seconds']) { + $format .= ':s'; + $parts[] = 'second'; + } + + if ('single_text' === $options['widget']) { + $builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format)); + } else { + $hourOptions = $minuteOptions = $secondOptions = array( + 'error_bubbling' => true, + ); + + if ('choice' === $options['widget']) { + $hours = $minutes = array(); + + foreach ($options['hours'] as $hour) { + $hours[$hour] = str_pad($hour, 2, '0', STR_PAD_LEFT); + } + + // Only pass a subset of the options to children + $hourOptions['choices'] = $hours; + $hourOptions['empty_value'] = $options['empty_value']['hour']; + + if ($options['with_minutes']) { + foreach ($options['minutes'] as $minute) { + $minutes[$minute] = str_pad($minute, 2, '0', STR_PAD_LEFT); + } + + $minuteOptions['choices'] = $minutes; + $minuteOptions['empty_value'] = $options['empty_value']['minute']; + } + + if ($options['with_seconds']) { + $seconds = array(); + + foreach ($options['seconds'] as $second) { + $seconds[$second] = str_pad($second, 2, '0', STR_PAD_LEFT); + } + + $secondOptions['choices'] = $seconds; + $secondOptions['empty_value'] = $options['empty_value']['second']; + } + + // Append generic carry-along options + foreach (array('required', 'translation_domain') as $passOpt) { + $hourOptions[$passOpt] = $options[$passOpt]; + + if ($options['with_minutes']) { + $minuteOptions[$passOpt] = $options[$passOpt]; + } + + if ($options['with_seconds']) { + $secondOptions[$passOpt] = $options[$passOpt]; + } + } + } + + $builder->add('hour', $options['widget'], $hourOptions); + + if ($options['with_minutes']) { + $builder->add('minute', $options['widget'], $minuteOptions); + } + + if ($options['with_seconds']) { + $builder->add('second', $options['widget'], $secondOptions); + } + + $builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget'])); + } + + if ('string' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'H:i:s') + )); + } elseif ('timestamp' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone']) + )); + } elseif ('array' === $options['input']) { + $builder->addModelTransformer(new ReversedTransformer( + new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], $parts) + )); + } + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars = array_replace($view->vars, array( + 'widget' => $options['widget'], + 'with_minutes' => $options['with_minutes'], + 'with_seconds' => $options['with_seconds'], + )); + + if ('single_text' === $options['widget']) { + $view->vars['type'] = 'time'; + } + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $compound = function (Options $options) { + return $options['widget'] !== 'single_text'; + }; + + $emptyValue = $emptyValueDefault = function (Options $options) { + return $options['required'] ? null : ''; + }; + + $emptyValueNormalizer = function (Options $options, $emptyValue) use ($emptyValueDefault) { + if (is_array($emptyValue)) { + $default = $emptyValueDefault($options); + + return array_merge( + array('hour' => $default, 'minute' => $default, 'second' => $default), + $emptyValue + ); + } + + return array( + 'hour' => $emptyValue, + 'minute' => $emptyValue, + 'second' => $emptyValue + ); + }; + + $resolver->setDefaults(array( + 'hours' => range(0, 23), + 'minutes' => range(0, 59), + 'seconds' => range(0, 59), + 'widget' => 'choice', + 'input' => 'datetime', + 'with_minutes' => true, + 'with_seconds' => false, + 'model_timezone' => null, + 'view_timezone' => null, + 'empty_value' => $emptyValue, + // Don't modify \DateTime classes by reference, we treat + // them like immutable value objects + 'by_reference' => false, + 'error_bubbling' => false, + // If initialized with a \DateTime object, FormType initializes + // this option to "\DateTime". Since the internal, normalized + // representation is not \DateTime, but an array, we need to unset + // this option. + 'data_class' => null, + 'compound' => $compound, + )); + + $resolver->setNormalizers(array( + 'empty_value' => $emptyValueNormalizer, + )); + + $resolver->setAllowedValues(array( + 'input' => array( + 'datetime', + 'string', + 'timestamp', + 'array', + ), + 'widget' => array( + 'single_text', + 'text', + 'choice', + ), + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'time'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php new file mode 100644 index 0000000..cd4a2ad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.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\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class TimezoneType extends AbstractType +{ + /** + * Stores the available timezone choices + * @var array + */ + private static $timezones; + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'choices' => self::getTimezones(), + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'choice'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'timezone'; + } + + /** + * Returns the timezone choices. + * + * The choices are generated from the ICU function + * \DateTimeZone::listIdentifiers(). They are cached during a single request, + * so multiple timezone fields on the same page don't lead to unnecessary + * overhead. + * + * @return array The timezone choices + */ + public static function getTimezones() + { + if (null === static::$timezones) { + static::$timezones = array(); + + foreach (\DateTimeZone::listIdentifiers() as $timezone) { + $parts = explode('/', $timezone); + + if (count($parts) > 2) { + $region = $parts[0]; + $name = $parts[1].' - '.$parts[2]; + } elseif (count($parts) > 1) { + $region = $parts[0]; + $name = $parts[1]; + } else { + $region = 'Other'; + $name = $parts[0]; + } + + static::$timezones[$region][$timezone] = str_replace('_', ' ', $name); + } + } + + return static::$timezones; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/UrlType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/UrlType.php new file mode 100644 index 0000000..27749b1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/UrlType.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\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class UrlType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addEventSubscriber(new FixUrlProtocolListener($options['default_protocol'])); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'default_protocol' => 'http', + )); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'url'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/View/ChoiceView.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/View/ChoiceView.php new file mode 100644 index 0000000..97cdd21 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/View/ChoiceView.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\Form\Extension\Core\View; + +/** + * Represents a choice in templates. + * + * @author Bernhard Schussek + */ +class ChoiceView +{ + /** + * The original choice value. + * + * @var mixed + */ + public $data; + + /** + * The view representation of the choice. + * + * @var string + */ + public $value; + + /** + * The label displayed to humans. + * + * @var string + */ + public $label; + + /** + * Creates a new ChoiceView. + * + * @param mixed $data The original choice. + * @param string $value The view representation of the choice. + * @param string $label The label displayed to humans. + */ + public function __construct($data, $value, $label) + { + $this->data = $data; + $this->value = $value; + $this->label = $label; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php new file mode 100644 index 0000000..f9d9e40 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.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\Extension\Csrf; + +use Symfony\Component\Form\Extension\Csrf\Type; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * This extension protects forms by using a CSRF token. + * + * @author Bernhard Schussek + */ +class CsrfExtension extends AbstractExtension +{ + /** + * @var CsrfProviderInterface + */ + private $csrfProvider; + + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * @var null|string + */ + private $translationDomain; + + /** + * Constructor. + * + * @param CsrfProviderInterface $csrfProvider The CSRF provider + * @param TranslatorInterface $translator The translator for translating error messages. + * @param null|string $translationDomain The translation domain for translating. + */ + public function __construct(CsrfProviderInterface $csrfProvider, TranslatorInterface $translator = null, $translationDomain = null) + { + $this->csrfProvider = $csrfProvider; + $this->translator = $translator; + $this->translationDomain = $translationDomain; + } + + /** + * {@inheritDoc} + */ + protected function loadTypeExtensions() + { + return array( + new Type\FormTypeCsrfExtension($this->csrfProvider, true, '_token', $this->translator, $this->translationDomain), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.php new file mode 100644 index 0000000..7143b13 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.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\Form\Extension\Csrf\CsrfProvider; + +/** + * Marks classes able to provide CSRF protection + * + * You can generate a CSRF token by using the method generateCsrfToken(). To + * this method you should pass a value that is unique to the page that should + * be secured against CSRF attacks. This value doesn't necessarily have to be + * secret. Implementations of this interface are responsible for adding more + * secret information. + * + * If you want to secure a form submission against CSRF attacks, you could + * supply an "intention" string. This way you make sure that the form can only + * be submitted to pages that are designed to handle the form, that is, that use + * the same intention string to validate the CSRF token with isCsrfTokenValid(). + * + * @author Bernhard Schussek + */ +interface CsrfProviderInterface +{ + /** + * Generates a CSRF token for a page of your application. + * + * @param string $intention Some value that identifies the action intention + * (i.e. "authenticate"). Doesn't have to be a secret value. + */ + public function generateCsrfToken($intention); + + /** + * Validates a CSRF token. + * + * @param string $intention The intention used when generating the CSRF token + * @param string $token The token supplied by the browser + * + * @return Boolean Whether the token supplied by the browser is correct + */ + public function isCsrfTokenValid($intention, $token); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php new file mode 100644 index 0000000..5354886 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.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\Form\Extension\Csrf\CsrfProvider; + +/** + * Default implementation of CsrfProviderInterface. + * + * This provider uses the session ID returned by session_id() as well as a + * user-defined secret value to secure the CSRF token. + * + * @author Bernhard Schussek + */ +class DefaultCsrfProvider implements CsrfProviderInterface +{ + /** + * A secret value used for generating the CSRF token + * @var string + */ + protected $secret; + + /** + * Initializes the provider with a secret value + * + * A recommended value for the secret is a generated value with at least + * 32 characters and mixed letters, digits and special characters. + * + * @param string $secret A secret value included in the CSRF token + */ + public function __construct($secret) + { + $this->secret = $secret; + } + + /** + * {@inheritDoc} + */ + public function generateCsrfToken($intention) + { + return sha1($this->secret.$intention.$this->getSessionId()); + } + + /** + * {@inheritDoc} + */ + public function isCsrfTokenValid($intention, $token) + { + return $token === $this->generateCsrfToken($intention); + } + + /** + * Returns the ID of the user session. + * + * Automatically starts the session if necessary. + * + * @return string The session ID + */ + protected function getSessionId() + { + if (version_compare(PHP_VERSION, '5.4', '>=')) { + if (PHP_SESSION_NONE === session_status()) { + session_start(); + } + } elseif (!session_id()) { + session_start(); + } + + return session_id(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php new file mode 100644 index 0000000..ea1fa58 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.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\Extension\Csrf\CsrfProvider; + +use Symfony\Component\HttpFoundation\Session\Session; + +/** + * This provider uses a Symfony2 Session object to retrieve the user's + * session ID. + * + * @see DefaultCsrfProvider + * + * @author Bernhard Schussek + */ +class SessionCsrfProvider extends DefaultCsrfProvider +{ + /** + * The user session from which the session ID is returned + * @var Session + */ + protected $session; + + /** + * Initializes the provider with a Session object and a secret value. + * + * A recommended value for the secret is a generated value with at least + * 32 characters and mixed letters, digits and special characters. + * + * @param Session $session The user session + * @param string $secret A secret value included in the CSRF token + */ + public function __construct(Session $session, $secret) + { + parent::__construct($secret); + + $this->session = $session; + } + + /** + * {@inheritdoc} + */ + protected function getSessionId() + { + $this->session->start(); + + return $this->session->getId(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php new file mode 100644 index 0000000..547e9d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.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\Extension\Csrf\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * @author Bernhard Schussek + */ +class CsrfValidationListener implements EventSubscriberInterface +{ + /** + * The name of the CSRF field + * @var string + */ + private $fieldName; + + /** + * The provider for generating and validating CSRF tokens + * @var CsrfProviderInterface + */ + private $csrfProvider; + + /** + * A text mentioning the intention of the CSRF token + * + * Validation of the token will only succeed if it was generated in the + * same session and with the same intention. + * + * @var string + */ + private $intention; + + /** + * The message displayed in case of an error. + * @var string + */ + private $errorMessage; + + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * @var null|string + */ + private $translationDomain; + + public static function getSubscribedEvents() + { + return array( + FormEvents::PRE_SUBMIT => 'preSubmit', + ); + } + + public function __construct($fieldName, CsrfProviderInterface $csrfProvider, $intention, $errorMessage, TranslatorInterface $translator = null, $translationDomain = null) + { + $this->fieldName = $fieldName; + $this->csrfProvider = $csrfProvider; + $this->intention = $intention; + $this->errorMessage = $errorMessage; + $this->translator = $translator; + $this->translationDomain = $translationDomain; + } + + public function preSubmit(FormEvent $event) + { + $form = $event->getForm(); + $data = $event->getData(); + + if ($form->isRoot() && $form->getConfig()->getOption('compound')) { + if (!isset($data[$this->fieldName]) || !$this->csrfProvider->isCsrfTokenValid($this->intention, $data[$this->fieldName])) { + $errorMessage = $this->errorMessage; + + if (null !== $this->translator) { + $errorMessage = $this->translator->trans($errorMessage, array(), $this->translationDomain); + } + + $form->addError(new FormError($errorMessage)); + } + + if (is_array($data)) { + unset($data[$this->fieldName]); + } + } + + $event->setData($data); + } + + /** + * Alias of {@link preSubmit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link preSubmit()} instead. + */ + public function preBind(FormEvent $event) + { + $this->preSubmit($event); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php new file mode 100644 index 0000000..336cf04 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.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\Form\Extension\Csrf\Type; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * @author Bernhard Schussek + */ +class FormTypeCsrfExtension extends AbstractTypeExtension +{ + /** + * @var CsrfProviderInterface + */ + private $defaultCsrfProvider; + + /** + * @var Boolean + */ + private $defaultEnabled; + + /** + * @var string + */ + private $defaultFieldName; + + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * @var null|string + */ + private $translationDomain; + + public function __construct(CsrfProviderInterface $defaultCsrfProvider, $defaultEnabled = true, $defaultFieldName = '_token', TranslatorInterface $translator = null, $translationDomain = null) + { + $this->defaultCsrfProvider = $defaultCsrfProvider; + $this->defaultEnabled = $defaultEnabled; + $this->defaultFieldName = $defaultFieldName; + $this->translator = $translator; + $this->translationDomain = $translationDomain; + } + + /** + * Adds a CSRF field to the form when the CSRF protection is enabled. + * + * @param FormBuilderInterface $builder The form builder + * @param array $options The options + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + if (!$options['csrf_protection']) { + return; + } + + $builder + ->setAttribute('csrf_factory', $builder->getFormFactory()) + ->addEventSubscriber(new CsrfValidationListener( + $options['csrf_field_name'], + $options['csrf_provider'], + $options['intention'], + $options['csrf_message'], + $this->translator, + $this->translationDomain + )) + ; + } + + /** + * Adds a CSRF field to the root form view. + * + * @param FormView $view The form view + * @param FormInterface $form The form + * @param array $options The options + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + if ($options['csrf_protection'] && !$view->parent && $options['compound']) { + $factory = $form->getConfig()->getAttribute('csrf_factory'); + $data = $options['csrf_provider']->generateCsrfToken($options['intention']); + + $csrfForm = $factory->createNamed($options['csrf_field_name'], 'hidden', $data, array( + 'mapped' => false, + )); + + $view->children[$options['csrf_field_name']] = $csrfForm->createView($view); + } + } + + /** + * {@inheritDoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'csrf_protection' => $this->defaultEnabled, + 'csrf_field_name' => $this->defaultFieldName, + 'csrf_provider' => $this->defaultCsrfProvider, + 'csrf_message' => 'The CSRF token is invalid. Please try to resubmit the form.', + 'intention' => 'unknown', + )); + } + + /** + * {@inheritDoc} + */ + public function getExtendedType() + { + return 'form'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php new file mode 100644 index 0000000..6637ac8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.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\Form\Extension\DependencyInjection; + +use Symfony\Component\Form\FormExtensionInterface; +use Symfony\Component\Form\FormTypeGuesserChain; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\ContainerInterface; + +class DependencyInjectionExtension implements FormExtensionInterface +{ + private $container; + + private $typeServiceIds; + + private $guesserServiceIds; + + private $guesser; + + private $guesserLoaded = false; + + public function __construct(ContainerInterface $container, + array $typeServiceIds, array $typeExtensionServiceIds, + array $guesserServiceIds) + { + $this->container = $container; + $this->typeServiceIds = $typeServiceIds; + $this->typeExtensionServiceIds = $typeExtensionServiceIds; + $this->guesserServiceIds = $guesserServiceIds; + } + + public function getType($name) + { + if (!isset($this->typeServiceIds[$name])) { + throw new InvalidArgumentException(sprintf('The field type "%s" is not registered with the service container.', $name)); + } + + $type = $this->container->get($this->typeServiceIds[$name]); + + if ($type->getName() !== $name) { + throw new InvalidArgumentException( + sprintf('The type name specified for the service "%s" does not match the actual name. Expected "%s", given "%s"', + $this->typeServiceIds[$name], + $name, + $type->getName() + )); + } + + return $type; + } + + public function hasType($name) + { + return isset($this->typeServiceIds[$name]); + } + + public function getTypeExtensions($name) + { + $extensions = array(); + + if (isset($this->typeExtensionServiceIds[$name])) { + foreach ($this->typeExtensionServiceIds[$name] as $serviceId) { + $extensions[] = $this->container->get($serviceId); + } + } + + return $extensions; + } + + public function hasTypeExtensions($name) + { + return isset($this->typeExtensionServiceIds[$name]); + } + + public function getTypeGuesser() + { + if (!$this->guesserLoaded) { + $this->guesserLoaded = true; + $guessers = array(); + + foreach ($this->guesserServiceIds as $serviceId) { + $guessers[] = $this->container->get($serviceId); + } + + if (count($guessers) > 0) { + $this->guesser = new FormTypeGuesserChain($guessers); + } + } + + return $this->guesser; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/EventListener/BindRequestListener.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/EventListener/BindRequestListener.php new file mode 100644 index 0000000..6205b98 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/EventListener/BindRequestListener.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\Form\Extension\HttpFoundation\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * @author Bernhard Schussek + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Pass the + * Request instance to {@link Form::process()} instead. + */ +class BindRequestListener implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + // High priority in order to supersede other listeners + return array(FormEvents::PRE_BIND => array('preBind', 128)); + } + + public function preBind(FormEvent $event) + { + $form = $event->getForm(); + + /* @var Request $request */ + $request = $event->getData(); + + // Only proceed if we actually deal with a Request + if (!$request instanceof Request) { + return; + } + + // Uncomment this as soon as the deprecation note should be shown + // trigger_error('Passing a Request instance to Form::submit() is deprecated since version 2.3 and will be disabled in 3.0. Call Form::process($request) instead.', E_USER_DEPRECATED); + + $name = $form->getConfig()->getName(); + $default = $form->getConfig()->getCompound() ? array() : null; + + // Store the bound data in case of a post request + switch ($request->getMethod()) { + case 'POST': + case 'PUT': + case 'DELETE': + case 'PATCH': + if ('' === $name) { + // Form bound without name + $params = $request->request->all(); + $files = $request->files->all(); + } else { + $params = $request->request->get($name, $default); + $files = $request->files->get($name, $default); + } + + if (is_array($params) && is_array($files)) { + $data = array_replace_recursive($params, $files); + } else { + $data = $params ?: $files; + } + + break; + + case 'GET': + $data = '' === $name + ? $request->query->all() + : $request->query->get($name, $default); + + break; + + default: + throw new LogicException(sprintf( + 'The request method "%s" is not supported', + $request->getMethod() + )); + } + + $event->setData($data); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationExtension.php new file mode 100644 index 0000000..08bd89c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationExtension.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\Form\Extension\HttpFoundation; + +use Symfony\Component\Form\AbstractExtension; + +/** + * Integrates the HttpFoundation component with the Form library. + * + * @author Bernhard Schussek + */ +class HttpFoundationExtension extends AbstractExtension +{ + protected function loadTypeExtensions() + { + return array( + new Type\FormTypeHttpFoundationExtension(), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php new file mode 100644 index 0000000..dcb6ede --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.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\Extension\HttpFoundation; + +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\RequestHandlerInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * A request processor using the {@link Request} class of the HttpFoundation + * component. + * + * @author Bernhard Schussek + */ +class HttpFoundationRequestHandler implements RequestHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleRequest(FormInterface $form, $request = null) + { + if (!$request instanceof Request) { + throw new UnexpectedTypeException($request, 'Symfony\Component\HttpFoundation\Request'); + } + + $name = $form->getName(); + $method = $form->getConfig()->getMethod(); + + if ($method !== $request->getMethod()) { + return; + } + + if ('GET' === $method) { + if ('' === $name) { + $data = $request->query->all(); + } else { + // Don't submit GET requests if the form's name does not exist + // in the request + if (!$request->query->has($name)) { + return; + } + + $data = $request->query->get($name); + } + } else { + if ('' === $name) { + $params = $request->request->all(); + $files = $request->files->all(); + } else { + $default = $form->getConfig()->getCompound() ? array() : null; + $params = $request->request->get($name, $default); + $files = $request->files->get($name, $default); + } + + 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); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php new file mode 100644 index 0000000..9b09b05 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.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\Extension\HttpFoundation\Type; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Extension\HttpFoundation\EventListener\BindRequestListener; +use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler; +use Symfony\Component\Form\FormBuilderInterface; + +/** + * @author Bernhard Schussek + */ +class FormTypeHttpFoundationExtension extends AbstractTypeExtension +{ + /** + * @var BindRequestListener + */ + private $listener; + + /** + * @var HttpFoundationRequestHandler + */ + private $requestHandler; + + public function __construct() + { + $this->listener = new BindRequestListener(); + $this->requestHandler = new HttpFoundationRequestHandler(); + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addEventSubscriber($this->listener); + $builder->setRequestHandler($this->requestHandler); + } + + /** + * {@inheritdoc} + */ + public function getExtendedType() + { + return 'form'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php new file mode 100644 index 0000000..573cb51 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Templating/TemplatingExtension.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\Extension\Templating; + +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\Form\FormRenderer; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Templating\PhpEngine; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper; + +/** + * Integrates the Templating component with the Form library. + * + * @author Bernhard Schussek + */ +class TemplatingExtension extends AbstractExtension +{ + public function __construct(PhpEngine $engine, CsrfProviderInterface $csrfProvider = null, array $defaultThemes = array()) + { + $engine->addHelpers(array( + new FormHelper(new FormRenderer(new TemplatingRendererEngine($engine, $defaultThemes), $csrfProvider)) + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.php new file mode 100644 index 0000000..c1dda60 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.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\Extension\Templating; + +use Symfony\Component\Form\AbstractRendererEngine; +use Symfony\Component\Form\FormView; +use Symfony\Component\Templating\EngineInterface; + +/** + * @author Bernhard Schussek + */ +class TemplatingRendererEngine extends AbstractRendererEngine +{ + /** + * @var EngineInterface + */ + private $engine; + + public function __construct(EngineInterface $engine, array $defaultThemes = array()) + { + parent::__construct($defaultThemes); + + $this->engine = $engine; + } + + /** + * {@inheritdoc} + */ + public function renderBlock(FormView $view, $resource, $blockName, array $variables = array()) + { + return trim($this->engine->render($resource, $variables)); + } + + /** + * Loads the cache with the resource for a given block name. + * + * This implementation tries to load as few blocks as possible, since each block + * is represented by a template on the file system. + * + * @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 Boolean True if the resource could be loaded, false otherwise. + */ + protected function loadResourceForBlockName($cacheKey, FormView $view, $blockName) + { + // Recursively try to find the block in the themes assigned to $view, + // then of its parent form, then of the parent form of the parent and so on. + // When the root form 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) { + if ($this->loadResourceFromTheme($cacheKey, $blockName, $this->themes[$cacheKey][$i])) { + return true; + } + } + } + + // Check the default themes once we reach the root form without success + if (!$view->parent) { + for ($i = count($this->defaultThemes) - 1; $i >= 0; --$i) { + if ($this->loadResourceFromTheme($cacheKey, $blockName, $this->defaultThemes[$i])) { + return true; + } + } + } + + // If we did not find anything in the themes of the current view, proceed + // with the themes of the parent view + if ($view->parent) { + $parentCacheKey = $view->parent->vars[self::CACHE_KEY_VAR]; + + if (!isset($this->resources[$parentCacheKey][$blockName])) { + $this->loadResourceForBlockName($parentCacheKey, $view->parent, $blockName); + } + + // If a template exists in the parent themes, cache that template + // for the current theme as well to speed up further accesses + if ($this->resources[$parentCacheKey][$blockName]) { + $this->resources[$cacheKey][$blockName] = $this->resources[$parentCacheKey][$blockName]; + + return true; + } + } + + // Cache that we didn't find anything to speed up further accesses + $this->resources[$cacheKey][$blockName] = false; + + return false; + } + + /** + * Tries to load the resource for a block from a theme. + * + * @param string $cacheKey The cache key for storing the resource. + * @param string $blockName The name of the block to load a resource for. + * @param mixed $theme The theme to load the block from. + * + * @return Boolean True if the resource could be loaded, false otherwise. + */ + protected function loadResourceFromTheme($cacheKey, $blockName, $theme) + { + if ($this->engine->exists($templateName = $theme.':'.$blockName.'.html.php')) { + $this->resources[$cacheKey][$blockName] = $templateName; + + return true; + } + + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Constraints/Form.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Constraints/Form.php new file mode 100644 index 0000000..87e3329 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Constraints/Form.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\Extension\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; + +/** + * @author Bernhard Schussek + */ +class Form extends Constraint +{ + /** + * Violation code marking an invalid form. + */ + const ERR_INVALID = 1; + + /** + * {@inheritdoc} + */ + public function getTargets() + { + return self::CLASS_CONSTRAINT; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php new file mode 100644 index 0000000..bad5a00 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.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\Form\Extension\Validator\Constraints; + +use Symfony\Component\Form\ClickableInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Extension\Validator\Util\ServerParams; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; + +/** + * @author Bernhard Schussek + */ +class FormValidator extends ConstraintValidator +{ + /** + * @var ServerParams + */ + private $serverParams; + + /** + * Creates a validator with the given server parameters. + * + * @param ServerParams $params The server parameters. Default + * parameters are created if null. + */ + public function __construct(ServerParams $params = null) + { + $this->serverParams = $params ?: new ServerParams(); + } + + /** + * {@inheritdoc} + */ + public function validate($form, Constraint $constraint) + { + if (!$form instanceof FormInterface) { + return; + } + + /* @var FormInterface $form */ + $config = $form->getConfig(); + + if ($form->isSynchronized()) { + // Validate the form data only if transformation succeeded + $groups = self::getValidationGroups($form); + + // Validate the data against its own constraints + if (self::allowDataWalking($form)) { + foreach ($groups as $group) { + $this->context->validate($form->getData(), 'data', $group, true); + } + } + + // Validate the data against the constraints defined + // in the form + $constraints = $config->getOption('constraints'); + foreach ($constraints as $constraint) { + foreach ($groups as $group) { + if (in_array($group, $constraint->groups)) { + $this->context->validateValue($form->getData(), $constraint, 'data', $group); + + // Prevent duplicate validation + continue 2; + } + } + } + } else { + $childrenSynchronized = true; + + foreach ($form as $child) { + if (!$child->isSynchronized()) { + $childrenSynchronized = false; + break; + } + } + + // Mark the form with an error if it is not synchronized BUT all + // of its children are synchronized. If any child is not + // synchronized, an error is displayed there already and showing + // a second error in its parent form is pointless, or worse, may + // lead to duplicate errors if error bubbling is enabled on the + // child. + // See also https://github.com/symfony/symfony/issues/4359 + if ($childrenSynchronized) { + $clientDataAsString = is_scalar($form->getViewData()) + ? (string) $form->getViewData() + : gettype($form->getViewData()); + + $this->context->addViolation( + $config->getOption('invalid_message'), + array_replace(array('{{ value }}' => $clientDataAsString), $config->getOption('invalid_message_parameters')), + $form->getViewData(), + null, + Form::ERR_INVALID + ); + } + } + + // Mark the form with an error if it contains extra fields + if (count($form->getExtraData()) > 0) { + $this->context->addViolation( + $config->getOption('extra_fields_message'), + array('{{ extra_fields }}' => implode('", "', array_keys($form->getExtraData()))), + $form->getExtraData() + ); + } + + // Mark the form with an error if the uploaded size was too large + $length = $this->serverParams->getContentLength(); + + if ($form->isRoot() && null !== $length) { + $max = $this->serverParams->getPostMaxSize(); + + if (!empty($max) && $length > $max) { + $this->context->addViolation( + $config->getOption('post_max_size_message'), + array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize()), + $length + ); + } + } + } + + /** + * Returns whether the data of a form may be walked. + * + * @param FormInterface $form The form to test. + * + * @return Boolean Whether the graph walker may walk the data. + */ + private static function allowDataWalking(FormInterface $form) + { + $data = $form->getData(); + + // Scalar values cannot have mapped constraints + if (!is_object($data) && !is_array($data)) { + return false; + } + + // Root forms are always validated + if ($form->isRoot()) { + return true; + } + + // Non-root forms are validated if validation cascading + // is enabled in all ancestor forms + while (null !== ($form = $form->getParent())) { + if (!$form->getConfig()->getOption('cascade_validation')) { + return false; + } + } + + return true; + } + + /** + * Returns the validation groups of the given form. + * + * @param FormInterface $form The form. + * + * @return array The validation groups. + */ + private static function getValidationGroups(FormInterface $form) + { + $button = self::findClickedButton($form->getRoot()); + + if (null !== $button) { + $groups = $button->getConfig()->getOption('validation_groups'); + + if (null !== $groups) { + return self::resolveValidationGroups($groups, $form); + } + } + + do { + $groups = $form->getConfig()->getOption('validation_groups'); + + if (null !== $groups) { + return self::resolveValidationGroups($groups, $form); + } + + $form = $form->getParent(); + } while (null !== $form); + + return array(Constraint::DEFAULT_GROUP); + } + + /** + * Extracts a clicked button from a form tree, if one exists. + * + * @param FormInterface $form The root form. + * + * @return ClickableInterface|null The clicked button or null. + */ + private static function findClickedButton(FormInterface $form) + { + if ($form instanceof ClickableInterface && $form->isClicked()) { + return $form; + } + + foreach ($form as $child) { + if (null !== ($button = self::findClickedButton($child))) { + return $button; + } + } + + return null; + } + + /** + * Post-processes the validation groups option for a given form. + * + * @param array|callable $groups The validation groups. + * @param FormInterface $form The validated form. + * + * @return array The validation groups. + */ + private static function resolveValidationGroups($groups, FormInterface $form) + { + if (!is_string($groups) && is_callable($groups)) { + $groups = call_user_func($groups, $form); + } + + return (array) $groups; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php new file mode 100644 index 0000000..1414753 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.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\Form\Extension\Validator\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface; +use Symfony\Component\Validator\ValidatorInterface; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Validator\Constraints\Form; + +/** + * @author Bernhard Schussek + */ +class ValidationListener implements EventSubscriberInterface +{ + private $validator; + + private $violationMapper; + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array(FormEvents::POST_SUBMIT => 'validateForm'); + } + + public function __construct(ValidatorInterface $validator, ViolationMapperInterface $violationMapper) + { + $this->validator = $validator; + $this->violationMapper = $violationMapper; + } + + /** + * Validates the form and its domain object. + * + * @param FormEvent $event The event object + */ + public function validateForm(FormEvent $event) + { + $form = $event->getForm(); + + if ($form->isRoot()) { + // Validate the form in group "Default" + $violations = $this->validator->validate($form); + + if (count($violations) > 0) { + foreach ($violations as $violation) { + // Allow the "invalid" constraint to be put onto + // non-synchronized forms + $allowNonSynchronized = Form::ERR_INVALID === $violation->getCode(); + + $this->violationMapper->mapViolation($violation, $form, $allowNonSynchronized); + } + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php new file mode 100644 index 0000000..7c5e678 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.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\Extension\Validator\Type; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * Encapsulates common logic of {@link FormTypeValidatorExtension} and + * {@link SubmitTypeValidatorExtension}. + * + * @author Bernhard Schussek + */ +abstract class BaseValidatorExtension extends AbstractTypeExtension +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + // Make sure that validation groups end up as null, closure or array + $validationGroupsNormalizer = function (Options $options, $groups) { + if (false === $groups) { + return array(); + } + + if (empty($groups)) { + return null; + } + + if (is_callable($groups)) { + return $groups; + } + + return (array) $groups; + }; + + $resolver->setDefaults(array( + 'validation_groups' => null, + )); + + $resolver->setNormalizers(array( + 'validation_groups' => $validationGroupsNormalizer, + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php new file mode 100644 index 0000000..2105997 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.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\Extension\Validator\Type; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper; +use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener; +use Symfony\Component\Validator\ValidatorInterface; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +class FormTypeValidatorExtension extends BaseValidatorExtension +{ + /** + * @var ValidatorInterface + */ + private $validator; + + /** + * @var ViolationMapper + */ + private $violationMapper; + + public function __construct(ValidatorInterface $validator) + { + $this->validator = $validator; + $this->violationMapper = new ViolationMapper(); + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addEventSubscriber(new ValidationListener($this->validator, $this->violationMapper)); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + parent::setDefaultOptions($resolver); + + // Constraint should always be converted to an array + $constraintsNormalizer = function (Options $options, $constraints) { + return is_object($constraints) ? array($constraints) : (array) $constraints; + }; + + $resolver->setDefaults(array( + 'error_mapping' => array(), + 'constraints' => array(), + 'cascade_validation' => false, + 'invalid_message' => 'This value is not valid.', + 'invalid_message_parameters' => array(), + 'extra_fields_message' => 'This form should not contain extra fields.', + 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', + )); + + $resolver->setNormalizers(array( + 'constraints' => $constraintsNormalizer, + )); + } + + /** + * {@inheritdoc} + */ + public function getExtendedType() + { + return 'form'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php new file mode 100644 index 0000000..858ff0f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.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\Extension\Validator\Type; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +class RepeatedTypeValidatorExtension extends AbstractTypeExtension +{ + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + // Map errors to the first field + $errorMapping = function (Options $options) { + return array('.' => $options['first_name']); + }; + + $resolver->setDefaults(array( + 'error_mapping' => $errorMapping, + )); + } + + /** + * {@inheritdoc} + */ + public function getExtendedType() + { + return 'repeated'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php new file mode 100644 index 0000000..5aad67f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.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\Extension\Validator\Type; + +use Symfony\Component\Form\AbstractTypeExtension; + +/** + * @author Bernhard Schussek + */ +class SubmitTypeValidatorExtension extends AbstractTypeExtension +{ + /** + * {@inheritdoc} + */ + public function getExtendedType() + { + return 'submit'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php new file mode 100644 index 0000000..fab6ac2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Util/ServerParams.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\Extension\Validator\Util; + +/** + * @author Bernhard Schussek + */ +class ServerParams +{ + /** + * Returns maximum post size in bytes. + * + * @return null|integer The maximum post size in bytes + */ + public function getPostMaxSize() + { + $iniMax = $this->getNormalizedIniPostMaxSize(); + + if ('' === $iniMax) { + return null; + } + + if (preg_match('#^\+?(0X?)?(.*?)([KMG]?)$#', $iniMax, $match)) { + $shifts = array('' => 0, 'K' => 10, 'M' => 20, 'G' => 30); + $bases = array('' => 10, '0' => 8, '0X' => 16); + + return intval($match[2], $bases[$match[1]]) << $shifts[$match[3]]; + } + + return 0; + } + + /** + * 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() + { + return isset($_SERVER['CONTENT_LENGTH']) + ? (int) $_SERVER['CONTENT_LENGTH'] + : null; + } + +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php new file mode 100644 index 0000000..9cff22a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.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\Extension\Validator; + +use Symfony\Component\Form\Extension\Validator\Type; +use Symfony\Component\Form\Extension\Validator\Constraints\Form; +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\Validator\ValidatorInterface; +use Symfony\Component\Validator\Constraints\Valid; + +/** + * Extension supporting the Symfony2 Validator component in forms. + * + * @author Bernhard Schussek + */ +class ValidatorExtension extends AbstractExtension +{ + private $validator; + + public function __construct(ValidatorInterface $validator) + { + $this->validator = $validator; + + // Register the form constraints in the validator programmatically. + // This functionality is required when using the Form component without + // the DIC, where the XML file is loaded automatically. Thus the following + // code must be kept synchronized with validation.xml + + /** @var \Symfony\Component\Validator\Mapping\ClassMetadata $metadata */ + $metadata = $this->validator->getMetadataFactory()->getMetadataFor('Symfony\Component\Form\Form'); + $metadata->addConstraint(new Form()); + $metadata->addPropertyConstraint('children', new Valid()); + } + + public function loadTypeGuesser() + { + return new ValidatorTypeGuesser($this->validator->getMetadataFactory()); + } + + protected function loadTypeExtensions() + { + return array( + new Type\FormTypeValidatorExtension($this->validator), + new Type\RepeatedTypeValidatorExtension(), + new Type\SubmitTypeValidatorExtension(), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php new file mode 100644 index 0000000..dcd9cc5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php @@ -0,0 +1,286 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator; + +use Symfony\Component\Form\FormTypeGuesserInterface; +use Symfony\Component\Form\Guess\Guess; +use Symfony\Component\Form\Guess\TypeGuess; +use Symfony\Component\Form\Guess\ValueGuess; +use Symfony\Component\Validator\MetadataFactoryInterface; +use Symfony\Component\Validator\Constraint; + +class ValidatorTypeGuesser implements FormTypeGuesserInterface +{ + private $metadataFactory; + + public function __construct(MetadataFactoryInterface $metadataFactory) + { + $this->metadataFactory = $metadataFactory; + } + + /** + * {@inheritDoc} + */ + public function guessType($class, $property) + { + $guesser = $this; + + return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) { + return $guesser->guessTypeForConstraint($constraint); + }); + } + + /** + * {@inheritDoc} + */ + public function guessRequired($class, $property) + { + $guesser = $this; + + return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) { + return $guesser->guessRequiredForConstraint($constraint); + // If we don't find any constraint telling otherwise, we can assume + // that a field is not required (with LOW_CONFIDENCE) + }, false); + } + + /** + * {@inheritDoc} + */ + public function guessMaxLength($class, $property) + { + $guesser = $this; + + return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) { + return $guesser->guessMaxLengthForConstraint($constraint); + }); + } + + /** + * {@inheritDoc} + */ + public function guessPattern($class, $property) + { + $guesser = $this; + + return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) { + return $guesser->guessPatternForConstraint($constraint); + }); + } + + /** + * Guesses a field class name for a given constraint + * + * @param Constraint $constraint The constraint to guess for + * + * @return TypeGuess The guessed field class and options + */ + public function guessTypeForConstraint(Constraint $constraint) + { + switch (get_class($constraint)) { + case 'Symfony\Component\Validator\Constraints\Type': + switch ($constraint->type) { + case 'array': + return new TypeGuess('collection', array(), Guess::MEDIUM_CONFIDENCE); + case 'boolean': + case 'bool': + return new TypeGuess('checkbox', array(), Guess::MEDIUM_CONFIDENCE); + + case 'double': + case 'float': + case 'numeric': + case 'real': + return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE); + + case 'integer': + case 'int': + case 'long': + return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE); + + case '\DateTime': + return new TypeGuess('date', array(), Guess::MEDIUM_CONFIDENCE); + + case 'string': + return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); + } + break; + + case 'Symfony\Component\Validator\Constraints\Country': + return new TypeGuess('country', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Date': + return new TypeGuess('date', array('input' => 'string'), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\DateTime': + return new TypeGuess('datetime', array('input' => 'string'), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Email': + return new TypeGuess('email', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\File': + case 'Symfony\Component\Validator\Constraints\Image': + return new TypeGuess('file', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Language': + return new TypeGuess('language', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Locale': + return new TypeGuess('locale', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Time': + return new TypeGuess('time', array('input' => 'string'), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Url': + return new TypeGuess('url', array(), Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Ip': + return new TypeGuess('text', array(), Guess::MEDIUM_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\MaxLength': + case 'Symfony\Component\Validator\Constraints\MinLength': + case 'Symfony\Component\Validator\Constraints\Regex': + return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Min': + case 'Symfony\Component\Validator\Constraints\Max': + return new TypeGuess('number', array(), Guess::LOW_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\MinCount': + case 'Symfony\Component\Validator\Constraints\MaxCount': + return new TypeGuess('collection', array(), Guess::LOW_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\True': + case 'Symfony\Component\Validator\Constraints\False': + return new TypeGuess('checkbox', array(), Guess::MEDIUM_CONFIDENCE); + } + + return null; + } + + /** + * Guesses whether a field is required based on the given constraint + * + * @param Constraint $constraint The constraint to guess for + * + * @return Guess The guess whether the field is required + */ + public function guessRequiredForConstraint(Constraint $constraint) + { + switch (get_class($constraint)) { + case 'Symfony\Component\Validator\Constraints\NotNull': + case 'Symfony\Component\Validator\Constraints\NotBlank': + case 'Symfony\Component\Validator\Constraints\True': + return new ValueGuess(true, Guess::HIGH_CONFIDENCE); + } + + return null; + } + + /** + * Guesses a field's maximum length based on the given constraint + * + * @param Constraint $constraint The constraint to guess for + * + * @return Guess The guess for the maximum length + */ + public function guessMaxLengthForConstraint(Constraint $constraint) + { + switch (get_class($constraint)) { + case 'Symfony\Component\Validator\Constraints\MaxLength': + return new ValueGuess($constraint->limit, Guess::HIGH_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Type': + if (in_array($constraint->type, array('double', 'float', 'numeric', 'real'))) { + return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); + } + break; + + case 'Symfony\Component\Validator\Constraints\Max': + return new ValueGuess(strlen((string) $constraint->limit), Guess::LOW_CONFIDENCE); + } + + return null; + } + + /** + * Guesses a field's pattern based on the given constraint + * + * @param Constraint $constraint The constraint to guess for + * + * @return Guess The guess for the pattern + */ + public function guessPatternForConstraint(Constraint $constraint) + { + switch (get_class($constraint)) { + case 'Symfony\Component\Validator\Constraints\MinLength': + return new ValueGuess(sprintf('.{%s,}', (string) $constraint->limit), Guess::LOW_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Regex': + $htmlPattern = $constraint->getHtmlPattern(); + + if (null !== $htmlPattern) { + return new ValueGuess($htmlPattern, Guess::HIGH_CONFIDENCE); + } + break; + + case 'Symfony\Component\Validator\Constraints\Min': + return new ValueGuess(sprintf('.{%s,}', strlen((string) $constraint->limit)), Guess::LOW_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Type': + if (in_array($constraint->type, array('double', 'float', 'numeric', 'real'))) { + return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); + } + break; + } + + return null; + } + + /** + * Iterates over the constraints of a property, executes a constraints on + * them and returns the best guess + * + * @param string $class The class to read the constraints from + * @param string $property The property for which to find constraints + * @param \Closure $closure The closure that returns a guess + * for a given constraint + * @param mixed $defaultValue The default value assumed if no other value + * can be guessed. + * + * @return Guess The guessed value with the highest confidence + */ + protected function guess($class, $property, \Closure $closure, $defaultValue = null) + { + $guesses = array(); + $classMetadata = $this->metadataFactory->getMetadataFor($class); + + if ($classMetadata->hasMemberMetadatas($property)) { + $memberMetadatas = $classMetadata->getMemberMetadatas($property); + + foreach ($memberMetadatas as $memberMetadata) { + $constraints = $memberMetadata->getConstraints(); + + foreach ($constraints as $constraint) { + if ($guess = $closure($constraint)) { + $guesses[] = $guess; + } + } + } + + if (null !== $defaultValue) { + $guesses[] = new ValueGuess($defaultValue, Guess::LOW_CONFIDENCE); + } + } + + return Guess::getBestGuess($guesses); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php new file mode 100644 index 0000000..7b96efb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.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\Form\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Exception\ErrorMappingException; + +/** + * @author Bernhard Schussek + */ +class MappingRule +{ + /** + * @var FormInterface + */ + private $origin; + + /** + * @var string + */ + private $propertyPath; + + /** + * @var string + */ + private $targetPath; + + public function __construct(FormInterface $origin, $propertyPath, $targetPath) + { + $this->origin = $origin; + $this->propertyPath = $propertyPath; + $this->targetPath = $targetPath; + } + + /** + * @return FormInterface + */ + public function getOrigin() + { + return $this->origin; + } + + /** + * Matches a property path against the rule path. + * + * If the rule matches, the form mapped by the rule is returned. + * Otherwise this method returns false. + * + * @param string $propertyPath The property path to match against the rule. + * + * @return null|FormInterface The mapped form or null. + */ + public function match($propertyPath) + { + if ($propertyPath === (string) $this->propertyPath) { + return $this->getTarget(); + } + + return null; + } + + /** + * Matches a property path against a prefix of the rule path. + * + * @param string $propertyPath The property path to match against the rule. + * + * @return Boolean Whether the property path is a prefix of the rule or not. + */ + public function isPrefix($propertyPath) + { + $length = strlen($propertyPath); + $prefix = substr($this->propertyPath, 0, $length); + $next = isset($this->propertyPath[$length]) ? $this->propertyPath[$length] : null; + + return $prefix === $propertyPath && ('[' === $next || '.' === $next); + } + + /** + * @return FormInterface + * + * @throws ErrorMappingException + */ + public function getTarget() + { + $childNames = explode('.', $this->targetPath); + $target = $this->origin; + + foreach ($childNames as $childName) { + if (!$target->has($childName)) { + throw new ErrorMappingException(sprintf('The child "%s" of "%s" mapped by the rule "%s" in "%s" does not exist.', $childName, $target->getName(), $this->targetPath, $this->origin->getName())); + } + $target = $target->get($childName); + } + + return $target; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php new file mode 100644 index 0000000..ef5c9fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.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\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\FormInterface; +use Symfony\Component\PropertyAccess\PropertyPath; + +/** + * @author Bernhard Schussek + */ +class RelativePath extends PropertyPath +{ + /** + * @var FormInterface + */ + private $root; + + /** + * @param FormInterface $root + * @param string $propertyPath + */ + public function __construct(FormInterface $root, $propertyPath) + { + parent::__construct($propertyPath); + + $this->root = $root; + } + + /** + * @return FormInterface + */ + public function getRoot() + { + return $this->root; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php new file mode 100644 index 0000000..8a7636c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php @@ -0,0 +1,299 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Util\InheritDataAwareIterator; +use Symfony\Component\PropertyAccess\PropertyPathIterator; +use Symfony\Component\PropertyAccess\PropertyPathBuilder; +use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface; +use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationPathIterator; +use Symfony\Component\Form\FormError; +use Symfony\Component\Validator\ConstraintViolation; + +/** + * @author Bernhard Schussek + */ +class ViolationMapper implements ViolationMapperInterface +{ + /** + * @var Boolean + */ + private $allowNonSynchronized; + + /** + * {@inheritdoc} + */ + public function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false) + { + $this->allowNonSynchronized = $allowNonSynchronized; + + // The scope is the currently found most specific form that + // an error should be mapped to. After setting the scope, the + // mapper will try to continue to find more specific matches in + // the children of scope. If it cannot, the error will be + // mapped to this scope. + $scope = null; + + $violationPath = null; + $relativePath = null; + $match = false; + + // Don't create a ViolationPath instance for empty property paths + if (strlen($violation->getPropertyPath()) > 0) { + $violationPath = new ViolationPath($violation->getPropertyPath()); + $relativePath = $this->reconstructPath($violationPath, $form); + } + + // This case happens if the violation path is empty and thus + // the violation should be mapped to the root form + if (null === $violationPath) { + $scope = $form; + } + + // In general, mapping happens from the root form to the leaf forms + // First, the rules of the root form are applied to determine + // the subsequent descendant. The rules of this descendant are then + // applied to find the next and so on, until we have found the + // most specific form that matches the violation. + + // If any of the forms found in this process is not synchronized, + // mapping is aborted. Non-synchronized forms could not reverse + // transform the value entered by the user, thus any further violations + // caused by the (invalid) reverse transformed value should be + // ignored. + + if (null !== $relativePath) { + // Set the scope to the root of the relative path + // This root will usually be $form. If the path contains + // an unmapped form though, the last unmapped form found + // will be the root of the path. + $scope = $relativePath->getRoot(); + $it = new PropertyPathIterator($relativePath); + + while ($this->acceptsErrors($scope) && null !== ($child = $this->matchChild($scope, $it))) { + $scope = $child; + $it->next(); + $match = true; + } + } + + // This case happens if an error happened in the data under a + // form inheriting its parent data that does not match any of the + // children of that form. + if (null !== $violationPath && !$match) { + // If we could not map the error to anything more specific + // than the root element, map it to the innermost directly + // mapped form of the violation path + // e.g. "children[foo].children[bar].data.baz" + // Here the innermost directly mapped child is "bar" + + $scope = $form; + $it = new ViolationPathIterator($violationPath); + + // Note: acceptsErrors() will always return true for forms inheriting + // their parent data, because these forms can never be non-synchronized + // (they don't do any data transformation on their own) + while ($this->acceptsErrors($scope) && $it->valid() && $it->mapsForm()) { + if (!$scope->has($it->current())) { + // Break if we find a reference to a non-existing child + break; + } + + $scope = $scope->get($it->current()); + $it->next(); + } + } + + // Follow dot rules until we have the final target + $mapping = $scope->getConfig()->getOption('error_mapping'); + + while ($this->acceptsErrors($scope) && isset($mapping['.'])) { + $dotRule = new MappingRule($scope, '.', $mapping['.']); + $scope = $dotRule->getTarget(); + $mapping = $scope->getConfig()->getOption('error_mapping'); + } + + // Only add the error if the form is synchronized + if ($this->acceptsErrors($scope)) { + $scope->addError(new FormError( + $violation->getMessage(), + $violation->getMessageTemplate(), + $violation->getMessageParameters(), + $violation->getMessagePluralization() + )); + } + } + + /** + * Tries to match the beginning of the property path at the + * current position against the children of the scope. + * + * If a matching child is found, it is returned. Otherwise + * null is returned. + * + * @param FormInterface $form The form to search. + * @param PropertyPathIteratorInterface $it The iterator at its current position. + * + * @return null|FormInterface The found match or null. + */ + private function matchChild(FormInterface $form, PropertyPathIteratorInterface $it) + { + // Remember at what property path underneath "data" + // we are looking. Check if there is a child with that + // path, otherwise increase path by one more piece + $chunk = ''; + $foundChild = null; + $foundAtIndex = 0; + + // Construct mapping rules for the given form + $rules = array(); + + foreach ($form->getConfig()->getOption('error_mapping') as $propertyPath => $targetPath) { + // Dot rules are considered at the very end + if ('.' !== $propertyPath) { + $rules[] = new MappingRule($form, $propertyPath, $targetPath); + } + } + + // Skip forms inheriting their parent data when iterating the children + $childIterator = new \RecursiveIteratorIterator( + new InheritDataAwareIterator($form->all()) + ); + + // Make the path longer until we find a matching child + while (true) { + if (!$it->valid()) { + return null; + } + + if ($it->isIndex()) { + $chunk .= '['.$it->current().']'; + } else { + $chunk .= ('' === $chunk ? '' : '.').$it->current(); + } + + // Test mapping rules as long as we have any + foreach ($rules as $key => $rule) { + /* @var MappingRule $rule */ + + // Mapping rule matches completely, terminate. + if (null !== ($form = $rule->match($chunk))) { + return $form; + } + + // Keep only rules that have $chunk as prefix + if (!$rule->isPrefix($chunk)) { + unset($rules[$key]); + } + } + + // Test children unless we already found one + if (null === $foundChild) { + foreach ($childIterator as $child) { + /* @var FormInterface $child */ + $childPath = (string) $child->getPropertyPath(); + + // Child found, mark as return value + if ($chunk === $childPath) { + $foundChild = $child; + $foundAtIndex = $it->key(); + } + } + } + + // Add element to the chunk + $it->next(); + + // If we reached the end of the path or if there are no + // more matching mapping rules, return the found child + if (null !== $foundChild && (!$it->valid() || count($rules) === 0)) { + // Reset index in case we tried to find mapping + // rules further down the path + $it->seek($foundAtIndex); + + return $foundChild; + } + } + + return null; + } + + /** + * Reconstructs a property path from a violation path and a form tree. + * + * @param ViolationPath $violationPath The violation path. + * @param FormInterface $origin The root form of the tree. + * + * @return RelativePath The reconstructed path. + */ + private function reconstructPath(ViolationPath $violationPath, FormInterface $origin) + { + $propertyPathBuilder = new PropertyPathBuilder($violationPath); + $it = $violationPath->getIterator(); + $scope = $origin; + + // Remember the current index in the builder + $i = 0; + + // Expand elements that map to a form (like "children[address]") + for ($it->rewind(); $it->valid() && $it->mapsForm(); $it->next()) { + if (!$scope->has($it->current())) { + // Scope relates to a form that does not exist + // Bail out + break; + } + + // Process child form + $scope = $scope->get($it->current()); + + if ($scope->getConfig()->getInheritData()) { + // Form inherits its parent data + // Cut the piece out of the property path and proceed + $propertyPathBuilder->remove($i); + } elseif (!$scope->getConfig()->getMapped()) { + // Form is not mapped + // Set the form as new origin and strip everything + // we have so far in the path + $origin = $scope; + $propertyPathBuilder->remove(0, $i + 1); + $i = 0; + } else { + /* @var \Symfony\Component\PropertyAccess\PropertyPathInterface $propertyPath */ + $propertyPath = $scope->getPropertyPath(); + + if (null === $propertyPath) { + // Property path of a mapped form is null + // Should not happen, bail out + break; + } + + $propertyPathBuilder->replace($i, 1, $propertyPath); + $i += $propertyPath->getLength(); + } + } + + $finalPath = $propertyPathBuilder->getPropertyPath(); + + return null !== $finalPath ? new RelativePath($origin, $finalPath) : null; + } + + /** + * @param FormInterface $form + * + * @return Boolean + */ + private function acceptsErrors(FormInterface $form) + { + return $this->allowNonSynchronized || $form->isSynchronized(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php new file mode 100644 index 0000000..eb8907f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.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\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Validator\ConstraintViolation; + +/** + * @author Bernhard Schussek + */ +interface ViolationMapperInterface +{ + /** + * Maps a constraint violation to a form in the form tree under + * the given form. + * + * @param ConstraintViolation $violation The violation to map. + * @param FormInterface $form The root form of the tree + * to map it to. + * @param Boolean $allowNonSynchronized Whether to allow + * mapping to non-synchronized forms. + */ + public function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php new file mode 100644 index 0000000..06d0919 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.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\Form\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\Exception\OutOfBoundsException; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\PropertyAccess\PropertyPathInterface; + +/** + * @author Bernhard Schussek + */ +class ViolationPath implements \IteratorAggregate, PropertyPathInterface +{ + /** + * @var array + */ + private $elements = array(); + + /** + * @var array + */ + private $isIndex = array(); + + /** + * @var array + */ + private $mapsForm = array(); + + /** + * @var string + */ + private $pathAsString = ''; + + /** + * @var integer + */ + private $length = 0; + + /** + * Creates a new violation path from a string. + * + * @param string $violationPath The property path of a {@link ConstraintViolation} + * object. + */ + public function __construct($violationPath) + { + $path = new PropertyPath($violationPath); + $elements = $path->getElements(); + $data = false; + + for ($i = 0, $l = count($elements); $i < $l; ++$i) { + if (!$data) { + // The element "data" has not yet been passed + if ('children' === $elements[$i] && $path->isProperty($i)) { + // Skip element "children" + ++$i; + + // Next element must exist and must be an index + // Otherwise consider this the end of the path + if ($i >= $l || !$path->isIndex($i)) { + break; + } + + $this->elements[] = $elements[$i]; + $this->isIndex[] = true; + $this->mapsForm[] = true; + } elseif ('data' === $elements[$i] && $path->isProperty($i)) { + // Skip element "data" + ++$i; + + // End of path + if ($i >= $l) { + break; + } + + $this->elements[] = $elements[$i]; + $this->isIndex[] = $path->isIndex($i); + $this->mapsForm[] = false; + $data = true; + } else { + // Neither "children" nor "data" property found + // Consider this the end of the path + break; + } + } else { + // Already after the "data" element + // Pick everything as is + $this->elements[] = $elements[$i]; + $this->isIndex[] = $path->isIndex($i); + $this->mapsForm[] = false; + } + } + + $this->length = count($this->elements); + + $this->buildString(); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->pathAsString; + } + + /** + * {@inheritdoc} + */ + public function getLength() + { + return $this->length; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + if ($this->length <= 1) { + return null; + } + + $parent = clone $this; + + --$parent->length; + array_pop($parent->elements); + array_pop($parent->isIndex); + array_pop($parent->mapsForm); + + $parent->buildString(); + + return $parent; + } + + /** + * {@inheritdoc} + */ + public function getElements() + { + return $this->elements; + } + + /** + * {@inheritdoc} + */ + public function getElement($index) + { + if (!isset($this->elements[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index)); + } + + return $this->elements[$index]; + } + + /** + * {@inheritdoc} + */ + public function isProperty($index) + { + if (!isset($this->isIndex[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index)); + } + + return !$this->isIndex[$index]; + } + + /** + * {@inheritdoc} + */ + public function isIndex($index) + { + if (!isset($this->isIndex[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index)); + } + + return $this->isIndex[$index]; + } + + /** + * Returns whether an element maps directly to a form. + * + * Consider the following violation path: + * + * + * children[address].children[office].data.street + * + * + * In this example, "address" and "office" map to forms, while + * "street does not. + * + * @param integer $index The element index. + * + * @return Boolean Whether the element maps to a form. + * + * @throws OutOfBoundsException If the offset is invalid. + */ + public function mapsForm($index) + { + if (!isset($this->mapsForm[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index)); + } + + return $this->mapsForm[$index]; + } + + /** + * Returns a new iterator for this path + * + * @return ViolationPathIterator + */ + public function getIterator() + { + return new ViolationPathIterator($this); + } + + /** + * Builds the string representation from the elements. + */ + private function buildString() + { + $this->pathAsString = ''; + $data = false; + + foreach ($this->elements as $index => $element) { + if ($this->mapsForm[$index]) { + $this->pathAsString .= ".children[$element]"; + } elseif (!$data) { + $this->pathAsString .= '.data'.($this->isIndex[$index] ? "[$element]" : ".$element"); + $data = true; + } else { + $this->pathAsString .= $this->isIndex[$index] ? "[$element]" : ".$element"; + } + } + + if ('' !== $this->pathAsString) { + // remove leading dot + $this->pathAsString = substr($this->pathAsString, 1); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php new file mode 100644 index 0000000..50baa45 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.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\Extension\Validator\ViolationMapper; + +use Symfony\Component\PropertyAccess\PropertyPathIterator; + +/** + * @author Bernhard Schussek + */ +class ViolationPathIterator extends PropertyPathIterator +{ + public function __construct(ViolationPath $violationPath) + { + parent::__construct($violationPath); + } + + public function mapsForm() + { + return $this->path->mapsForm($this->key()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Form.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Form.php new file mode 100644 index 0000000..3513527 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Form.php @@ -0,0 +1,1046 @@ + + * + * 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\RuntimeException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\Exception\AlreadySubmittedException; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Exception\OutOfBoundsException; +use Symfony\Component\Form\Util\FormUtil; +use Symfony\Component\Form\Util\InheritDataAwareIterator; +use Symfony\Component\PropertyAccess\PropertyPath; + +/** + * Form represents a form. + * + * To implement your own form fields, you need to have a thorough understanding + * of the data flow within a form. A form stores its data in three different + * representations: + * + * (1) the "model" format required by the form's object + * (2) the "normalized" format for internal processing + * (3) the "view" format used for display + * + * A date field, for example, may store a date as "Y-m-d" string (1) in the + * object. To facilitate processing in the field, this value is normalized + * to a DateTime object (2). In the HTML representation of your form, a + * localized string (3) is presented to and modified by the user. + * + * In most cases, format (1) and format (2) will be the same. For example, + * a checkbox field uses a Boolean value for both internal processing and + * storage in the object. In these cases you simply need to set a value + * transformer to convert between formats (2) and (3). You can do this by + * calling addViewTransformer(). + * + * In some cases though it makes sense to make format (1) configurable. To + * demonstrate this, let's extend our above date field to store the value + * either as "Y-m-d" string or as timestamp. Internally we still want to + * use a DateTime object for processing. To convert the data from string/integer + * to DateTime you can set a normalization transformer by calling + * addNormTransformer(). The normalized data is then converted to the displayed + * data as described before. + * + * The conversions (1) -> (2) -> (3) use the transform methods of the transformers. + * The conversions (3) -> (2) -> (1) use the reverseTransform methods of the transformers. + * + * @author Fabien Potencier + * @author Bernhard Schussek + */ +class Form implements \IteratorAggregate, FormInterface +{ + /** + * The form's configuration + * @var FormConfigInterface + */ + private $config; + + /** + * The parent of this form + * @var FormInterface + */ + private $parent; + + /** + * The children of this form + * @var FormInterface[] An array of FormInterface instances + */ + private $children = array(); + + /** + * The errors of this form + * @var FormError[] An array of FormError instances + */ + private $errors = array(); + + /** + * Whether this form was submitted + * @var Boolean + */ + private $submitted = false; + + /** + * The form data in model format + * @var mixed + */ + private $modelData; + + /** + * The form data in normalized format + * @var mixed + */ + private $normData; + + /** + * The form data in view format + * @var mixed + */ + private $viewData; + + /** + * The submitted values that don't belong to any children + * @var array + */ + private $extraData = array(); + + /** + * Whether the data in model, normalized and view format is + * synchronized. Data may not be synchronized if transformation errors + * occur. + * @var Boolean + */ + private $synchronized = true; + + /** + * Whether the form's data has been initialized. + * + * When the data is initialized with its default value, that default value + * is passed through the transformer chain in order to synchronize the + * model, normalized and view format for the first time. This is done + * lazily in order to save performance when {@link setData()} is called + * manually, making the initialization with the configured default value + * superfluous. + * + * @var Boolean + */ + private $defaultDataSet = false; + + /** + * Whether setData() is currently being called. + * @var Boolean + */ + private $lockSetData = false; + + /** + * Creates a new form based on the given configuration. + * + * @param FormConfigInterface $config The form configuration. + * + * @throws LogicException if a data mapper is not provided for a compound form + */ + public function __construct(FormConfigInterface $config) + { + // Compound forms always need a data mapper, otherwise calls to + // `setData` and `add` will not lead to the correct population of + // the child forms. + if ($config->getCompound() && !$config->getDataMapper()) { + throw new LogicException('Compound forms need a data mapper'); + } + + // If the form inherits the data from its parent, it is not necessary + // to call setData() with the default data. + if ($config->getInheritData()) { + $this->defaultDataSet = true; + } + + $this->config = $config; + } + + public function __clone() + { + foreach ($this->children as $key => $child) { + $this->children[$key] = clone $child; + } + } + + /** + * {@inheritdoc} + */ + public function getConfig() + { + return $this->config; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->config->getName(); + } + + /** + * {@inheritdoc} + */ + public function getPropertyPath() + { + if (null !== $this->config->getPropertyPath()) { + return $this->config->getPropertyPath(); + } + + if (null === $this->getName() || '' === $this->getName()) { + return null; + } + + $parent = $this->parent; + + while ($parent && $parent->getConfig()->getInheritData()) { + $parent = $parent->getParent(); + } + + if ($parent && null === $parent->getConfig()->getDataClass()) { + return new PropertyPath('['.$this->getName().']'); + } + + return new PropertyPath($this->getName()); + } + + /** + * {@inheritdoc} + */ + public function isRequired() + { + if (null === $this->parent || $this->parent->isRequired()) { + return $this->config->getRequired(); + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function isDisabled() + { + if (null === $this->parent || !$this->parent->isDisabled()) { + return $this->config->getDisabled(); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function setParent(FormInterface $parent = null) + { + if ($this->submitted) { + throw new AlreadySubmittedException('You cannot set the parent of a submitted form'); + } + + if (null !== $parent && '' === $this->config->getName()) { + throw new LogicException('A form with an empty name cannot have a parent form.'); + } + + $this->parent = $parent; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return $this->parent; + } + + /** + * {@inheritdoc} + */ + public function getRoot() + { + return $this->parent ? $this->parent->getRoot() : $this; + } + + /** + * {@inheritdoc} + */ + public function isRoot() + { + return null === $this->parent; + } + + /** + * {@inheritdoc} + */ + public function setData($modelData) + { + // If the form is submitted while disabled, it is set to submitted, but the data is not + // changed. In such cases (i.e. when the form is not initialized yet) don't + // abort this method. + if ($this->submitted && $this->defaultDataSet) { + throw new AlreadySubmittedException('You cannot change the data of a submitted form.'); + } + + // If the form inherits its parent's data, disallow data setting to + // prevent merge conflicts + if ($this->config->getInheritData()) { + throw new RuntimeException('You cannot change the data of a form inheriting its parent data.'); + } + + // Don't allow modifications of the configured data if the data is locked + if ($this->config->getDataLocked() && $modelData !== $this->config->getData()) { + return $this; + } + + if (is_object($modelData) && !$this->config->getByReference()) { + $modelData = clone $modelData; + } + + if ($this->lockSetData) { + throw new RuntimeException('A cycle was detected. Listeners to the PRE_SET_DATA event must not call setData(). You should call setData() on the FormEvent object instead.'); + } + + $this->lockSetData = true; + $dispatcher = $this->config->getEventDispatcher(); + + // Hook to change content of the data + if ($dispatcher->hasListeners(FormEvents::PRE_SET_DATA)) { + $event = new FormEvent($this, $modelData); + $dispatcher->dispatch(FormEvents::PRE_SET_DATA, $event); + $modelData = $event->getData(); + } + + // Treat data as strings unless a value transformer exists + if (!$this->config->getViewTransformers() && !$this->config->getModelTransformers() && is_scalar($modelData)) { + $modelData = (string) $modelData; + } + + // Synchronize representations - must not change the content! + $normData = $this->modelToNorm($modelData); + $viewData = $this->normToView($normData); + + // Validate if view data matches data class (unless empty) + if (!FormUtil::isEmpty($viewData)) { + $dataClass = $this->config->getDataClass(); + + $actualType = is_object($viewData) ? 'an instance of class '.get_class($viewData) : ' a(n) '.gettype($viewData); + + if (null === $dataClass && is_object($viewData) && !$viewData instanceof \ArrayAccess) { + $expectedType = 'scalar, array or an instance of \ArrayAccess'; + + throw new LogicException( + 'The form\'s view data is expected to be of type '.$expectedType.', ' . + 'but is '.$actualType.'. You ' . + 'can avoid this error by setting the "data_class" option to ' . + '"'.get_class($viewData).'" or by adding a view transformer ' . + 'that transforms '.$actualType.' to '.$expectedType.'.' + ); + } + + if (null !== $dataClass && !$viewData instanceof $dataClass) { + throw new LogicException( + 'The form\'s view data is expected to be an instance of class ' . + $dataClass.', but is '. $actualType.'. You can avoid this error ' . + 'by setting the "data_class" option to null or by adding a view ' . + 'transformer that transforms '.$actualType.' to an instance of ' . + $dataClass.'.' + ); + } + } + + $this->modelData = $modelData; + $this->normData = $normData; + $this->viewData = $viewData; + $this->defaultDataSet = true; + $this->lockSetData = false; + + // It is not necessary to invoke this method if the form doesn't have children, + // even if the form is compound. + if (count($this->children) > 0) { + // Update child forms from the data + $childrenIterator = new InheritDataAwareIterator($this->children); + $childrenIterator = new \RecursiveIteratorIterator($childrenIterator); + $this->config->getDataMapper()->mapDataToForms($viewData, $childrenIterator); + } + + if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) { + $event = new FormEvent($this, $modelData); + $dispatcher->dispatch(FormEvents::POST_SET_DATA, $event); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getData() + { + if ($this->config->getInheritData()) { + if (!$this->parent) { + throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.'); + } + + return $this->parent->getData(); + } + + if (!$this->defaultDataSet) { + $this->setData($this->config->getData()); + } + + return $this->modelData; + } + + /** + * {@inheritdoc} + */ + public function getNormData() + { + if ($this->config->getInheritData()) { + if (!$this->parent) { + throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.'); + } + + return $this->parent->getNormData(); + } + + if (!$this->defaultDataSet) { + $this->setData($this->config->getData()); + } + + return $this->normData; + } + + /** + * {@inheritdoc} + */ + public function getViewData() + { + if ($this->config->getInheritData()) { + if (!$this->parent) { + throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.'); + } + + return $this->parent->getViewData(); + } + + if (!$this->defaultDataSet) { + $this->setData($this->config->getData()); + } + + return $this->viewData; + } + + /** + * {@inheritdoc} + */ + public function getExtraData() + { + return $this->extraData; + } + + /** + * {@inheritdoc} + */ + public function initialize() + { + if (null !== $this->parent) { + throw new RuntimeException('Only root forms should be initialized.'); + } + + // Guarantee that the *_SET_DATA events have been triggered once the + // form is initialized. This makes sure that dynamically added or + // removed fields are already visible after initialization. + if (!$this->defaultDataSet) { + $this->setData($this->config->getData()); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function handleRequest($request = null) + { + $this->config->getRequestHandler()->handleRequest($this, $request); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function submit($submittedData, $clearMissing = true) + { + if ($this->submitted) { + throw new AlreadySubmittedException('A form can only be submitted once'); + } + + // Initialize errors in the very beginning so that we don't lose any + // errors added during listeners + $this->errors = array(); + + // Obviously, a disabled form should not change its data upon submission. + if ($this->isDisabled()) { + $this->submitted = true; + + return $this; + } + + // The data must be initialized if it was not initialized yet. + // This is necessary to guarantee that the *_SET_DATA listeners + // are always invoked before submit() takes place. + if (!$this->defaultDataSet) { + $this->setData($this->config->getData()); + } + + // Treat false as NULL to support binding false to checkboxes. + // Don't convert NULL to a string here in order to determine later + // whether an empty value has been submitted or whether no value has + // been submitted at all. This is important for processing checkboxes + // and radio buttons with empty values. + if (false === $submittedData) { + $submittedData = null; + } elseif (is_scalar($submittedData)) { + $submittedData = (string) $submittedData; + } + + $dispatcher = $this->config->getEventDispatcher(); + + // Hook to change content of the data submitted by the browser + if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) { + $event = new FormEvent($this, $submittedData); + $dispatcher->dispatch(FormEvents::PRE_SUBMIT, $event); + $submittedData = $event->getData(); + } + + // Check whether the form is compound. + // This check is preferable over checking the number of children, + // since forms without children may also be compound. + // (think of empty collection forms) + if ($this->config->getCompound()) { + if (!is_array($submittedData)) { + $submittedData = array(); + } + + foreach ($this->children as $name => $child) { + if (isset($submittedData[$name]) || $clearMissing) { + $child->submit(isset($submittedData[$name]) ? $submittedData[$name] : null, $clearMissing); + unset($submittedData[$name]); + } + } + + $this->extraData = $submittedData; + } + + // Forms that inherit their parents' data also are not processed, + // because then it would be too difficult to merge the changes in + // the child and the parent form. Instead, the parent form also takes + // changes in the grandchildren (i.e. children of the form that inherits + // its parent's data) into account. + // (see InheritDataAwareIterator below) + if ($this->config->getInheritData()) { + $this->submitted = true; + + // When POST_SUBMIT is reached, the data is not yet updated, so pass + // NULL to prevent hard-to-debug bugs. + $dataForPostSubmit = null; + } else { + // If the form is compound, the default data in view format + // is reused. The data of the children is merged into this + // default data using the data mapper. + // If the form is not compound, the submitted data is also the data in view format. + $viewData = $this->config->getCompound() ? $this->viewData : $submittedData; + + if (FormUtil::isEmpty($viewData)) { + $emptyData = $this->config->getEmptyData(); + + if ($emptyData instanceof \Closure) { + /* @var \Closure $emptyData */ + $emptyData = $emptyData($this, $viewData); + } + + $viewData = $emptyData; + } + + // Merge form data from children into existing view data + // It is not necessary to invoke this method if the form has no children, + // even if it is compound. + if (count($this->children) > 0) { + // Use InheritDataAwareIterator to process children of + // descendants that inherit this form's data. + // These descendants will not be submitted normally (see the check + // for $this->config->getInheritData() above) + $childrenIterator = new InheritDataAwareIterator($this->children); + $childrenIterator = new \RecursiveIteratorIterator($childrenIterator); + $this->config->getDataMapper()->mapFormsToData($childrenIterator, $viewData); + } + + $modelData = null; + $normData = null; + + try { + // Normalize data to unified representation + $normData = $this->viewToNorm($viewData); + + // Hook to change content of the data into the normalized + // representation + if ($dispatcher->hasListeners(FormEvents::SUBMIT)) { + $event = new FormEvent($this, $normData); + $dispatcher->dispatch(FormEvents::SUBMIT, $event); + $normData = $event->getData(); + } + + // Synchronize representations - must not change the content! + $modelData = $this->normToModel($normData); + $viewData = $this->normToView($normData); + } catch (TransformationFailedException $e) { + $this->synchronized = false; + } + + $this->submitted = true; + $this->modelData = $modelData; + $this->normData = $normData; + $this->viewData = $viewData; + + $dataForPostSubmit = $viewData; + } + + if ($dispatcher->hasListeners(FormEvents::POST_SUBMIT)) { + $event = new FormEvent($this, $dataForPostSubmit); + $dispatcher->dispatch(FormEvents::POST_SUBMIT, $event); + } + + return $this; + } + + /** + * Alias of {@link submit()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link submit()} instead. + */ + public function bind($submittedData) + { + return $this->submit($submittedData); + } + + /** + * {@inheritdoc} + */ + public function addError(FormError $error) + { + if ($this->parent && $this->config->getErrorBubbling()) { + $this->parent->addError($error); + } else { + $this->errors[] = $error; + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function isSubmitted() + { + return $this->submitted; + } + + /** + * Alias of {@link isSubmitted()}. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link isSubmitted()} instead. + */ + public function isBound() + { + return $this->submitted; + } + + /** + * {@inheritdoc} + */ + public function isSynchronized() + { + return $this->synchronized; + } + + /** + * {@inheritdoc} + */ + public function isEmpty() + { + foreach ($this->children as $child) { + if (!$child->isEmpty()) { + return false; + } + } + + return FormUtil::isEmpty($this->modelData) || + // arrays, countables + 0 === count($this->modelData) || + // traversables that are not countable + ($this->modelData instanceof \Traversable && 0 === iterator_count($this->modelData)); + } + + /** + * {@inheritdoc} + */ + public function isValid() + { + if (!$this->submitted) { + return false; + } + + if (count($this->errors) > 0) { + return false; + } + + if (!$this->isDisabled()) { + foreach ($this->children as $child) { + if (!$child->isValid()) { + return false; + } + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getErrors() + { + return $this->errors; + } + + /** + * Returns a string representation of all form errors (including children errors). + * + * This method should only be used to help debug a form. + * + * @param integer $level The indentation level (used internally) + * + * @return string A string representation of all errors + */ + public function getErrorsAsString($level = 0) + { + $errors = ''; + foreach ($this->errors as $error) { + $errors .= str_repeat(' ', $level).'ERROR: '.$error->getMessage()."\n"; + } + + foreach ($this->children as $key => $child) { + $errors .= str_repeat(' ', $level).$key.":\n"; + if ($err = $child->getErrorsAsString($level + 4)) { + $errors .= $err; + } else { + $errors .= str_repeat(' ', $level + 4)."No errors\n"; + } + } + + return $errors; + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->children; + } + + /** + * {@inheritdoc} + */ + public function add($child, $type = null, array $options = array()) + { + if ($this->submitted) { + throw new AlreadySubmittedException('You cannot add children to a submitted form'); + } + + if (!$this->config->getCompound()) { + throw new LogicException('You cannot add children to a simple form. Maybe you should set the option "compound" to true?'); + } + + // Obtain the view data + $viewData = null; + + // If setData() is currently being called, there is no need to call + // mapDataToForms() here, as mapDataToForms() is called at the end + // of setData() anyway. Not doing this check leads to an endless + // recursion when initializing the form lazily and an event listener + // (such as ResizeFormListener) adds fields depending on the data: + // + // * setData() is called, the form is not initialized yet + // * add() is called by the listener (setData() is not complete, so + // the form is still not initialized) + // * getViewData() is called + // * setData() is called since the form is not initialized yet + // * ... endless recursion ... + // + // Also skip data mapping if setData() has not been called yet. + // setData() will be called upon form initialization and data mapping + // will take place by then. + if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) { + $viewData = $this->getViewData(); + } + + if (!$child instanceof FormInterface) { + if (!is_string($child) && !is_int($child)) { + throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormInterface'); + } + + if (null !== $type && !is_string($type) && !$type instanceof FormTypeInterface) { + throw new UnexpectedTypeException($type, 'string or Symfony\Component\Form\FormTypeInterface'); + } + + // Never initialize child forms automatically + $options['auto_initialize'] = false; + + if (null === $type) { + $child = $this->config->getFormFactory()->createForProperty($this->config->getDataClass(), $child, null, $options); + } else { + $child = $this->config->getFormFactory()->createNamed($child, $type, null, $options); + } + } elseif ($child->getConfig()->getAutoInitialize()) { + throw new RuntimeException(sprintf( + 'Automatic initialization is only supported on root forms. You '. + 'should set the "auto_initialize" option to false on the field "%s".', + $child->getName() + )); + } + + $this->children[$child->getName()] = $child; + + $child->setParent($this); + + if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) { + $childrenIterator = new InheritDataAwareIterator(array($child)); + $childrenIterator = new \RecursiveIteratorIterator($childrenIterator); + $this->config->getDataMapper()->mapDataToForms($viewData, $childrenIterator); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + if ($this->submitted) { + throw new AlreadySubmittedException('You cannot remove children from a submitted form'); + } + + if (isset($this->children[$name])) { + $this->children[$name]->setParent(null); + + unset($this->children[$name]); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return isset($this->children[$name]); + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + if (isset($this->children[$name])) { + return $this->children[$name]; + } + + throw new OutOfBoundsException(sprintf('Child "%s" does not exist.', $name)); + } + + /** + * Returns whether a child with the given name exists (implements the \ArrayAccess interface). + * + * @param string $name The name of the child + * + * @return Boolean + */ + public function offsetExists($name) + { + return $this->has($name); + } + + /** + * Returns the child with the given name (implements the \ArrayAccess interface). + * + * @param string $name The name of the child + * + * @return FormInterface The child form + * + * @throws \OutOfBoundsException If the named child does not exist. + */ + public function offsetGet($name) + { + return $this->get($name); + } + + /** + * Adds a child to the form (implements the \ArrayAccess interface). + * + * @param string $name Ignored. The name of the child is used. + * @param FormInterface $child The child to be added. + * + * @throws AlreadySubmittedException If the form has already been submitted. + * @throws LogicException When trying to add a child to a non-compound form. + * + * @see self::add() + */ + public function offsetSet($name, $child) + { + $this->add($child); + } + + /** + * Removes the child with the given name from the form (implements the \ArrayAccess interface). + * + * @param string $name The name of the child to remove + * + * @throws AlreadySubmittedException If the form has already been submitted. + */ + public function offsetUnset($name) + { + $this->remove($name); + } + + /** + * Returns the iterator for this group. + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->children); + } + + /** + * Returns the number of form children (implements the \Countable interface). + * + * @return integer The number of embedded form children + */ + public function count() + { + return count($this->children); + } + + /** + * {@inheritdoc} + */ + public function createView(FormView $parent = null) + { + if (null === $parent && $this->parent) { + $parent = $this->parent->createView(); + } + + return $this->config->getType()->createView($this, $parent); + } + + /** + * Normalizes the value if a normalization transformer is set. + * + * @param mixed $value The value to transform + * + * @return mixed + */ + private function modelToNorm($value) + { + foreach ($this->config->getModelTransformers() as $transformer) { + $value = $transformer->transform($value); + } + + return $value; + } + + /** + * Reverse transforms a value if a normalization transformer is set. + * + * @param string $value The value to reverse transform + * + * @return mixed + */ + private function normToModel($value) + { + $transformers = $this->config->getModelTransformers(); + + for ($i = count($transformers) - 1; $i >= 0; --$i) { + $value = $transformers[$i]->reverseTransform($value); + } + + return $value; + } + + /** + * Transforms the value if a value transformer is set. + * + * @param mixed $value The value to transform + * + * @return mixed + */ + private function normToView($value) + { + // Scalar values should be converted to strings to + // facilitate differentiation between empty ("") and zero (0). + // Only do this for simple forms, as the resulting value in + // compound forms is passed to the data mapper and thus should + // not be converted to a string before. + if (!$this->config->getViewTransformers() && !$this->config->getCompound()) { + return null === $value || is_scalar($value) ? (string) $value : $value; + } + + foreach ($this->config->getViewTransformers() as $transformer) { + $value = $transformer->transform($value); + } + + return $value; + } + + /** + * Reverse transforms a value if a value transformer is set. + * + * @param string $value The value to reverse transform + * + * @return mixed + */ + private function viewToNorm($value) + { + $transformers = $this->config->getViewTransformers(); + + if (!$transformers) { + return '' === $value ? null : $value; + } + + for ($i = count($transformers) - 1; $i >= 0; --$i) { + $value = $transformers[$i]->reverseTransform($value); + } + + return $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormBuilder.php new file mode 100644 index 0000000..2caefe4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormBuilder.php @@ -0,0 +1,275 @@ + + * + * 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\BadMethodCallException; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * A builder for creating {@link Form} instances. + * + * @author Bernhard Schussek + */ +class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormBuilderInterface +{ + /** + * The children of the form builder. + * + * @var FormBuilderInterface[] + */ + private $children = array(); + + /** + * The data of children who haven't been converted to form builders yet. + * + * @var array + */ + private $unresolvedChildren = array(); + + /** + * Creates a new form builder. + * + * @param string $name + * @param string $dataClass + * @param EventDispatcherInterface $dispatcher + * @param FormFactoryInterface $factory + * @param array $options + */ + public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, FormFactoryInterface $factory, array $options = array()) + { + parent::__construct($name, $dataClass, $dispatcher, $options); + + $this->setFormFactory($factory); + } + + /** + * {@inheritdoc} + */ + public function add($child, $type = null, array $options = array()) + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + if ($child instanceof self) { + $this->children[$child->getName()] = $child; + + // In case an unresolved child with the same name exists + unset($this->unresolvedChildren[$child->getName()]); + + return $this; + } + + if (!is_string($child) && !is_int($child)) { + throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormBuilder'); + } + + if (null !== $type && !is_string($type) && !$type instanceof FormTypeInterface) { + throw new UnexpectedTypeException($type, 'string or Symfony\Component\Form\FormTypeInterface'); + } + + // Add to "children" to maintain order + $this->children[$child] = null; + $this->unresolvedChildren[$child] = array( + 'type' => $type, + 'options' => $options, + ); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function create($name, $type = null, array $options = array()) + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + if (null === $type && null === $this->getDataClass()) { + $type = 'text'; + } + + if (null !== $type) { + return $this->getFormFactory()->createNamedBuilder($name, $type, null, $options); + } + + return $this->getFormFactory()->createBuilderForProperty($this->getDataClass(), $name, null, $options); + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + if (isset($this->unresolvedChildren[$name])) { + return $this->resolveChild($name); + } + + if (isset($this->children[$name])) { + return $this->children[$name]; + } + + throw new InvalidArgumentException(sprintf('The child with the name "%s" does not exist.', $name)); + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + unset($this->unresolvedChildren[$name]); + + if (array_key_exists($name, $this->children)) { + unset($this->children[$name]); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + if (isset($this->unresolvedChildren[$name])) { + return true; + } + + if (isset($this->children[$name])) { + return true; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function all() + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->resolveChildren(); + + return $this->children; + } + + /** + * {@inheritdoc} + */ + public function count() + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + return count($this->children); + } + + /** + * {@inheritdoc} + */ + public function getFormConfig() + { + $config = parent::getFormConfig(); + + $config->children = array(); + $config->unresolvedChildren = array(); + + return $config; + } + + /** + * {@inheritdoc} + */ + public function getForm() + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->resolveChildren(); + + $form = new Form($this->getFormConfig()); + + foreach ($this->children as $child) { + // Automatic initialization is only supported on root forms + $form->add($child->setAutoInitialize(false)->getForm()); + } + + if ($this->getAutoInitialize()) { + // Automatically initialize the form if it is configured so + $form->initialize(); + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + if ($this->locked) { + throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + return new \ArrayIterator($this->children); + } + + /** + * Converts an unresolved child into a {@link FormBuilder} instance. + * + * @param string $name The name of the unresolved child. + * + * @return FormBuilder The created instance. + */ + private function resolveChild($name) + { + $info = $this->unresolvedChildren[$name]; + $child = $this->create($name, $info['type'], $info['options']); + $this->children[$name] = $child; + unset($this->unresolvedChildren[$name]); + + return $child; + } + + /** + * Converts all unresolved children into {@link FormBuilder} instances. + */ + private function resolveChildren() + { + foreach ($this->unresolvedChildren as $name => $info) { + $this->children[$name] = $this->create($name, $info['type'], $info['options']); + } + + $this->unresolvedChildren = array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormBuilderInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormBuilderInterface.php new file mode 100644 index 0000000..1dc4a64 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormBuilderInterface.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\Form; + +/** + * @author Bernhard Schussek + */ +interface FormBuilderInterface extends \Traversable, \Countable, FormConfigBuilderInterface +{ + /** + * Adds a new field to this group. A field must have a unique name within + * the group. Otherwise the existing field is overwritten. + * + * If you add a nested group, this group should also be represented in the + * object hierarchy. + * + * @param string|integer|FormBuilderInterface $child + * @param string|FormTypeInterface $type + * @param array $options + * + * @return FormBuilderInterface The builder object. + */ + public function add($child, $type = null, array $options = array()); + + /** + * Creates a form builder. + * + * @param string $name The name of the form or the name of the property + * @param string|FormTypeInterface $type The type of the form or null if name is a property + * @param array $options The options + * + * @return FormBuilderInterface The created builder. + */ + public function create($name, $type = null, array $options = array()); + + /** + * Returns a child by name. + * + * @param string $name The name of the child + * + * @return FormBuilderInterface The builder for the child + * + * @throws Exception\InvalidArgumentException if the given child does not exist + */ + public function get($name); + + /** + * Removes the field with the given name. + * + * @param string $name + * + * @return FormBuilderInterface The builder object. + */ + public function remove($name); + + /** + * Returns whether a field with the given name exists. + * + * @param string $name + * + * @return Boolean + */ + public function has($name); + + /** + * Returns the children. + * + * @return array + */ + public function all(); + + /** + * Creates the form. + * + * @return Form The form + */ + public function getForm(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormConfigBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormConfigBuilder.php new file mode 100644 index 0000000..dc9c8f0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormConfigBuilder.php @@ -0,0 +1,920 @@ + + * + * 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\BadMethodCallException; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\PropertyAccess\PropertyPathInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; + +/** + * A basic form configuration. + * + * @author Bernhard Schussek + */ +class FormConfigBuilder implements FormConfigBuilderInterface +{ + /** + * Caches a globally unique {@link NativeRequestHandler} instance. + * + * @var NativeRequestHandler + */ + private static $nativeRequestProcessor; + + /** + * The accepted request methods. + * + * @var array + */ + private static $allowedMethods = array( + 'GET', + 'PUT', + 'POST', + 'DELETE', + 'PATCH' + ); + + /** + * @var Boolean + */ + protected $locked = false; + + /** + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * @var string + */ + private $name; + + /** + * @var PropertyPathInterface + */ + private $propertyPath; + + /** + * @var Boolean + */ + private $mapped = true; + + /** + * @var Boolean + */ + private $byReference = true; + + /** + * @var Boolean + */ + private $inheritData = false; + + /** + * @var Boolean + */ + private $compound = false; + + /** + * @var ResolvedFormTypeInterface + */ + private $type; + + /** + * @var array + */ + private $viewTransformers = array(); + + /** + * @var array + */ + private $modelTransformers = array(); + + /** + * @var DataMapperInterface + */ + private $dataMapper; + + /** + * @var Boolean + */ + private $required = true; + + /** + * @var Boolean + */ + private $disabled = false; + + /** + * @var Boolean + */ + private $errorBubbling = false; + + /** + * @var mixed + */ + private $emptyData; + + /** + * @var array + */ + private $attributes = array(); + + /** + * @var mixed + */ + private $data; + + /** + * @var string + */ + private $dataClass; + + /** + * @var Boolean + */ + private $dataLocked; + + /** + * @var FormFactoryInterface + */ + private $formFactory; + + /** + * @var string + */ + private $action; + + /** + * @var string + */ + private $method = 'POST'; + + /** + * @var RequestHandlerInterface + */ + private $requestHandler; + + /** + * @var Boolean + */ + private $autoInitialize = false; + + /** + * @var array + */ + private $options; + + /** + * Creates an empty form configuration. + * + * @param string|integer $name The form name + * @param string $dataClass The class of the form's data + * @param EventDispatcherInterface $dispatcher The event dispatcher + * @param array $options The form options + * + * @throws InvalidArgumentException If the data class is not a valid class or if + * the name contains invalid characters. + */ + public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, array $options = array()) + { + self::validateName($name); + + if (null !== $dataClass && !class_exists($dataClass)) { + throw new InvalidArgumentException(sprintf('The data class "%s" is not a valid class.', $dataClass)); + } + + $this->name = (string) $name; + $this->dataClass = $dataClass; + $this->dispatcher = $dispatcher; + $this->options = $options; + } + + /** + * {@inheritdoc} + */ + public function addEventListener($eventName, $listener, $priority = 0) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->dispatcher->addListener($eventName, $listener, $priority); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addEventSubscriber(EventSubscriberInterface $subscriber) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->dispatcher->addSubscriber($subscriber); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addViewTransformer(DataTransformerInterface $viewTransformer, $forcePrepend = false) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + if ($forcePrepend) { + array_unshift($this->viewTransformers, $viewTransformer); + } else { + $this->viewTransformers[] = $viewTransformer; + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function resetViewTransformers() + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->viewTransformers = array(); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addModelTransformer(DataTransformerInterface $modelTransformer, $forceAppend = false) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + if ($forceAppend) { + $this->modelTransformers[] = $modelTransformer; + } else { + array_unshift($this->modelTransformers, $modelTransformer); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function resetModelTransformers() + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->modelTransformers = array(); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getEventDispatcher() + { + if ($this->locked && !$this->dispatcher instanceof ImmutableEventDispatcher) { + $this->dispatcher = new ImmutableEventDispatcher($this->dispatcher); + } + + return $this->dispatcher; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getPropertyPath() + { + return $this->propertyPath; + } + + /** + * {@inheritdoc} + */ + public function getMapped() + { + return $this->mapped; + } + + /** + * {@inheritdoc} + */ + public function getByReference() + { + return $this->byReference; + } + + /** + * {@inheritdoc} + */ + public function getInheritData() + { + return $this->inheritData; + } + + /** + * Alias of {@link getInheritData()}. + * + * @return FormConfigBuilder The configuration object. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link getInheritData()} instead. + */ + public function getVirtual() + { + // Uncomment this as soon as the deprecation note should be shown + // trigger_error('getVirtual() is deprecated since version 2.3 and will be removed in 3.0. Use getInheritData() instead.', E_USER_DEPRECATED); + + return $this->getInheritData(); + } + + /** + * {@inheritdoc} + */ + public function getCompound() + { + return $this->compound; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return $this->type; + } + + /** + * {@inheritdoc} + */ + public function getViewTransformers() + { + return $this->viewTransformers; + } + + /** + * {@inheritdoc} + */ + public function getModelTransformers() + { + return $this->modelTransformers; + } + + /** + * {@inheritdoc} + */ + public function getDataMapper() + { + return $this->dataMapper; + } + + /** + * {@inheritdoc} + */ + public function getRequired() + { + return $this->required; + } + + /** + * {@inheritdoc} + */ + public function getDisabled() + { + return $this->disabled; + } + + /** + * {@inheritdoc} + */ + public function getErrorBubbling() + { + return $this->errorBubbling; + } + + /** + * {@inheritdoc} + */ + public function getEmptyData() + { + return $this->emptyData; + } + + /** + * {@inheritdoc} + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * {@inheritdoc} + */ + public function hasAttribute($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * {@inheritdoc} + */ + public function getAttribute($name, $default = null) + { + return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function getData() + { + return $this->data; + } + + /** + * {@inheritdoc} + */ + public function getDataClass() + { + return $this->dataClass; + } + + /** + * {@inheritdoc} + */ + public function getDataLocked() + { + return $this->dataLocked; + } + + /** + * {@inheritdoc} + */ + public function getFormFactory() + { + return $this->formFactory; + } + + /** + * {@inheritdoc} + */ + public function getAction() + { + return $this->action; + } + + /** + * {@inheritdoc} + */ + public function getMethod() + { + return $this->method; + } + + /** + * {@inheritdoc} + */ + public function getRequestHandler() + { + if (null === $this->requestHandler) { + if (null === self::$nativeRequestProcessor) { + self::$nativeRequestProcessor = new NativeRequestHandler(); + } + $this->requestHandler = self::$nativeRequestProcessor; + } + + return $this->requestHandler; + } + + /** + * {@inheritdoc} + */ + public function getAutoInitialize() + { + return $this->autoInitialize; + } + + /** + * {@inheritdoc} + */ + public function getOptions() + { + return $this->options; + } + + /** + * {@inheritdoc} + */ + public function hasOption($name) + { + return array_key_exists($name, $this->options); + } + + /** + * {@inheritdoc} + */ + public function getOption($name, $default = null) + { + return array_key_exists($name, $this->options) ? $this->options[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function setAttribute($name, $value) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->attributes[$name] = $value; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setAttributes(array $attributes) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->attributes = $attributes; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDataMapper(DataMapperInterface $dataMapper = null) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->dataMapper = $dataMapper; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDisabled($disabled) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->disabled = (Boolean) $disabled; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setEmptyData($emptyData) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->emptyData = $emptyData; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setErrorBubbling($errorBubbling) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->errorBubbling = null === $errorBubbling ? null : (Boolean) $errorBubbling; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setRequired($required) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->required = (Boolean) $required; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setPropertyPath($propertyPath) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + if (null !== $propertyPath && !$propertyPath instanceof PropertyPathInterface) { + $propertyPath = new PropertyPath($propertyPath); + } + + $this->propertyPath = $propertyPath; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setMapped($mapped) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->mapped = $mapped; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setByReference($byReference) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->byReference = $byReference; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setInheritData($inheritData) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->inheritData = $inheritData; + + return $this; + } + + /** + * Alias of {@link setInheritData()}. + * + * @param Boolean $inheritData Whether the form should inherit its parent's data. + * + * @return FormConfigBuilder The configuration object. + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link setInheritData()} instead. + */ + public function setVirtual($inheritData) + { + // Uncomment this as soon as the deprecation note should be shown + // trigger_error('setVirtual() is deprecated since version 2.3 and will be removed in 3.0. Use setInheritData() instead.', E_USER_DEPRECATED); + + $this->setInheritData($inheritData); + } + + /** + * {@inheritdoc} + */ + public function setCompound($compound) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->compound = $compound; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setType(ResolvedFormTypeInterface $type) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->type = $type; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setData($data) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->data = $data; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDataLocked($locked) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->dataLocked = $locked; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setFormFactory(FormFactoryInterface $formFactory) + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + $this->formFactory = $formFactory; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setAction($action) + { + if ($this->locked) { + throw new BadMethodCallException('The config builder cannot be modified anymore.'); + } + + $this->action = $action; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setMethod($method) + { + if ($this->locked) { + throw new BadMethodCallException('The config builder cannot be modified anymore.'); + } + + $upperCaseMethod = strtoupper($method); + + if (!in_array($upperCaseMethod, self::$allowedMethods)) { + throw new InvalidArgumentException(sprintf( + 'The form method is "%s", but should be one of "%s".', + $method, + implode('", "', self::$allowedMethods) + )); + } + + $this->method = $upperCaseMethod; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setRequestHandler(RequestHandlerInterface $requestHandler) + { + if ($this->locked) { + throw new BadMethodCallException('The config builder cannot be modified anymore.'); + } + + $this->requestHandler = $requestHandler; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setAutoInitialize($initialize) + { + $this->autoInitialize = (Boolean) $initialize; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFormConfig() + { + if ($this->locked) { + throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); + } + + // This method should be idempotent, so clone the builder + $config = clone $this; + $config->locked = true; + + return $config; + } + + /** + * Validates whether the given variable is a valid form name. + * + * @param string|integer $name The tested form name. + * + * @throws UnexpectedTypeException If the name is not a string or an integer. + * @throws InvalidArgumentException If the name contains invalid characters. + */ + public static function validateName($name) + { + if (null !== $name && !is_string($name) && !is_int($name)) { + throw new UnexpectedTypeException($name, 'string, integer or null'); + } + + if (!self::isValidName($name)) { + throw new InvalidArgumentException(sprintf( + 'The name "%s" contains illegal characters. Names should start with a letter, digit or underscore and only contain letters, digits, numbers, underscores ("_"), hyphens ("-") and colons (":").', + $name + )); + } + } + + /** + * Returns whether the given variable contains a valid form name. + * + * A name is accepted if it + * + * * is empty + * * starts with a letter, digit or underscore + * * contains only letters, digits, numbers, underscores ("_"), + * hyphens ("-") and colons (":") + * + * @param string $name The tested form name. + * + * @return Boolean Whether the name is valid. + */ + public static function isValidName($name) + { + return '' === $name || null === $name || preg_match('/^[a-zA-Z0-9_][a-zA-Z0-9_\-:]*$/D', $name); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormConfigBuilderInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormConfigBuilderInterface.php new file mode 100644 index 0000000..62d12c0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormConfigBuilderInterface.php @@ -0,0 +1,287 @@ + + * + * 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\EventDispatcher\EventSubscriberInterface; + +/** + * @author Bernhard Schussek + */ +interface FormConfigBuilderInterface extends FormConfigInterface +{ + /** + * Adds an event listener to an event on this form. + * + * @param string $eventName The name of the event to listen to. + * @param callable $listener The listener to execute. + * @param integer $priority The priority of the listener. Listeners + * with a higher priority are called before + * listeners with a lower priority. + * + * @return self The configuration object. + */ + public function addEventListener($eventName, $listener, $priority = 0); + + /** + * Adds an event subscriber for events on this form. + * + * @param EventSubscriberInterface $subscriber The subscriber to attach. + * + * @return self The configuration object. + */ + public function addEventSubscriber(EventSubscriberInterface $subscriber); + + /** + * Appends / prepends a transformer to the view transformer chain. + * + * The transform method of the transformer is used to convert data from the + * normalized to the view format. + * The reverseTransform method of the transformer is used to convert from the + * view to the normalized format. + * + * @param DataTransformerInterface $viewTransformer + * @param Boolean $forcePrepend if set to true, prepend instead of appending + * + * @return self The configuration object. + */ + public function addViewTransformer(DataTransformerInterface $viewTransformer, $forcePrepend = false); + + /** + * Clears the view transformers. + * + * @return self The configuration object. + */ + public function resetViewTransformers(); + + /** + * Prepends / appends a transformer to the normalization transformer chain. + * + * The transform method of the transformer is used to convert data from the + * model to the normalized format. + * The reverseTransform method of the transformer is used to convert from the + * normalized to the model format. + * + * @param DataTransformerInterface $modelTransformer + * @param Boolean $forceAppend if set to true, append instead of prepending + * + * @return self The configuration object. + */ + public function addModelTransformer(DataTransformerInterface $modelTransformer, $forceAppend = false); + + /** + * Clears the normalization transformers. + * + * @return self The configuration object. + */ + public function resetModelTransformers(); + + /** + * Sets the value for an attribute. + * + * @param string $name The name of the attribute + * @param string $value The value of the attribute + * + * @return self The configuration object. + */ + public function setAttribute($name, $value); + + /** + * Sets the attributes. + * + * @param array $attributes The attributes. + * + * @return self The configuration object. + */ + public function setAttributes(array $attributes); + + /** + * Sets the data mapper used by the form. + * + * @param DataMapperInterface $dataMapper + * + * @return self The configuration object. + */ + public function setDataMapper(DataMapperInterface $dataMapper = null); + + /** + * Set whether the form is disabled. + * + * @param Boolean $disabled Whether the form is disabled + * + * @return self The configuration object. + */ + public function setDisabled($disabled); + + /** + * Sets the data used for the client data when no value is submitted. + * + * @param mixed $emptyData The empty data. + * + * @return self The configuration object. + */ + public function setEmptyData($emptyData); + + /** + * Sets whether errors bubble up to the parent. + * + * @param Boolean $errorBubbling + * + * @return self The configuration object. + */ + public function setErrorBubbling($errorBubbling); + + /** + * Sets whether this field is required to be filled out when submitted. + * + * @param Boolean $required + * + * @return self The configuration object. + */ + public function setRequired($required); + + /** + * Sets the property path that the form should be mapped to. + * + * @param null|string|\Symfony\Component\PropertyAccess\PropertyPathInterface $propertyPath + * The property path or null if the path should be set + * automatically based on the form's name. + * + * @return self The configuration object. + */ + public function setPropertyPath($propertyPath); + + /** + * Sets whether the form should be mapped to an element of its + * parent's data. + * + * @param Boolean $mapped Whether the form should be mapped. + * + * @return self The configuration object. + */ + public function setMapped($mapped); + + /** + * Sets whether the form's data should be modified by reference. + * + * @param Boolean $byReference Whether the data should be + * modified by reference. + * + * @return self The configuration object. + */ + public function setByReference($byReference); + + /** + * Sets whether the form should read and write the data of its parent. + * + * @param Boolean $inheritData Whether the form should inherit its parent's data. + * + * @return self The configuration object. + */ + public function setInheritData($inheritData); + + /** + * Sets whether the form should be compound. + * + * @param Boolean $compound Whether the form should be compound. + * + * @return self The configuration object. + * + * @see FormConfigInterface::getCompound() + */ + public function setCompound($compound); + + /** + * Set the types. + * + * @param ResolvedFormTypeInterface $type The type of the form. + * + * @return self The configuration object. + */ + public function setType(ResolvedFormTypeInterface $type); + + /** + * Sets the initial data of the form. + * + * @param array $data The data of the form in application format. + * + * @return self The configuration object. + */ + public function setData($data); + + /** + * Locks the form's data to the data passed in the configuration. + * + * A form with locked data is restricted to the data passed in + * this configuration. The data can only be modified then by + * submitting the form. + * + * @param Boolean $locked Whether to lock the default data. + * + * @return self The configuration object. + */ + public function setDataLocked($locked); + + /** + * Sets the form factory used for creating new forms. + * + * @param FormFactoryInterface $formFactory The form factory. + */ + public function setFormFactory(FormFactoryInterface $formFactory); + + /** + * Sets the target URL of the form. + * + * @param string $action The target URL of the form. + * + * @return self The configuration object. + */ + public function setAction($action); + + /** + * Sets the HTTP method used by the form. + * + * @param string $method The HTTP method of the form. + * + * @return self The configuration object. + */ + public function setMethod($method); + + /** + * Sets the request handler used by the form. + * + * @param RequestHandlerInterface $requestHandler + * + * @return self The configuration object. + */ + public function setRequestHandler(RequestHandlerInterface $requestHandler); + + /** + * Sets whether the form should be initialized automatically. + * + * Should be set to true only for root forms. + * + * @param Boolean $initialize True to initialize the form automatically, + * false to suppress automatic initialization. + * In the second case, you need to call + * {@link FormInterface::initialize()} manually. + * + * @return self The configuration object. + */ + public function setAutoInitialize($initialize); + + /** + * Builds and returns the form configuration. + * + * @return FormConfigInterface + */ + public function getFormConfig(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormConfigInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormConfigInterface.php new file mode 100644 index 0000000..576fcd8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormConfigInterface.php @@ -0,0 +1,243 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * The configuration of a {@link Form} object. + * + * @author Bernhard Schussek + */ +interface FormConfigInterface +{ + /** + * Returns the event dispatcher used to dispatch form events. + * + * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface The dispatcher. + */ + public function getEventDispatcher(); + + /** + * Returns the name of the form used as HTTP parameter. + * + * @return string The form name. + */ + public function getName(); + + /** + * Returns the property path that the form should be mapped to. + * + * @return null|\Symfony\Component\PropertyAccess\PropertyPathInterface The property path. + */ + public function getPropertyPath(); + + /** + * Returns whether the form should be mapped to an element of its + * parent's data. + * + * @return Boolean Whether the form is mapped. + */ + public function getMapped(); + + /** + * Returns whether the form's data should be modified by reference. + * + * @return Boolean Whether to modify the form's data by reference. + */ + public function getByReference(); + + /** + * Returns whether the form should read and write the data of its parent. + * + * @return Boolean Whether the form should inherit its parent's data. + */ + public function getInheritData(); + + /** + * Returns whether the form is compound. + * + * This property is independent of whether the form actually has + * children. A form can be compound and have no children at all, like + * for example an empty collection form. + * + * @return Boolean Whether the form is compound. + */ + public function getCompound(); + + /** + * Returns the form types used to construct the form. + * + * @return ResolvedFormTypeInterface The form's type. + */ + public function getType(); + + /** + * Returns the view transformers of the form. + * + * @return DataTransformerInterface[] An array of {@link DataTransformerInterface} instances. + */ + public function getViewTransformers(); + + /** + * Returns the model transformers of the form. + * + * @return DataTransformerInterface[] An array of {@link DataTransformerInterface} instances. + */ + public function getModelTransformers(); + + /** + * Returns the data mapper of the form. + * + * @return DataMapperInterface The data mapper. + */ + public function getDataMapper(); + + /** + * Returns whether the form is required. + * + * @return Boolean Whether the form is required. + */ + public function getRequired(); + + /** + * Returns whether the form is disabled. + * + * @return Boolean Whether the form is disabled. + */ + public function getDisabled(); + + /** + * Returns whether errors attached to the form will bubble to its parent. + * + * @return Boolean Whether errors will bubble up. + */ + public function getErrorBubbling(); + + /** + * Returns the data that should be returned when the form is empty. + * + * @return mixed The data returned if the form is empty. + */ + public function getEmptyData(); + + /** + * Returns additional attributes of the form. + * + * @return array An array of key-value combinations. + */ + public function getAttributes(); + + /** + * Returns whether the attribute with the given name exists. + * + * @param string $name The attribute name. + * + * @return Boolean Whether the attribute exists. + */ + public function hasAttribute($name); + + /** + * Returns the value of the given attribute. + * + * @param string $name The attribute name. + * @param mixed $default The value returned if the attribute does not exist. + * + * @return mixed The attribute value. + */ + public function getAttribute($name, $default = null); + + /** + * Returns the initial data of the form. + * + * @return mixed The initial form data. + */ + public function getData(); + + /** + * Returns the class of the form data or null if the data is scalar or an array. + * + * @return string The data class or null. + */ + public function getDataClass(); + + /** + * Returns whether the form's data is locked. + * + * A form with locked data is restricted to the data passed in + * this configuration. The data can only be modified then by + * submitting the form. + * + * @return Boolean Whether the data is locked. + */ + public function getDataLocked(); + + /** + * Returns the form factory used for creating new forms. + * + * @return FormFactoryInterface The form factory. + */ + public function getFormFactory(); + + /** + * Returns the target URL of the form. + * + * @return string The target URL of the form. + */ + public function getAction(); + + /** + * Returns the HTTP method used by the form. + * + * @return string The HTTP method of the form. + */ + public function getMethod(); + + /** + * Returns the request handler used by the form. + * + * @return RequestHandlerInterface The request handler. + */ + public function getRequestHandler(); + + /** + * Returns whether the form should be initialized upon creation. + * + * @return Boolean Returns true if the form should be initialized + * when created, false otherwise. + */ + public function getAutoInitialize(); + + /** + * Returns all options passed during the construction of the form. + * + * @return array The passed options. + */ + public function getOptions(); + + /** + * Returns whether a specific option exists. + * + * @param string $name The option name, + * + * @return Boolean Whether the option exists. + */ + public function hasOption($name); + + /** + * Returns the value of a specific option. + * + * @param string $name The option name. + * @param mixed $default The value returned if the option does not exist. + * + * @return mixed The option value. + */ + public function getOption($name, $default = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormError.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormError.php new file mode 100644 index 0000000..343165c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormError.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\Form; + +/** + * Wraps errors in forms + * + * @author Bernhard Schussek + */ +class FormError +{ + /** + * @var string + */ + private $message; + + /** + * The template for the error message + * @var string + */ + protected $messageTemplate; + + /** + * The parameters that should be substituted in the message template + * @var array + */ + protected $messageParameters; + + /** + * The value for error message pluralization + * @var integer|null + */ + protected $messagePluralization; + + /** + * Constructor + * + * Any array key in $messageParameters will be used as a placeholder in + * $messageTemplate. + * + * @param string $message The translated error message + * @param string|null $messageTemplate The template for the error message + * @param array $messageParameters The parameters that should be + * substituted in the message template. + * @param integer|null $messagePluralization The value for error message pluralization + * + * @see \Symfony\Component\Translation\Translator + */ + public function __construct($message, $messageTemplate = null, array $messageParameters = array(), $messagePluralization = null) + { + $this->message = $message; + $this->messageTemplate = $messageTemplate ?: $message; + $this->messageParameters = $messageParameters; + $this->messagePluralization = $messagePluralization; + } + + /** + * Returns the error message + * + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Returns the error message template + * + * @return string + */ + public function getMessageTemplate() + { + return $this->messageTemplate; + } + + /** + * Returns the parameters to be inserted in the message template + * + * @return array + */ + public function getMessageParameters() + { + return $this->messageParameters; + } + + /** + * Returns the value for error message pluralization. + * + * @return integer|null + */ + public function getMessagePluralization() + { + return $this->messagePluralization; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormEvent.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormEvent.php new file mode 100644 index 0000000..57cebad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormEvent.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; + +use Symfony\Component\EventDispatcher\Event; + +/** + * @author Bernhard Schussek + */ +class FormEvent extends Event +{ + private $form; + protected $data; + + /** + * Constructs an event. + * + * @param FormInterface $form The associated form + * @param mixed $data The data + */ + public function __construct(FormInterface $form, $data) + { + $this->form = $form; + $this->data = $data; + } + + /** + * Returns the form at the source of the event. + * + * @return FormInterface + */ + public function getForm() + { + return $this->form; + } + + /** + * Returns the data associated with this event. + * + * @return mixed + */ + public function getData() + { + return $this->data; + } + + /** + * Allows updating with some filtered data. + * + * @param mixed $data + */ + public function setData($data) + { + $this->data = $data; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormEvents.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormEvents.php new file mode 100644 index 0000000..6c4efc5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormEvents.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\Form; + +/** + * @author Bernhard Schussek + */ +final class FormEvents +{ + const PRE_SUBMIT = 'form.pre_bind'; + + const SUBMIT = 'form.bind'; + + const POST_SUBMIT = 'form.post_bind'; + + const PRE_SET_DATA = 'form.pre_set_data'; + + const POST_SET_DATA = 'form.post_set_data'; + + /** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link PRE_SUBMIT} instead. + */ + const PRE_BIND = 'form.pre_bind'; + + /** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link SUBMIT} instead. + */ + const BIND = 'form.bind'; + + /** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link POST_SUBMIT} instead. + */ + const POST_BIND = 'form.post_bind'; + + private function __construct() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormExtensionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormExtensionInterface.php new file mode 100644 index 0000000..a67055b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormExtensionInterface.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\Form; + +/** + * Interface for extensions which provide types, type extensions and a guesser. + */ +interface FormExtensionInterface +{ + /** + * Returns a type by name. + * + * @param string $name The name of the type + * + * @return FormTypeInterface The type + * + * @throws Exception\InvalidArgumentException if the given type is not supported by this extension + */ + public function getType($name); + + /** + * Returns whether the given type is supported. + * + * @param string $name The name of the type + * + * @return Boolean Whether the type is supported by this extension + */ + public function hasType($name); + + /** + * Returns the extensions for the given type. + * + * @param string $name The name of the type + * + * @return FormTypeExtensionInterface[] An array of extensions as FormTypeExtensionInterface instances + */ + public function getTypeExtensions($name); + + /** + * Returns whether this extension provides type extensions for the given type. + * + * @param string $name The name of the type + * + * @return Boolean Whether the given type has extensions + */ + public function hasTypeExtensions($name); + + /** + * Returns the type guesser provided by this extension. + * + * @return FormTypeGuesserInterface|null The type guesser + */ + public function getTypeGuesser(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactory.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactory.php new file mode 100644 index 0000000..a5fd9cc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactory.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\Form; + +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +class FormFactory implements FormFactoryInterface +{ + /** + * @var FormRegistryInterface + */ + private $registry; + + /** + * @var ResolvedFormTypeFactoryInterface + */ + private $resolvedTypeFactory; + + public function __construct(FormRegistryInterface $registry, ResolvedFormTypeFactoryInterface $resolvedTypeFactory) + { + $this->registry = $registry; + $this->resolvedTypeFactory = $resolvedTypeFactory; + } + + /** + * {@inheritdoc} + */ + public function create($type = 'form', $data = null, array $options = array()) + { + return $this->createBuilder($type, $data, $options)->getForm(); + } + + /** + * {@inheritdoc} + */ + public function createNamed($name, $type = 'form', $data = null, array $options = array()) + { + return $this->createNamedBuilder($name, $type, $data, $options)->getForm(); + } + + /** + * {@inheritdoc} + */ + public function createForProperty($class, $property, $data = null, array $options = array()) + { + return $this->createBuilderForProperty($class, $property, $data, $options)->getForm(); + } + + /** + * {@inheritdoc} + */ + public function createBuilder($type = 'form', $data = null, array $options = array()) + { + $name = $type instanceof FormTypeInterface || $type instanceof ResolvedFormTypeInterface + ? $type->getName() + : $type; + + return $this->createNamedBuilder($name, $type, $data, $options); + } + + /** + * {@inheritdoc} + */ + public function createNamedBuilder($name, $type = 'form', $data = null, array $options = array()) + { + if (null !== $data && !array_key_exists('data', $options)) { + $options['data'] = $data; + } + + if ($type instanceof FormTypeInterface) { + $type = $this->resolveType($type); + } elseif (is_string($type)) { + $type = $this->registry->getType($type); + } elseif (!$type instanceof ResolvedFormTypeInterface) { + throw new UnexpectedTypeException($type, 'string, Symfony\Component\Form\ResolvedFormTypeInterface or Symfony\Component\Form\FormTypeInterface'); + } + + return $type->createBuilder($this, $name, $options); + } + + /** + * {@inheritdoc} + */ + public function createBuilderForProperty($class, $property, $data = null, array $options = array()) + { + if (null === $guesser = $this->registry->getTypeGuesser()) { + return $this->createNamedBuilder($property, 'text', $data, $options); + } + + $typeGuess = $guesser->guessType($class, $property); + $maxLengthGuess = $guesser->guessMaxLength($class, $property); + $requiredGuess = $guesser->guessRequired($class, $property); + $patternGuess = $guesser->guessPattern($class, $property); + + $type = $typeGuess ? $typeGuess->getType() : 'text'; + + $maxLength = $maxLengthGuess ? $maxLengthGuess->getValue() : null; + $pattern = $patternGuess ? $patternGuess->getValue() : null; + + if (null !== $pattern) { + $options = array_merge(array('pattern' => $pattern), $options); + } + + if (null !== $maxLength) { + $options = array_merge(array('max_length' => $maxLength), $options); + } + + if ($requiredGuess) { + $options = array_merge(array('required' => $requiredGuess->getValue()), $options); + } + + // user options may override guessed options + if ($typeGuess) { + $options = array_merge($typeGuess->getOptions(), $options); + } + + return $this->createNamedBuilder($property, $type, $data, $options); + } + + /** + * Wraps a type into a ResolvedFormTypeInterface implementation and connects + * it with its parent type. + * + * @param FormTypeInterface $type The type to resolve. + * + * @return ResolvedFormTypeInterface The resolved type. + */ + private function resolveType(FormTypeInterface $type) + { + $parentType = $type->getParent(); + + if ($parentType instanceof FormTypeInterface) { + $parentType = $this->resolveType($parentType); + } elseif (null !== $parentType) { + $parentType = $this->registry->getType($parentType); + } + + return $this->resolvedTypeFactory->createResolvedType( + $type, + // Type extensions are not supported for unregistered type instances, + // i.e. type instances that are passed to the FormFactory directly, + // nor for their parents, if getParent() also returns a type instance. + array(), + $parentType + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactoryBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactoryBuilder.php new file mode 100644 index 0000000..10383e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactoryBuilder.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\Form; + +/** + * The default implementation of FormFactoryBuilderInterface. + * + * @author Bernhard Schussek + */ +class FormFactoryBuilder implements FormFactoryBuilderInterface +{ + /** + * @var ResolvedFormTypeFactoryInterface + */ + private $resolvedTypeFactory; + + /** + * @var array + */ + private $extensions = array(); + + /** + * @var array + */ + private $types = array(); + + /** + * @var array + */ + private $typeExtensions = array(); + + /** + * @var array + */ + private $typeGuessers = array(); + + /** + * {@inheritdoc} + */ + public function setResolvedTypeFactory(ResolvedFormTypeFactoryInterface $resolvedTypeFactory) + { + $this->resolvedTypeFactory = $resolvedTypeFactory; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addExtension(FormExtensionInterface $extension) + { + $this->extensions[] = $extension; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addExtensions(array $extensions) + { + $this->extensions = array_merge($this->extensions, $extensions); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addType(FormTypeInterface $type) + { + $this->types[$type->getName()] = $type; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addTypes(array $types) + { + foreach ($types as $type) { + $this->types[$type->getName()] = $type; + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addTypeExtension(FormTypeExtensionInterface $typeExtension) + { + $this->typeExtensions[$typeExtension->getExtendedType()][] = $typeExtension; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addTypeExtensions(array $typeExtensions) + { + foreach ($typeExtensions as $typeExtension) { + $this->typeExtensions[$typeExtension->getExtendedType()][] = $typeExtension; + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addTypeGuesser(FormTypeGuesserInterface $typeGuesser) + { + $this->typeGuessers[] = $typeGuesser; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addTypeGuessers(array $typeGuessers) + { + $this->typeGuessers = array_merge($this->typeGuessers, $typeGuessers); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFormFactory() + { + $extensions = $this->extensions; + + if (count($this->types) > 0 || count($this->typeExtensions) > 0 || count($this->typeGuessers) > 0) { + if (count($this->typeGuessers) > 1) { + $typeGuesser = new FormTypeGuesserChain($this->typeGuessers); + } else { + $typeGuesser = isset($this->typeGuessers[0]) ? $this->typeGuessers[0] : null; + } + + $extensions[] = new PreloadedExtension($this->types, $this->typeExtensions, $typeGuesser); + } + + $resolvedTypeFactory = $this->resolvedTypeFactory ?: new ResolvedFormTypeFactory(); + $registry = new FormRegistry($extensions, $resolvedTypeFactory); + + return new FormFactory($registry, $resolvedTypeFactory); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactoryBuilderInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactoryBuilderInterface.php new file mode 100644 index 0000000..9370c57 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactoryBuilderInterface.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; + +/** + * A builder for FormFactoryInterface objects. + * + * @author Bernhard Schussek + */ +interface FormFactoryBuilderInterface +{ + /** + * Sets the factory for creating ResolvedFormTypeInterface instances. + * + * @param ResolvedFormTypeFactoryInterface $resolvedTypeFactory + * + * @return FormFactoryBuilderInterface The builder. + */ + public function setResolvedTypeFactory(ResolvedFormTypeFactoryInterface $resolvedTypeFactory); + + /** + * Adds an extension to be loaded by the factory. + * + * @param FormExtensionInterface $extension The extension. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addExtension(FormExtensionInterface $extension); + + /** + * Adds a list of extensions to be loaded by the factory. + * + * @param array $extensions The extensions. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addExtensions(array $extensions); + + /** + * Adds a form type to the factory. + * + * @param FormTypeInterface $type The form type. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addType(FormTypeInterface $type); + + /** + * Adds a list of form types to the factory. + * + * @param array $types The form types. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addTypes(array $types); + + /** + * Adds a form type extension to the factory. + * + * @param FormTypeExtensionInterface $typeExtension The form type extension. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addTypeExtension(FormTypeExtensionInterface $typeExtension); + + /** + * Adds a list of form type extensions to the factory. + * + * @param array $typeExtensions The form type extensions. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addTypeExtensions(array $typeExtensions); + + /** + * Adds a type guesser to the factory. + * + * @param FormTypeGuesserInterface $typeGuesser The type guesser. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addTypeGuesser(FormTypeGuesserInterface $typeGuesser); + + /** + * Adds a list of type guessers to the factory. + * + * @param array $typeGuessers The type guessers. + * + * @return FormFactoryBuilderInterface The builder. + */ + public function addTypeGuessers(array $typeGuessers); + + /** + * Builds and returns the factory. + * + * @return FormFactoryInterface The form factory. + */ + public function getFormFactory(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactoryInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactoryInterface.php new file mode 100644 index 0000000..31c46b5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactoryInterface.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\Form; + +/** + * @author Bernhard Schussek + */ +interface FormFactoryInterface +{ + /** + * Returns a form. + * + * @see createBuilder() + * + * @param string|FormTypeInterface $type The type of the form + * @param mixed $data The initial data + * @param array $options The options + * + * @return FormInterface The form named after the type + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type + */ + public function create($type = 'form', $data = null, array $options = array()); + + /** + * Returns a form. + * + * @see createNamedBuilder() + * + * @param string|integer $name The name of the form + * @param string|FormTypeInterface $type The type of the form + * @param mixed $data The initial data + * @param array $options The options + * + * @return FormInterface The form + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type + */ + public function createNamed($name, $type = 'form', $data = null, array $options = array()); + + /** + * Returns a form for a property of a class. + * + * @see createBuilderForProperty() + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * @param mixed $data The initial data + * @param array $options The options for the builder + * + * @return FormInterface The form named after the property + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the form type + */ + public function createForProperty($class, $property, $data = null, array $options = array()); + + /** + * Returns a form builder. + * + * @param string|FormTypeInterface $type The type of the form + * @param mixed $data The initial data + * @param array $options The options + * + * @return FormBuilderInterface The form builder + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type + */ + public function createBuilder($type = 'form', $data = null, array $options = array()); + + /** + * Returns a form builder. + * + * @param string|integer $name The name of the form + * @param string|FormTypeInterface $type The type of the form + * @param mixed $data The initial data + * @param array $options The options + * + * @return FormBuilderInterface The form builder + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the given type + */ + public function createNamedBuilder($name, $type = 'form', $data = null, array $options = array()); + + /** + * Returns a form builder for a property of a class. + * + * If any of the 'max_length', 'required' and type options can be guessed, + * and are not provided in the options argument, the guessed value is used. + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * @param mixed $data The initial data + * @param array $options The options for the builder + * + * @return FormBuilderInterface The form builder named after the property + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException if any given option is not applicable to the form type + */ + public function createBuilderForProperty($class, $property, $data = null, array $options = array()); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormInterface.php new file mode 100644 index 0000000..5a852e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormInterface.php @@ -0,0 +1,288 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A form group bundling multiple forms in a hierarchical structure. + * + * @author Bernhard Schussek + */ +interface FormInterface extends \ArrayAccess, \Traversable, \Countable +{ + /** + * Sets the parent form. + * + * @param FormInterface|null $parent The parent form or null if it's the root. + * + * @return FormInterface The form instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + * @throws Exception\LogicException When trying to set a parent for a form with + * an empty name. + */ + public function setParent(FormInterface $parent = null); + + /** + * Returns the parent form. + * + * @return FormInterface|null The parent form or null if there is none. + */ + public function getParent(); + + /** + * Adds a child to the form. + * + * @param FormInterface|string|integer $child The FormInterface instance or the name of the child. + * @param string|null $type The child's type, if a name was passed. + * @param array $options The child's options, if a name was passed. + * + * @return FormInterface The form instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + * @throws Exception\LogicException When trying to add a child to a non-compound form. + * @throws Exception\UnexpectedTypeException If $child or $type has an unexpected type. + */ + public function add($child, $type = null, array $options = array()); + + /** + * Returns the child with the given name. + * + * @param string $name The name of the child + * + * @return FormInterface The child form + * + * @throws \OutOfBoundsException If the named child does not exist. + */ + public function get($name); + + /** + * Returns whether a child with the given name exists. + * + * @param string $name The name of the child + * + * @return Boolean + */ + public function has($name); + + /** + * Removes a child from the form. + * + * @param string $name The name of the child to remove + * + * @return FormInterface The form instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + */ + public function remove($name); + + /** + * Returns all children in this group. + * + * @return FormInterface[] An array of FormInterface instances + */ + public function all(); + + /** + * Returns all errors. + * + * @return FormError[] An array of FormError instances that occurred during validation + */ + public function getErrors(); + + /** + * Updates the form with default data. + * + * @param mixed $modelData The data formatted as expected for the underlying object + * + * @return FormInterface The form instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + * @throws Exception\LogicException If listeners try to call setData in a cycle. Or if + * the view data does not match the expected type + * according to {@link FormConfigInterface::getDataClass}. + */ + public function setData($modelData); + + /** + * Returns the data in the format needed for the underlying object. + * + * @return mixed + */ + public function getData(); + + /** + * Returns the normalized data of the field. + * + * @return mixed When the field is not submitted, the default data is returned. + * When the field is submitted, the normalized submitted data is + * returned if the field is valid, null otherwise. + */ + public function getNormData(); + + /** + * Returns the data transformed by the value transformer. + * + * @return mixed + */ + public function getViewData(); + + /** + * Returns the extra data. + * + * @return array The submitted data which do not belong to a child + */ + public function getExtraData(); + + /** + * Returns the form's configuration. + * + * @return FormConfigInterface The configuration. + */ + public function getConfig(); + + /** + * Returns whether the form is submitted. + * + * @return Boolean true if the form is submitted, false otherwise + */ + public function isSubmitted(); + + /** + * Returns the name by which the form is identified in forms. + * + * @return string The name of the form. + */ + public function getName(); + + /** + * Returns the property path that the form is mapped to. + * + * @return \Symfony\Component\PropertyAccess\PropertyPathInterface The property path. + */ + public function getPropertyPath(); + + /** + * Adds an error to this form. + * + * @param FormError $error + * + * @return FormInterface The form instance + */ + public function addError(FormError $error); + + /** + * Returns whether the form and all children are valid. + * + * If the form is not submitted, this method always returns false. + * + * @return Boolean + */ + public function isValid(); + + /** + * Returns whether the form is required to be filled out. + * + * If the form has a parent and the parent is not required, this method + * will always return false. Otherwise the value set with setRequired() + * is returned. + * + * @return Boolean + */ + public function isRequired(); + + /** + * Returns whether this form is disabled. + * + * The content of a disabled form is displayed, but not allowed to be + * modified. The validation of modified disabled forms should fail. + * + * Forms whose parents are disabled are considered disabled regardless of + * their own state. + * + * @return Boolean + */ + public function isDisabled(); + + /** + * Returns whether the form is empty. + * + * @return Boolean + */ + public function isEmpty(); + + /** + * Returns whether the data in the different formats is synchronized. + * + * @return Boolean + */ + public function isSynchronized(); + + /** + * Initializes the form tree. + * + * Should be called on the root form after constructing the tree. + * + * @return FormInterface The form instance. + */ + public function initialize(); + + /** + * Inspects the given request and calls {@link submit()} if the form was + * submitted. + * + * Internally, the request is forwarded to the configured + * {@link RequestHandlerInterface} instance, which determines whether to + * submit the form or not. + * + * @param mixed $request The request to handle. + * + * @return FormInterface The form instance. + */ + public function handleRequest($request = null); + + /** + * Submits data to the form, transforms and validates it. + * + * @param null|string|array $submittedData The submitted data. + * @param Boolean $clearMissing Whether to set fields to NULL + * when they are missing in the + * submitted data. + * + * @return FormInterface The form instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + */ + public function submit($submittedData, $clearMissing = true); + + /** + * Returns the root of the form tree. + * + * @return FormInterface The root of the tree + */ + public function getRoot(); + + /** + * Returns whether the field is the root of the form tree. + * + * @return Boolean + */ + public function isRoot(); + + /** + * Creates a view. + * + * @param FormView $parent The parent view + * + * @return FormView The view + */ + public function createView(FormView $parent = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormRegistry.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormRegistry.php new file mode 100644 index 0000000..0267a56 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormRegistry.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\Form; + +use Symfony\Component\Form\Exception\ExceptionInterface; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\Exception\InvalidArgumentException; + +/** + * The central registry of the Form component. + * + * @author Bernhard Schussek + */ +class FormRegistry implements FormRegistryInterface +{ + /** + * Extensions + * + * @var FormExtensionInterface[] An array of FormExtensionInterface + */ + private $extensions = array(); + + /** + * @var array + */ + private $types = array(); + + /** + * @var FormTypeGuesserInterface|false|null + */ + private $guesser = false; + + /** + * @var ResolvedFormTypeFactoryInterface + */ + private $resolvedTypeFactory; + + /** + * Constructor. + * + * @param FormExtensionInterface[] $extensions An array of FormExtensionInterface + * @param ResolvedFormTypeFactoryInterface $resolvedTypeFactory The factory for resolved form types. + * + * @throws UnexpectedTypeException if any extension does not implement FormExtensionInterface + */ + public function __construct(array $extensions, ResolvedFormTypeFactoryInterface $resolvedTypeFactory) + { + foreach ($extensions as $extension) { + if (!$extension instanceof FormExtensionInterface) { + throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormExtensionInterface'); + } + } + + $this->extensions = $extensions; + $this->resolvedTypeFactory = $resolvedTypeFactory; + } + + /** + * {@inheritdoc} + */ + public function getType($name) + { + if (!is_string($name)) { + throw new UnexpectedTypeException($name, 'string'); + } + + if (!isset($this->types[$name])) { + /** @var FormTypeInterface $type */ + $type = null; + + foreach ($this->extensions as $extension) { + /* @var FormExtensionInterface $extension */ + if ($extension->hasType($name)) { + $type = $extension->getType($name); + break; + } + } + + if (!$type) { + throw new InvalidArgumentException(sprintf('Could not load type "%s"', $name)); + } + + $this->resolveAndAddType($type); + } + + return $this->types[$name]; + } + + /** + * Wraps a type into a ResolvedFormTypeInterface implementation and connects + * it with its parent type. + * + * @param FormTypeInterface $type The type to resolve. + * + * @return ResolvedFormTypeInterface The resolved type. + */ + private function resolveAndAddType(FormTypeInterface $type) + { + $parentType = $type->getParent(); + + if ($parentType instanceof FormTypeInterface) { + $this->resolveAndAddType($parentType); + $parentType = $parentType->getName(); + } + + $typeExtensions = array(); + + foreach ($this->extensions as $extension) { + /* @var FormExtensionInterface $extension */ + $typeExtensions = array_merge( + $typeExtensions, + $extension->getTypeExtensions($type->getName()) + ); + } + + $this->types[$type->getName()] = $this->resolvedTypeFactory->createResolvedType( + $type, + $typeExtensions, + $parentType ? $this->getType($parentType) : null + ); + } + + /** + * {@inheritdoc} + */ + public function hasType($name) + { + if (isset($this->types[$name])) { + return true; + } + + try { + $this->getType($name); + } catch (ExceptionInterface $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getTypeGuesser() + { + if (false === $this->guesser) { + $guessers = array(); + + foreach ($this->extensions as $extension) { + /* @var FormExtensionInterface $extension */ + $guesser = $extension->getTypeGuesser(); + + if ($guesser) { + $guessers[] = $guesser; + } + } + + $this->guesser = !empty($guessers) ? new FormTypeGuesserChain($guessers) : null; + } + + return $this->guesser; + } + + /** + * {@inheritdoc} + */ + public function getExtensions() + { + return $this->extensions; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormRegistryInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormRegistryInterface.php new file mode 100644 index 0000000..16cd938 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormRegistryInterface.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; + +/** + * The central registry of the Form component. + * + * @author Bernhard Schussek + */ +interface FormRegistryInterface +{ + /** + * Returns a form type by name. + * + * This methods registers the type extensions from the form extensions. + * + * @param string $name The name of the type + * + * @return ResolvedFormTypeInterface The type + * + * @throws Exception\UnexpectedTypeException if the passed name is not a string + * @throws Exception\InvalidArgumentException if the type can not be retrieved from any extension + */ + public function getType($name); + + /** + * Returns whether the given form type is supported. + * + * @param string $name The name of the type + * + * @return Boolean Whether the type is supported + */ + public function hasType($name); + + /** + * Returns the guesser responsible for guessing types. + * + * @return FormTypeGuesserInterface|null + */ + public function getTypeGuesser(); + + /** + * Returns the extensions loaded by the framework. + * + * @return array + */ + public function getExtensions(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormRenderer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormRenderer.php new file mode 100644 index 0000000..09b0105 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormRenderer.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\Form; + +use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Exception\BadMethodCallException; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; + +/** + * Renders a form into HTML using a rendering engine. + * + * @author Bernhard Schussek + */ +class FormRenderer implements FormRendererInterface +{ + const CACHE_KEY_VAR = 'unique_block_prefix'; + + /** + * @var FormRendererEngineInterface + */ + private $engine; + + /** + * @var CsrfProviderInterface + */ + private $csrfProvider; + + /** + * @var array + */ + private $blockNameHierarchyMap = array(); + + /** + * @var array + */ + private $hierarchyLevelMap = array(); + + /** + * @var array + */ + private $variableStack = array(); + + public function __construct(FormRendererEngineInterface $engine, CsrfProviderInterface $csrfProvider = null) + { + $this->engine = $engine; + $this->csrfProvider = $csrfProvider; + } + + /** + * {@inheritdoc} + */ + public function getEngine() + { + return $this->engine; + } + + /** + * {@inheritdoc} + */ + public function setTheme(FormView $view, $themes) + { + $this->engine->setTheme($view, $themes); + } + + /** + * {@inheritdoc} + */ + public function renderCsrfToken($intention) + { + if (null === $this->csrfProvider) { + throw new BadMethodCallException('CSRF token can only be generated if a CsrfProviderInterface is injected in the constructor.'); + } + + return $this->csrfProvider->generateCsrfToken($intention); + } + + /** + * {@inheritdoc} + */ + public function renderBlock(FormView $view, $blockName, array $variables = array()) + { + $resource = $this->engine->getResourceForBlockName($view, $blockName); + + if (!$resource) { + throw new LogicException(sprintf('No block "%s" found while rendering the form.', $blockName)); + } + + $viewCacheKey = $view->vars[self::CACHE_KEY_VAR]; + + // The variables are cached globally for a view (instead of for the + // current suffix) + if (!isset($this->variableStack[$viewCacheKey])) { + $this->variableStack[$viewCacheKey] = array(); + + // The default variable scope contains all view variables, merged with + // the variables passed explicitly to the helper + $scopeVariables = $view->vars; + + $varInit = true; + } else { + // Reuse the current scope and merge it with the explicitly passed variables + $scopeVariables = end($this->variableStack[$viewCacheKey]); + + $varInit = false; + } + + // Merge the passed with the existing attributes + if (isset($variables['attr']) && isset($scopeVariables['attr'])) { + $variables['attr'] = array_replace($scopeVariables['attr'], $variables['attr']); + } + + // Merge the passed with the exist *label* attributes + if (isset($variables['label_attr']) && isset($scopeVariables['label_attr'])) { + $variables['label_attr'] = array_replace($scopeVariables['label_attr'], $variables['label_attr']); + } + + // Do not use array_replace_recursive(), otherwise array variables + // cannot be overwritten + $variables = array_replace($scopeVariables, $variables); + + $this->variableStack[$viewCacheKey][] = $variables; + + // Do the rendering + $html = $this->engine->renderBlock($view, $resource, $blockName, $variables); + + // Clear the stack + array_pop($this->variableStack[$viewCacheKey]); + + if ($varInit) { + unset($this->variableStack[$viewCacheKey]); + } + + return $html; + } + + /** + * {@inheritdoc} + */ + public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $variables = array()) + { + $renderOnlyOnce = 'row' === $blockNameSuffix || 'widget' === $blockNameSuffix; + + if ($renderOnlyOnce && $view->isRendered()) { + return ''; + } + + // The cache key for storing the variables and types + $viewCacheKey = $view->vars[self::CACHE_KEY_VAR]; + $viewAndSuffixCacheKey = $viewCacheKey.$blockNameSuffix; + + // In templates, we have to deal with two kinds of block hierarchies: + // + // +---------+ +---------+ + // | Theme B | -------> | Theme A | + // +---------+ +---------+ + // + // form_widget -------> form_widget + // ^ + // | + // choice_widget -----> choice_widget + // + // The first kind of hierarchy is the theme hierarchy. This allows to + // override the block "choice_widget" from Theme A in the extending + // Theme B. This kind of inheritance needs to be supported by the + // template engine and, for example, offers "parent()" or similar + // functions to fall back from the custom to the parent implementation. + // + // The second kind of hierarchy is the form type hierarchy. This allows + // to implement a custom "choice_widget" block (no matter in which theme), + // or to fallback to the block of the parent type, which would be + // "form_widget" in this example (again, no matter in which theme). + // If the designer wants to explicitly fallback to "form_widget" in his + // custom "choice_widget", for example because he only wants to wrap + // a
    around the original implementation, he can simply call the + // widget() function again to render the block for the parent type. + // + // The second kind is implemented in the following blocks. + if (!isset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey])) { + // INITIAL CALL + // Calculate the hierarchy of template blocks and start on + // the bottom level of the hierarchy (= "__
    " block) + $blockNameHierarchy = array(); + foreach ($view->vars['block_prefixes'] as $blockNamePrefix) { + $blockNameHierarchy[] = $blockNamePrefix.'_'.$blockNameSuffix; + } + $hierarchyLevel = count($blockNameHierarchy) - 1; + + $hierarchyInit = true; + } else { + // RECURSIVE CALL + // If a block recursively calls searchAndRenderBlock() again, resume rendering + // using the parent type in the hierarchy. + $blockNameHierarchy = $this->blockNameHierarchyMap[$viewAndSuffixCacheKey]; + $hierarchyLevel = $this->hierarchyLevelMap[$viewAndSuffixCacheKey] - 1; + + $hierarchyInit = false; + } + + // The variables are cached globally for a view (instead of for the + // current suffix) + if (!isset($this->variableStack[$viewCacheKey])) { + $this->variableStack[$viewCacheKey] = array(); + + // The default variable scope contains all view variables, merged with + // the variables passed explicitly to the helper + $scopeVariables = $view->vars; + + $varInit = true; + } else { + // Reuse the current scope and merge it with the explicitly passed variables + $scopeVariables = end($this->variableStack[$viewCacheKey]); + + $varInit = false; + } + + // Load the resource where this block can be found + $resource = $this->engine->getResourceForBlockNameHierarchy($view, $blockNameHierarchy, $hierarchyLevel); + + // Update the current hierarchy level to the one at which the resource was + // found. For example, if looking for "choice_widget", but only a resource + // is found for its parent "form_widget", then the level is updated here + // to the parent level. + $hierarchyLevel = $this->engine->getResourceHierarchyLevel($view, $blockNameHierarchy, $hierarchyLevel); + + // The actually existing block name in $resource + $blockName = $blockNameHierarchy[$hierarchyLevel]; + + // Escape if no resource exists for this block + if (!$resource) { + throw new LogicException(sprintf( + 'Unable to render the form as none of the following blocks exist: "%s".', + implode('", "', array_reverse($blockNameHierarchy)) + )); + } + + // Merge the passed with the existing attributes + if (isset($variables['attr']) && isset($scopeVariables['attr'])) { + $variables['attr'] = array_replace($scopeVariables['attr'], $variables['attr']); + } + + // Merge the passed with the exist *label* attributes + if (isset($variables['label_attr']) && isset($scopeVariables['label_attr'])) { + $variables['label_attr'] = array_replace($scopeVariables['label_attr'], $variables['label_attr']); + } + + // Do not use array_replace_recursive(), otherwise array variables + // cannot be overwritten + $variables = array_replace($scopeVariables, $variables); + + // In order to make recursive calls possible, we need to store the block hierarchy, + // the current level of the hierarchy and the variables so that this method can + // resume rendering one level higher of the hierarchy when it is called recursively. + // + // We need to store these values in maps (associative arrays) because within a + // call to widget() another call to widget() can be made, but for a different view + // object. These nested calls should not override each other. + $this->blockNameHierarchyMap[$viewAndSuffixCacheKey] = $blockNameHierarchy; + $this->hierarchyLevelMap[$viewAndSuffixCacheKey] = $hierarchyLevel; + + // We also need to store the variables for the view so that we can render other + // blocks for the same view using the same variables as in the outer block. + $this->variableStack[$viewCacheKey][] = $variables; + + // Do the rendering + $html = $this->engine->renderBlock($view, $resource, $blockName, $variables); + + // Clear the stack + array_pop($this->variableStack[$viewCacheKey]); + + // Clear the caches if they were filled for the first time within + // this function call + if ($hierarchyInit) { + unset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey]); + unset($this->hierarchyLevelMap[$viewAndSuffixCacheKey]); + } + + if ($varInit) { + unset($this->variableStack[$viewCacheKey]); + } + + if ($renderOnlyOnce) { + $view->setRendered(); + } + + return $html; + } + + /** + * {@inheritdoc} + */ + public function humanize($text) + { + return ucfirst(trim(strtolower(preg_replace(array('/([A-Z])/', '/[_\s]+/'), array('_$1', ' '), $text)))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormRendererEngineInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormRendererEngineInterface.php new file mode 100644 index 0000000..e06824e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormRendererEngineInterface.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\Form; + +/** + * Adapter for rendering form templates with a specific templating engine. + * + * @author Bernhard Schussek + */ +interface FormRendererEngineInterface +{ + /** + * Sets the theme(s) to be used for rendering a view and its children. + * + * @param FormView $view The view to assign the theme(s) to. + * @param mixed $themes The theme(s). The type of these themes + * is open to the implementation. + */ + public function setTheme(FormView $view, $themes); + + /** + * Returns the resource for a block name. + * + * The resource is first searched in the themes attached to $view, then + * in the themes of its parent view and so on, until a resource was found. + * + * The type of the resource is decided by the implementation. The resource + * is later passed to {@link renderBlock()} by the rendering algorithm. + * + * @param FormView $view The view for determining the used themes. + * First the themes attached directly to the + * view with {@link setTheme()} are considered, + * then the ones of its parent etc. + * @param string $blockName The name of the block to render. + * + * @return mixed The renderer resource or false, if none was found. + */ + public function getResourceForBlockName(FormView $view, $blockName); + + /** + * Returns the resource for a block hierarchy. + * + * A block hierarchy is an array which starts with the root of the hierarchy + * and continues with the child of that root, the child of that child etc. + * The following is an example for a block hierarchy: + * + * + * form_widget + * text_widget + * url_widget + * + * + * In this example, "url_widget" is the most specific block, while the other + * blocks are its ancestors in the hierarchy. + * + * The second parameter $hierarchyLevel determines the level of the hierarchy + * that should be rendered. For example, if $hierarchyLevel is 2 for the + * above hierarchy, the engine will first look for the block "url_widget", + * then, if that does not exist, for the block "text_widget" etc. + * + * The type of the resource is decided by the implementation. The resource + * is later passed to {@link renderBlock()} by the rendering algorithm. + * + * @param FormView $view The view for determining the + * used themes. First the themes + * attached directly to the view + * with {@link setTheme()} are + * considered, then the ones of + * its parent etc. + * @param array $blockNameHierarchy The block name hierarchy, with + * the root block at the beginning. + * @param integer $hierarchyLevel The level in the hierarchy at + * which to start looking. Level 0 + * indicates the root block, i.e. + * the first element of + * $blockNameHierarchy. + * + * @return mixed The renderer resource or false, if none was found. + */ + public function getResourceForBlockNameHierarchy(FormView $view, array $blockNameHierarchy, $hierarchyLevel); + + /** + * Returns the hierarchy level at which a resource can be found. + * + * A block hierarchy is an array which starts with the root of the hierarchy + * and continues with the child of that root, the child of that child etc. + * The following is an example for a block hierarchy: + * + * + * form_widget + * text_widget + * url_widget + * + * + * The second parameter $hierarchyLevel determines the level of the hierarchy + * that should be rendered. + * + * If we call this method with the hierarchy level 2, the engine will first + * look for a resource for block "url_widget". If such a resource exists, + * the method returns 2. Otherwise it tries to find a resource for block + * "text_widget" (at level 1) and, again, returns 1 if a resource was found. + * The method continues to look for resources until the root level was + * reached and nothing was found. In this case false is returned. + * + * The type of the resource is decided by the implementation. The resource + * is later passed to {@link renderBlock()} by the rendering algorithm. + * + * @param FormView $view The view for determining the + * used themes. First the themes + * attached directly to the view + * with {@link setTheme()} are + * considered, then the ones of + * its parent etc. + * @param array $blockNameHierarchy The block name hierarchy, with + * the root block at the beginning. + * @param integer $hierarchyLevel The level in the hierarchy at + * which to start looking. Level 0 + * indicates the root block, i.e. + * the first element of + * $blockNameHierarchy. + * + * @return integer|Boolean The hierarchy level or false, if no resource was found. + */ + public function getResourceHierarchyLevel(FormView $view, array $blockNameHierarchy, $hierarchyLevel); + + /** + * Renders a block in the given renderer resource. + * + * The resource can be obtained by calling {@link getResourceForBlock()} + * or {@link getResourceForBlockHierarchy()}. The type of the resource is + * decided by the implementation. + * + * @param FormView $view The view to render. + * @param mixed $resource The renderer resource. + * @param string $blockName The name of the block to render. + * @param array $variables The variables to pass to the template. + * + * @return string The HTML markup. + */ + public function renderBlock(FormView $view, $resource, $blockName, array $variables = array()); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormRendererInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormRendererInterface.php new file mode 100644 index 0000000..848e712 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormRendererInterface.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\Form; + +/** + * Renders a form into HTML. + * + * @author Bernhard Schussek + */ +interface FormRendererInterface +{ + /** + * Returns the engine used by this renderer. + * + * @return FormRendererEngineInterface The renderer engine. + */ + public function getEngine(); + + /** + * Sets the theme(s) to be used for rendering a view and its children. + * + * @param FormView $view The view to assign the theme(s) to. + * @param mixed $themes The theme(s). The type of these themes + * is open to the implementation. + */ + public function setTheme(FormView $view, $themes); + + /** + * Renders a named block of the form theme. + * + * @param FormView $view The view for which to render the block. + * @param string $blockName The name of the block. + * @param array $variables The variables to pass to the template. + * + * @return string The HTML markup + */ + public function renderBlock(FormView $view, $blockName, array $variables = array()); + + /** + * Searches and renders a block for a given name suffix. + * + * The block is searched by combining the block names stored in the + * form view with the given suffix. If a block name is found, that + * block is rendered. + * + * If this method is called recursively, the block search is continued + * where a block was found before. + * + * @param FormView $view The view for which to render the block. + * @param string $blockNameSuffix The suffix of the block name. + * @param array $variables The variables to pass to the template. + * + * @return string The HTML markup + */ + public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $variables = array()); + + /** + * Renders a CSRF token. + * + * Use this helper for CSRF protection without the overhead of creating a + * form. + * + * + * + * + * + * Check the token in your action using the same intention. + * + * + * $csrfProvider = $this->get('form.csrf_provider'); + * if (!$csrfProvider->isCsrfTokenValid('rm_user_'.$user->getId(), $token)) { + * throw new \RuntimeException('CSRF attack detected.'); + * } + * + * + * @param string $intention The intention of the protected action + * + * @return string A CSRF token + */ + public function renderCsrfToken($intention); + + /** + * Makes a technical name human readable. + * + * Sequences of underscores are replaced by single spaces. The first letter + * of the resulting string is capitalized, while all other letters are + * turned to lowercase. + * + * @param string $text The text to humanize. + * + * @return string The humanized text. + */ + public function humanize($text); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeExtensionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeExtensionInterface.php new file mode 100644 index 0000000..9866b28 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeExtensionInterface.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; + +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +interface FormTypeExtensionInterface +{ + /** + * Builds the form. + * + * This method is called after the extended type has built the form to + * further modify it. + * + * @see FormTypeInterface::buildForm() + * + * @param FormBuilderInterface $builder The form builder + * @param array $options The options + */ + public function buildForm(FormBuilderInterface $builder, array $options); + + /** + * Builds the view. + * + * This method is called after the extended type has built the view to + * further modify it. + * + * @see FormTypeInterface::buildView() + * + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options + */ + public function buildView(FormView $view, FormInterface $form, array $options); + + /** + * Finishes the view. + * + * This method is called after the extended type has finished the view to + * further modify it. + * + * @see FormTypeInterface::finishView() + * + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options + */ + public function finishView(FormView $view, FormInterface $form, array $options); + + /** + * Overrides the default options from the extended type. + * + * @param OptionsResolverInterface $resolver The resolver for the options. + */ + public function setDefaultOptions(OptionsResolverInterface $resolver); + + /** + * Returns the name of the type being extended. + * + * @return string The name of the type being extended + */ + public function getExtendedType(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeGuesserChain.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeGuesserChain.php new file mode 100644 index 0000000..c7f8ece --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeGuesserChain.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; + +use Symfony\Component\Form\Guess\Guess; +use Symfony\Component\Form\Exception\UnexpectedTypeException; + +class FormTypeGuesserChain implements FormTypeGuesserInterface +{ + private $guessers = array(); + + /** + * Constructor. + * + * @param array $guessers Guessers as instances of FormTypeGuesserInterface + * + * @throws UnexpectedTypeException if any guesser does not implement FormTypeGuesserInterface + */ + public function __construct(array $guessers) + { + foreach ($guessers as $guesser) { + if (!$guesser instanceof FormTypeGuesserInterface) { + throw new UnexpectedTypeException($guesser, 'Symfony\Component\Form\FormTypeGuesserInterface'); + } + + if ($guesser instanceof self) { + $this->guessers = array_merge($this->guessers, $guesser->guessers); + } else { + $this->guessers[] = $guesser; + } + } + } + + /** + * {@inheritDoc} + */ + public function guessType($class, $property) + { + return $this->guess(function ($guesser) use ($class, $property) { + return $guesser->guessType($class, $property); + }); + } + + /** + * {@inheritDoc} + */ + public function guessRequired($class, $property) + { + return $this->guess(function ($guesser) use ($class, $property) { + return $guesser->guessRequired($class, $property); + }); + } + + /** + * {@inheritDoc} + */ + public function guessMaxLength($class, $property) + { + return $this->guess(function ($guesser) use ($class, $property) { + return $guesser->guessMaxLength($class, $property); + }); + } + + /** + * {@inheritDoc} + */ + public function guessPattern($class, $property) + { + return $this->guess(function ($guesser) use ($class, $property) { + return $guesser->guessPattern($class, $property); + }); + } + + /** + * Executes a closure for each guesser and returns the best guess from the + * return values + * + * @param \Closure $closure The closure to execute. Accepts a guesser + * as argument and should return a Guess instance + * + * @return Guess The guess with the highest confidence + */ + private function guess(\Closure $closure) + { + $guesses = array(); + + foreach ($this->guessers as $guesser) { + if ($guess = $closure($guesser)) { + $guesses[] = $guess; + } + } + + return Guess::getBestGuess($guesses); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeGuesserInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeGuesserInterface.php new file mode 100644 index 0000000..e8b603f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeGuesserInterface.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; + +/** + * @author Bernhard Schussek + */ +interface FormTypeGuesserInterface +{ + /** + * Returns a field guess for a property name of a class + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * + * @return Guess\TypeGuess A guess for the field's type and options + */ + public function guessType($class, $property); + + /** + * Returns a guess whether a property of a class is required + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * + * @return Guess\Guess A guess for the field's required setting + */ + public function guessRequired($class, $property); + + /** + * Returns a guess about the field's maximum length + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * + * @return Guess\Guess A guess for the field's maximum length + */ + public function guessMaxLength($class, $property); + + /** + * Returns a guess about the field's pattern + * + * - When you have a min value, you guess a min length of this min (LOW_CONFIDENCE) , lines below + * - If this value is a float type, this is wrong so you guess null with MEDIUM_CONFIDENCE to override the previous guess. + * Example: + * You want a float greater than 5, 4.512313 is not valid but length(4.512314) > length(5) + * @link https://github.com/symfony/symfony/pull/3927 + * + * @param string $class The fully qualified class name + * @param string $property The name of the property to guess for + * + * @return Guess\Guess A guess for the field's required pattern + */ + public function guessPattern($class, $property); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeInterface.php new file mode 100644 index 0000000..bcef73c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormTypeInterface.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\Form; + +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @author Bernhard Schussek + */ +interface FormTypeInterface +{ + /** + * Builds the form. + * + * This method is called for each type in the hierarchy starting form the + * top most type. Type extensions can further modify the form. + * + * @see FormTypeExtensionInterface::buildForm() + * + * @param FormBuilderInterface $builder The form builder + * @param array $options The options + */ + public function buildForm(FormBuilderInterface $builder, array $options); + + /** + * Builds the form view. + * + * This method is called for each type in the hierarchy starting form the + * top most type. Type extensions can further modify the view. + * + * A view of a form is built before the views of the child forms are built. + * This means that you cannot access child views in this method. If you need + * to do so, move your logic to {@link finishView()} instead. + * + * @see FormTypeExtensionInterface::buildView() + * + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options + */ + public function buildView(FormView $view, FormInterface $form, array $options); + + /** + * Finishes the form view. + * + * This method gets called for each type in the hierarchy starting form the + * top most type. Type extensions can further modify the view. + * + * When this method is called, views of the form's children have already + * been built and finished and can be accessed. You should only implement + * such logic in this method that actually accesses child views. For everything + * else you are recommended to implement {@link buildView()} instead. + * + * @see FormTypeExtensionInterface::finishView() + * + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options + */ + public function finishView(FormView $view, FormInterface $form, array $options); + + /** + * Sets the default options for this type. + * + * @param OptionsResolverInterface $resolver The resolver for the options. + */ + public function setDefaultOptions(OptionsResolverInterface $resolver); + + /** + * Returns the name of the parent type. + * + * You can also return a type instance from this method, although doing so + * is discouraged because it leads to a performance penalty. The support + * for returning type instances may be dropped from future releases. + * + * @return string|null|FormTypeInterface The name of the parent type if any, null otherwise. + */ + public function getParent(); + + /** + * Returns the name of this type. + * + * @return string The name of this type + */ + public function getName(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/FormView.php b/vendor/symfony/symfony/src/Symfony/Component/Form/FormView.php new file mode 100644 index 0000000..1f53ec6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/FormView.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\Form; + +use Symfony\Component\Form\Exception\BadMethodCallException; + +/** + * @author Bernhard Schussek + */ +class FormView implements \ArrayAccess, \IteratorAggregate, \Countable +{ + /** + * The variables assigned to this view. + * @var array + */ + public $vars = array( + 'value' => null, + 'attr' => array(), + ); + + /** + * The parent view. + * @var FormView + */ + public $parent; + + /** + * The child views. + * @var array + */ + public $children = array(); + + /** + * Is the form attached to this renderer rendered? + * + * Rendering happens when either the widget or the row method was called. + * Row implicitly includes widget, however certain rendering mechanisms + * have to skip widget rendering when a row is rendered. + * + * @var Boolean + */ + private $rendered = false; + + public function __construct(FormView $parent = null) + { + $this->parent = $parent; + } + + /** + * Returns whether the view was already rendered. + * + * @return Boolean Whether this view's widget is rendered. + */ + public function isRendered() + { + $hasChildren = 0 < count($this->children); + + if (true === $this->rendered || !$hasChildren) { + return $this->rendered; + } + + if ($hasChildren) { + foreach ($this->children as $child) { + if (!$child->isRendered()) { + return false; + } + } + + return $this->rendered = true; + } + + return false; + } + + /** + * Marks the view as rendered. + * + * @return FormView The view object. + */ + public function setRendered() + { + $this->rendered = true; + + return $this; + } + + /** + * Returns a child by name (implements \ArrayAccess). + * + * @param string $name The child name + * + * @return FormView The child view + */ + public function offsetGet($name) + { + return $this->children[$name]; + } + + /** + * Returns whether the given child exists (implements \ArrayAccess). + * + * @param string $name The child name + * + * @return Boolean Whether the child view exists + */ + public function offsetExists($name) + { + return isset($this->children[$name]); + } + + /** + * Implements \ArrayAccess. + * + * @throws BadMethodCallException always as setting a child by name is not allowed + */ + public function offsetSet($name, $value) + { + throw new BadMethodCallException('Not supported'); + } + + /** + * Removes a child (implements \ArrayAccess). + * + * @param string $name The child name + */ + public function offsetUnset($name) + { + unset($this->children[$name]); + } + + /** + * Returns an iterator to iterate over children (implements \IteratorAggregate) + * + * @return \ArrayIterator The iterator + */ + public function getIterator() + { + return new \ArrayIterator($this->children); + } + + /** + * Implements \Countable. + * + * @return integer The number of children views + */ + public function count() + { + return count($this->children); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Forms.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Forms.php new file mode 100644 index 0000000..c949c1f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Forms.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\Component\Form; + +use Symfony\Component\Form\Extension\Core\CoreExtension; + +/** + * Entry point of the Form component. + * + * Use this class to conveniently create new form factories: + * + * + * use Symfony\Component\Form\Forms; + * + * $formFactory = Forms::createFormFactory(); + * + * $form = $formFactory->createBuilder() + * ->add('firstName', 'text') + * ->add('lastName', 'text') + * ->add('age', 'integer') + * ->add('gender', 'choice', array( + * 'choices' => array('m' => 'Male', 'f' => 'Female'), + * )) + * ->getForm(); + * + * + * You can also add custom extensions to the form factory: + * + * + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new AcmeExtension()) + * ->getFormFactory(); + * + * + * If you create custom form types or type extensions, it is + * generally recommended to create your own extensions that lazily + * load these types and type extensions. In projects where performance + * does not matter that much, you can also pass them directly to the + * form factory: + * + * + * $formFactory = Forms::createFormFactoryBuilder() + * ->addType(new PersonType()) + * ->addType(new PhoneNumberType()) + * ->addTypeExtension(new FormTypeHelpTextExtension()) + * ->getFormFactory(); + * + * + * Support for CSRF protection is provided by the CsrfExtension. + * This extension needs a CSRF provider with a strong secret + * (e.g. a 20 character long random string). The default + * implementation for this is DefaultCsrfProvider: + * + * + * use Symfony\Component\Form\Extension\Csrf\CsrfExtension; + * use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; + * + * $secret = 'V8a5Z97e...'; + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new CsrfExtension(new DefaultCsrfProvider($secret))) + * ->getFormFactory(); + * + * + * Support for the HttpFoundation is provided by the + * HttpFoundationExtension. You are also advised to load the CSRF + * extension with the driver for HttpFoundation's Session class: + * + * + * use Symfony\Component\HttpFoundation\Session\Session; + * use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension; + * use Symfony\Component\Form\Extension\Csrf\CsrfExtension; + * use Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider; + * + * $session = new Session(); + * $secret = 'V8a5Z97e...'; + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new HttpFoundationExtension()) + * ->addExtension(new CsrfExtension(new SessionCsrfProvider($session, $secret))) + * ->getFormFactory(); + * + * + * Support for the Validator component is provided by ValidatorExtension. + * This extension needs a validator object to function properly: + * + * + * use Symfony\Component\Validator\Validation; + * use Symfony\Component\Form\Extension\Validator\ValidatorExtension; + * + * $validator = Validation::createValidator(); + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new ValidatorExtension($validator)) + * ->getFormFactory(); + * + * + * Support for the Templating component is provided by TemplatingExtension. + * This extension needs a PhpEngine object for rendering forms. As second + * argument you should pass the names of the default themes. Here is an + * example for using the default layout with "
    " tags: + * + * + * use Symfony\Component\Form\Extension\Templating\TemplatingExtension; + * + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new TemplatingExtension($engine, null, array( + * 'FrameworkBundle:Form', + * ))) + * ->getFormFactory(); + * + * + * The next example shows how to include the "
    " layout: + * + * + * use Symfony\Component\Form\Extension\Templating\TemplatingExtension; + * + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new TemplatingExtension($engine, null, array( + * 'FrameworkBundle:Form', + * 'FrameworkBundle:FormTable', + * ))) + * ->getFormFactory(); + * + * + * If you also loaded the CsrfExtension, you should pass the CSRF provider + * to the extension so that you can render CSRF tokens in your templates + * more easily: + * + * + * use Symfony\Component\Form\Extension\Csrf\CsrfExtension; + * use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; + * use Symfony\Component\Form\Extension\Templating\TemplatingExtension; + * + * + * $secret = 'V8a5Z97e...'; + * $csrfProvider = new DefaultCsrfProvider($secret); + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new CsrfExtension($csrfProvider)) + * ->addExtension(new TemplatingExtension($engine, $csrfProvider, array( + * 'FrameworkBundle:Form', + * ))) + * ->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..b33c3d8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/Guess.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\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 integer + */ + const VERY_HIGH_CONFIDENCE = 3; + + /** + * Marks an instance with a value that is very likely to be correct + * @var integer + */ + const HIGH_CONFIDENCE = 2; + + /** + * Marks an instance with a value that is likely to be correct + * @var integer + */ + const MEDIUM_CONFIDENCE = 1; + + /** + * Marks an instance with a value that may be correct + * @var integer + */ + 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 integer + */ + 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 array $guesses A list of guesses + * + * @return Guess 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 integer $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 integer 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..3241e60 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/TypeGuess.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\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 integer $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..2e3333b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/ValueGuess.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\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 integer $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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Component/Form/NativeRequestHandler.php b/vendor/symfony/symfony/src/Symfony/Component/Form/NativeRequestHandler.php new file mode 100644 index 0000000..aaa4e4c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/NativeRequestHandler.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\Form; + +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\RequestHandlerInterface; + +/** + * A request handler using PHP's super globals $_GET, $_POST and $_SERVER. + * + * @author Bernhard Schussek + */ +class NativeRequestHandler implements RequestHandlerInterface +{ + /** + * 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; + } + + if ('GET' === $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 { + $fixedFiles = array(); + foreach ($_FILES as $name => $file) { + $fixedFiles[$name] = self::stripEmptyFiles(self::fixPhpFilesArray($file)); + } + + if ('' === $name) { + $params = $_POST; + $files = $fixedFiles; + } else { + $default = $form->getConfig()->getCompound() ? array() : null; + $params = isset($_POST[$name]) ? $_POST[$name] : $default; + $files = isset($fixedFiles[$name]) ? $fixedFiles[$name] : $default; + } + + 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 (array_keys($data['name']) as $key) { + $files[$key] = self::fixPhpFilesArray(array( + 'error' => $data['error'][$key], + 'name' => $data['name'][$key], + '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 null; + } + + 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..2d3e9ef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/PreloadedExtension.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\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 array + */ + private $types = array(); + + /** + * @var array + */ + private $typeExtensions = array(); + + /** + * @var FormTypeGuesserInterface + */ + private $typeGuesser; + + /** + * Creates a new preloaded extension. + * + * @param array $types The types that the extension should support. + * @param array $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->types = $types; + $this->typeExtensions = $typeExtensions; + $this->typeGuesser = $typeGuesser; + } + + /** + * {@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..7bfff7f --- /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/fabpot/Silex/blob/master/src/Silex/Provider/FormServiceProvider.php + +Documentation: + +http://symfony.com/doc/2.3/book/forms.html + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Form/ + $ composer.phar install --dev + $ 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..47d4355 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormType.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\Form; + +use Symfony\Component\Form\Exception\InvalidArgumentException; +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 array + */ + private $typeExtensions; + + /** + * @var ResolvedFormTypeInterface + */ + private $parent; + + /** + * @var OptionsResolver + */ + private $optionsResolver; + + public function __construct(FormTypeInterface $innerType, array $typeExtensions = array(), ResolvedFormTypeInterface $parent = null) + { + if (!preg_match('/^[a-z0-9_]*$/i', $innerType->getName())) { + throw new InvalidArgumentException(sprintf( + 'The "%s" form type name ("%s") is not valid. Names must only contain letters, numbers, and "_".', + get_class($innerType), + $innerType->getName() + )); + } + + 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 getName() + { + return $this->innerType->getName(); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return $this->parent; + } + + /** + * {@inheritdoc} + */ + public function getInnerType() + { + return $this->innerType; + } + + /** + * {@inheritdoc} + */ + public function getTypeExtensions() + { + // BC + if ($this->innerType instanceof AbstractType) { + return $this->innerType->getExtensions(); + } + + 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); + + $this->buildForm($builder, $options); + + return $builder; + } + + /** + * {@inheritdoc} + */ + public function createView(FormInterface $form, FormView $parent = null) + { + $options = $form->getConfig()->getOptions(); + + $view = $this->newView($parent); + + $this->buildView($view, $form, $options); + + foreach ($form as $name => $child) { + /* @var FormInterface $child */ + $view->children[$name] = $child->createView($view); + } + + $this->finishView($view, $form, $options); + + return $view; + } + + /** + * Configures a form builder for the type hierarchy. + * + * This method is protected in order to allow implementing classes + * to change or call it in re-implementations of {@link createBuilder()}. + * + * @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) { + /* @var FormTypeExtensionInterface $extension */ + $extension->buildForm($builder, $options); + } + } + + /** + * Configures a form view for the type hierarchy. + * + * This method is protected in order to allow implementing classes + * to change or call it in re-implementations of {@link createView()}. + * + * 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) + { + if (null !== $this->parent) { + $this->parent->buildView($view, $form, $options); + } + + $this->innerType->buildView($view, $form, $options); + + foreach ($this->typeExtensions as $extension) { + /* @var FormTypeExtensionInterface $extension */ + $extension->buildView($view, $form, $options); + } + } + + /** + * Finishes a form view for the type hierarchy. + * + * This method is protected in order to allow implementing classes + * to change or call it in re-implementations of {@link createView()}. + * + * 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) + { + 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. + * + * This method is protected in order to allow implementing classes + * to change or call it in re-implementations of {@link createBuilder()}. + * + * @return \Symfony\Component\OptionsResolver\OptionsResolverInterface 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->setDefaultOptions($this->optionsResolver); + + foreach ($this->typeExtensions as $extension) { + /* @var FormTypeExtensionInterface $extension */ + $extension->setDefaultOptions($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..f0ec233 --- /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 array $typeExtensions + * @param ResolvedFormTypeInterface $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..c999bcd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormTypeInterface.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\Form; + +/** + * A wrapper for a form type and its extensions. + * + * @author Bernhard Schussek + */ +interface ResolvedFormTypeInterface +{ + /** + * Returns the name of the type. + * + * @return string The type name. + */ + public function getName(); + + /** + * Returns the parent type. + * + * @return ResolvedFormTypeInterface 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 \Symfony\Component\OptionsResolver\OptionsResolverInterface 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.bg.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.bg.xlf new file mode 100644 index 0000000..6f00bde --- /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 токен. Моля, опитайте да изпратите формата отново. + + + + \ No newline at end of file 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..c801d67 --- /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. + Das CSRF-Token ist ungültig. Versuchen Sie bitte das Formular erneut zu senden. + + + + \ No newline at end of file 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..ebfacfd --- /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 de 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..a07f786 --- /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. + CSFR 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..d223bb0 --- /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. Olen 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..db23fe2 --- /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..5198738 --- /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. + אסימון 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..d1491e7 --- /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..4559af1 --- /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. + 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..103124d --- /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. + To področje ne sme vsebovati dodatnih polj. + + + The uploaded file was too large. Please try to upload a smaller file. + Naložena datoteka je prevelika. Prosim, 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.ua.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ua.xlf new file mode 100644 index 0000000..4c739fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ua.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..7069705 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/ReversedTransformer.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\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..47d4be0 --- /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 Boolean + */ + private $clicked = false; + + /** + * {@inheritdoc} + */ + public function isClicked() + { + return $this->clicked; + } + + /** + * Submits data to the button. + * + * @param null|string $submittedData The data. + * @param Boolean $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..088fb74 --- /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 Button 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/DeprecationErrorHandler.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/DeprecationErrorHandler.php new file mode 100644 index 0000000..36417f7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/DeprecationErrorHandler.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\Test; + +use Symfony\Component\Form\FormEvent; + +class DeprecationErrorHandler +{ + public static function handle($errorNumber, $message, $file, $line, $context) + { + if ($errorNumber & E_USER_DEPRECATED) { + return true; + } + + return \PHPUnit_Util_ErrorHandler::handleError($errorNumber, $message, $file, $line); + } + + public static function handleBC($errorNumber, $message, $file, $line, $context) + { + if ($errorNumber & E_USER_DEPRECATED) { + return true; + } + + return false; + } + + public static function preBind($listener, FormEvent $event) + { + set_error_handler(array('Symfony\Component\Form\Test\DeprecationErrorHandler', 'handle')); + $listener->preBind($event); + restore_error_handler(); + } +} 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..711cecd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormBuilderInterface.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\Test; + +interface FormBuilderInterface extends \Iterator, \Symfony\Component\Form\FormBuilderInterface +{ +} 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..68e5f24 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormIntegrationTestCase.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\Forms; + +/** + * @author Bernhard Schussek + */ +abstract class FormIntegrationTestCase extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\Form\FormFactoryInterface + */ + protected $factory; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $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..22a83fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormInterface.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\Test; + +interface FormInterface extends \Iterator, \Symfony\Component\Form\FormInterface +{ +} 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..573f4e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormPerformanceTestCase.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\Test; + +/** + * Base class for performance tests. + * + * Copied from Doctrine 2's OrmPerformanceTestCase. + * + * @author robo + * @author Bernhard Schussek + */ +abstract class FormPerformanceTestCase extends FormIntegrationTestCase +{ + /** + * @var integer + */ + protected $maxRunningTime = 0; + + /** + */ + 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 integer $maxRunningTime + * @throws \InvalidArgumentException + */ + public function setMaxRunningTime($maxRunningTime) + { + if (is_integer($maxRunningTime) && $maxRunningTime >= 0) { + $this->maxRunningTime = $maxRunningTime; + } else { + throw new \InvalidArgumentException; + } + } + + /** + * @return integer + * @since Method available since Release 2.3.0 + */ + 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..9d51a9e --- /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/AbstractDivLayoutTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php new file mode 100644 index 0000000..ee9ed8f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php @@ -0,0 +1,732 @@ + + * + * 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\Tests\Fixtures\AlternatingRowType; + +abstract class AbstractDivLayoutTest extends AbstractLayoutTest +{ + public function testRow() + { + $form = $this->factory->createNamed('name', 'text'); + $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', 'text')->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', 'repeated'); + $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', 'button'); + $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', 'form') + ->add('field1', 'text') + ->add('field2', 'repeated') + ->add('field3', 'text') + ->add('field4', 'text') + ->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', 'form') + ->add('field1', 'text') + ->add('field2', 'text'); + + $child2 = $this->factory->createNamedBuilder('child2', 'form') + ->add('field1', 'text') + ->add('field2', 'text'); + + $view = $this->factory->createNamedBuilder('parent', 'form') + ->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', 'form') + ->add('first', 'text') + ->add('password', 'repeated') + ->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', 'form') + ->add('first', 'text') + ->add('password', 'repeated') + ->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', 'form') + ->add('first', 'text') + ->add('password', 'repeated') + ->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('name', 'collection', array('a', 'b'), array( + 'type' => 'text', + )); + + $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('name', 'collection', $data, array( + 'type' => new 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('name', 'collection', array(), array( + 'type' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [./input[@type="hidden"][@id="name__token"]] + [count(./div)=0] +' + ); + } + + public function testCollectionRow() + { + $collection = $this->factory->createNamedBuilder( + 'collection', + 'collection', + array('a', 'b'), + array('type' => 'text') + ); + + $form = $this->factory->createNamedBuilder('form', 'form') + ->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', 'form') + ->setMethod('PUT') + ->setAction('http://example.com') + ->add('firstName', 'text') + ->add('lastName', 'text') + ->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', 'form') + ->add('firstName', 'text') + ->add('lastName', 'text') + ->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', 'form') + ->add($this->factory + ->createNamedBuilder('child', 'form', null, array('error_bubbling' => false)) + ->add('grandChild', 'form') + ) + ->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->csrfProvider->expects($this->any()) + ->method('generateCsrfToken') + ->will($this->returnValue('foo&bar')); + + $form = $this->factory->createNamedBuilder('name', 'form') + ->add($this->factory + // No CSRF protection on nested forms + ->createNamedBuilder('child', 'form') + ->add($this->factory->createNamedBuilder('grandchild', 'text')) + ) + ->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', 'repeated', 'foobar', array( + 'type' => 'text', + )); + + $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', 'repeated', 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', 'form') + ->add('name', 'search') + ->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', 'text'); + $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', 'text', 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', 'email') + ->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', 'form') + ->add('field', 'text'); + + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('field', 'text') + ->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('name', 'collection', $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]"]] + ] +' + ); + } + + public function testFormEndWithRest() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->add('field1', 'text') + ->add('field2', 'text') + ->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', 'form') + ->add('field1', 'text') + ->add('field2', 'text') + ->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); + } +} 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..c16cb22 --- /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('foo')); + $this->assertFalse($loader->hasType('bar')); + } + + public function testGetType() + { + $loader = new ConcreteExtension(); + $this->assertInstanceOf('Symfony\Component\Form\Tests\Fixtures\FooType', $loader->getType('foo')); + } +} + +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..2911818 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractFormTest.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\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() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + // We need an actual dispatcher to use the deprecated + // bindRequest() method + $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 + * + * @return FormBuilder + */ + protected function getBuilder($name = 'name', EventDispatcherInterface $dispatcher = null, $dataClass = null) + { + return new FormBuilder($name, $dataClass, $dispatcher ?: $this->dispatcher, $this->factory); + } + + /** + * @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; + } + + /** + * @param string $name + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getValidForm($name) + { + $form = $this->getMockForm($name); + + $form->expects($this->any()) + ->method('isValid') + ->will($this->returnValue(true)); + + return $form; + } + + /** + * @param string $name + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getInvalidForm($name) + { + $form = $this->getMockForm($name); + + $form->expects($this->any()) + ->method('isValid') + ->will($this->returnValue(false)); + + 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..8f632a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -0,0 +1,1876 @@ + + * + * 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 $csrfProvider; + + protected function setUp() + { + if (!extension_loaded('intl')) { + $this->markTestSkipped('The "intl" extension is not available'); + } + + \Locale::setDefault('en'); + + $this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); + + parent::setUp(); + } + + protected function getExtensions() + { + return array( + new CsrfExtension($this->csrfProvider), + ); + } + + protected function tearDown() + { + $this->csrfProvider = 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)); + + $xpath = trim($xpath).' + [@id="my&id"] + [@class="my&class"]'; + + $this->assertMatchesXpath($html, $xpath); + } + + abstract protected function renderForm(FormView $view, array $vars = array()); + + abstract protected function renderEnctype(FormView $view); + + 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 testEnctype() + { + $form = $this->factory->createNamedBuilder('name', 'form') + ->add('file', 'file') + ->getForm(); + + $this->assertEquals('enctype="multipart/form-data"', $this->renderEnctype($form->createView())); + } + + public function testNoEnctype() + { + $form = $this->factory->createNamedBuilder('name', 'form') + ->add('text', 'text') + ->getForm(); + + $this->assertEquals('', $this->renderEnctype($form->createView())); + } + + public function testLabel() + { + $form = $this->factory->createNamed('name', 'text'); + $view = $form->createView(); + $this->renderWidget($view, array('label' => 'foo')); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [.="[trans]Name[/trans]"] +' + ); + } + + public function testLabelOnForm() + { + $form = $this->factory->createNamed('name', 'date'); + $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', 'text', 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', 'text'); + $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', 'text', 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', 'text'); + $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', 'text'); + $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', 'text'); + $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', 'text', 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 testErrors() + { + $form = $this->factory->createNamed('name', 'text'); + $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 testWidgetById() + { + $form = $this->factory->createNamed('text_id', 'text'); + $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', 'checkbox', true); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="checkbox"] + [@name="name"] + [@checked="checked"] + [@value="1"] +' + ); + } + + public function testUncheckedCheckbox() + { + $form = $this->factory->createNamed('name', 'checkbox', false); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="checkbox"] + [@name="name"] + [not(@checked)] +' + ); + } + + public function testCheckboxWithValue() + { + $form = $this->factory->createNamed('name', 'checkbox', 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', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [@required="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 testSingleChoiceWithPreferred() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => '-- sep --'), +'/select + [@name="name"] + [@required="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', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => null), +'/select + [@name="name"] + [@required="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', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => ''), +'/select + [@name="name"] + [@required="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', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&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', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'required' => false, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value=""][.="[trans][/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 testSingleChoiceNonRequiredNoneSelected() + { + $form = $this->factory->createNamed('name', 'choice', null, array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'required' => false, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value=""][.="[trans][/trans]"] + /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 testSingleChoiceWithNonRequiredEmptyValue() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => false, + 'expanded' => false, + 'required' => false, + 'empty_value' => '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 testSingleChoiceRequiredWithEmptyValue() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'required' => true, + 'multiple' => false, + 'expanded' => false, + 'empty_value' => 'Test&Me' + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [@required="required"] + [ + ./option[not(@value)][not(@selected)][@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 testSingleChoiceRequiredWithEmptyValueViaView() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'required' => true, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('empty_value' => ''), +'/select + [@name="name"] + [@required="required"] + [ + ./option[not(@value)][not(@selected)][@disabled][.="[trans][/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 testSingleChoiceGrouped() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array( + 'Group&1' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'Group&2' => array('&c' => 'Choice&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', 'choice', array('&a'), array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + '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 testMultipleChoiceSkipsEmptyValue() + { + $form = $this->factory->createNamed('name', 'choice', array('&a'), array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => true, + 'expanded' => false, + 'empty_value' => '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', 'choice', array('&a'), array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&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', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&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 testSingleChoiceExpandedWithEmptyValue() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => false, + 'expanded' => true, + 'empty_value' => '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 testSingleChoiceExpandedWithBooleanValue() + { + $form = $this->factory->createNamed('name', 'choice', true, array( + 'choices' => array('1' => 'Choice&A', '0' => 'Choice&B'), + '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', 'choice', array('&a', '&c'), array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&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 testCountry() + { + $form = $this->factory->createNamed('name', 'country', 'AT'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./option[@value="AT"][@selected="selected"][.="[trans]Austria[/trans]"]] + [count(./option)>200] +' + ); + } + + public function testCountryWithEmptyValue() + { + $form = $this->factory->createNamed('name', 'country', 'AT', array( + 'empty_value' => '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"][.="[trans]Austria[/trans]"]] + [count(./option)>201] +' + ); + } + + public function testDateTime() + { + $form = $this->factory->createNamed('name', 'datetime', '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 testDateTimeWithEmptyValueGlobal() + { + $form = $this->factory->createNamed('name', 'datetime', null, array( + 'input' => 'string', + 'empty_value' => '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 testDateTimeWithEmptyValueOnTime() + { + $data = array('year' => '2011', 'month' => '2', 'day' => '3', 'hour' => '', 'minute' => ''); + + $form = $this->factory->createNamed('name', 'datetime', $data, array( + 'input' => 'array', + 'empty_value' => array('hour' => 'Change&Me', 'minute' => 'Change&Me'), + '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=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_time_minute"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + ] + ] + [count(.//select)=5] +' + ); + } + + public function testDateTimeWithSeconds() + { + $form = $this->factory->createNamed('name', 'datetime', '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', 'datetime', '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', 'datetime', '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', 'datetime', '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', 'date', '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 testDateChoiceWithEmptyValueGlobal() + { + $form = $this->factory->createNamed('name', 'date', null, array( + 'input' => 'string', + 'widget' => 'choice', + 'empty_value' => '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 testDateChoiceWithEmptyValueOnYear() + { + $form = $this->factory->createNamed('name', 'date', null, array( + 'input' => 'string', + 'widget' => 'choice', + 'required' => false, + 'empty_value' => 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', 'date', '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', 'date', '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', 'form') + ->add('date', 'date') + ->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', 'birthday', '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 testBirthDayWithEmptyValue() + { + $form = $this->factory->createNamed('name', 'birthday', '1950-01-01', array( + 'input' => 'string', + 'empty_value' => '', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_month"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans][/trans]"]] + [./option[@value="1"][@selected="selected"]] + /following-sibling::select + [@id="name_day"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans][/trans]"]] + [./option[@value="1"][@selected="selected"]] + /following-sibling::select + [@id="name_year"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans][/trans]"]] + [./option[@value="1950"][@selected="selected"]] + ] + [count(./select)=3] +' + ); + } + + public function testEmail() + { + $form = $this->factory->createNamed('name', 'email', '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', 'email', 'foo&bar', array( + 'max_length' => 123, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="email"] + [@name="name"] + [@value="foo&bar"] + [@maxlength="123"] +' + ); + } + + public function testFile() + { + $form = $this->factory->createNamed('name', 'file'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="file"] +' + ); + } + + public function testHidden() + { + $form = $this->factory->createNamed('name', 'hidden', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="hidden"] + [@name="name"] + [@value="foo&bar"] +' + ); + } + + public function testReadOnly() + { + $form = $this->factory->createNamed('name', 'text', null, array( + 'read_only' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@readonly="readonly"] +' + ); + } + + public function testDisabled() + { + $form = $this->factory->createNamed('name', 'text', null, array( + 'disabled' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@disabled="disabled"] +' + ); + } + + public function testInteger() + { + $form = $this->factory->createNamed('name', 'integer', 123); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="number"] + [@name="name"] + [@value="123"] +' + ); + } + + public function testLanguage() + { + $form = $this->factory->createNamed('name', 'language', 'de'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./option[@value="de"][@selected="selected"][.="[trans]German[/trans]"]] + [count(./option)>200] +' + ); + } + + public function testLocale() + { + $form = $this->factory->createNamed('name', 'locale', 'de_AT'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./option[@value="de_AT"][@selected="selected"][.="[trans]German (Austria)[/trans]"]] + [count(./option)>200] +' + ); + } + + public function testMoney() + { + $form = $this->factory->createNamed('name', 'money', 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', 'number', 1234.56); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="1234.56"] +' + ); + } + + public function testPassword() + { + $form = $this->factory->createNamed('name', 'password', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="password"] + [@name="name"] +' + ); + } + + public function testPasswordSubmittedWithNotAlwaysEmpty() + { + $form = $this->factory->createNamed('name', 'password', 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', 'password', 'foo&bar', array( + 'max_length' => 123, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="password"] + [@name="name"] + [@maxlength="123"] +' + ); + } + + public function testPercent() + { + $form = $this->factory->createNamed('name', 'percent', 0.1); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="10"] + [contains(.., "%")] +' + ); + } + + public function testCheckedRadio() + { + $form = $this->factory->createNamed('name', 'radio', true); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="radio"] + [@name="name"] + [@checked="checked"] + [@value="1"] +' + ); + } + + public function testUncheckedRadio() + { + $form = $this->factory->createNamed('name', 'radio', false); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="radio"] + [@name="name"] + [not(@checked)] +' + ); + } + + public function testRadioWithValue() + { + $form = $this->factory->createNamed('name', 'radio', false, array( + 'value' => 'foo&bar', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="radio"] + [@name="name"] + [@value="foo&bar"] +' + ); + } + + public function testTextarea() + { + $form = $this->factory->createNamed('name', 'textarea', 'foo&bar', array( + 'pattern' => 'foo', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/textarea + [@name="name"] + [not(@pattern)] + [.="foo&bar"] +' + ); + } + + public function testText() + { + $form = $this->factory->createNamed('name', 'text', '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', 'text', 'foo&bar', array( + 'max_length' => 123, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="foo&bar"] + [@maxlength="123"] +' + ); + } + + public function testSearch() + { + $form = $this->factory->createNamed('name', 'search', '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', 'time', '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', 'time', '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', 'time', '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', 'time', '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 testTimeWithEmptyValueGlobal() + { + $form = $this->factory->createNamed('name', 'time', null, array( + 'input' => 'string', + 'empty_value' => '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 testTimeWithEmptyValueOnYear() + { + $form = $this->factory->createNamed('name', 'time', null, array( + 'input' => 'string', + 'required' => false, + 'empty_value' => 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', 'form') + ->add('time', 'time') + ->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', 'timezone', 'Europe/Vienna'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [@required="required"] + [./optgroup + [@label="[trans]Europe[/trans]"] + [./option[@value="Europe/Vienna"][@selected="selected"][.="[trans]Vienna[/trans]"]] + ] + [count(./optgroup)>10] + [count(.//option)>200] +' + ); + } + + public function testTimezoneWithEmptyValue() + { + $form = $this->factory->createNamed('name', 'timezone', null, array( + 'empty_value' => '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', 'url', $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', 'form', array('items' => array('one', 'two', 'three'))) + ->add('items', 'collection', 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('', 'form') + ->add('child', 'text') + ->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', 'button'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), + '/button[@type="button"][@name="name"][.="[trans]Name[/trans]"]' + ); + } + + public function testButtonLabelIsEmpty() + { + $form = $this->factory->createNamed('name', 'button'); + + $this->assertSame('', $this->renderLabel($form->createView())); + } + + public function testSubmit() + { + $form = $this->factory->createNamed('name', 'submit'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), + '/button[@type="submit"][@name="name"]' + ); + } + + public function testReset() + { + $form = $this->factory->createNamed('name', 'reset'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), + '/button[@type="reset"][@name="name"]' + ); + } + + public function testStartTag() + { + $form = $this->factory->create('form', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory' + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('
    ', $html); + } + + public function testStartTagForPutRequest() + { + $form = $this->factory->create('form', 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('form', 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('form', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory' + )) + ->add('file', 'file') + ->getForm(); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testStartTagWithExtraAttributes() + { + $form = $this->factory->create('form', 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/AbstractRequestHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php new file mode 100644 index 0000000..cef8f3b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php @@ -0,0 +1,280 @@ + + * + * 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 + */ +abstract class AbstractRequestHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\Form\RequestHandlerInterface + */ + protected $requestHandler; + + protected $request; + + protected function setUp() + { + $this->requestHandler = $this->getRequestHandler(); + $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 testSubmitSimpleFormWithNullIfNameNotInRequestAndNotGetRequest($method) + { + $form = $this->getMockForm('param1', $method, false); + + $this->setRequestData($method, array( + 'paramx' => array(), + )); + + $form->expects($this->once()) + ->method('submit') + ->with($this->identicalTo(null), 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testSubmitCompoundFormWithArrayIfNameNotInRequestAndNotGetRequest($method) + { + $form = $this->getMockForm('param1', $method, true); + + $this->setRequestData($method, array( + 'paramx' => array(), + )); + + $form->expects($this->once()) + ->method('submit') + ->with($this->identicalTo(array()), 'PATCH' !== $method); + + $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); + } + + abstract protected function setRequestData($method, $data, $files = array()); + + abstract protected function getRequestHandler(); + + abstract protected function getMockFile(); + + 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..5c91195 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php @@ -0,0 +1,509 @@ + + * + * 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 AbstractTableLayoutTest extends AbstractLayoutTest +{ + public function testRow() + { + $form = $this->factory->createNamed('name', 'text'); + $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', 'text', 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', 'repeated'); + $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', 'repeated'); + $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', 'button'); + $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', 'form') + ->add('field1', 'text') + ->add('field2', 'repeated') + ->add('field3', 'text') + ->add('field4', 'text') + ->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('name', 'collection', array('a', 'b'), array( + 'type' => 'text', + )); + + $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="name__token"]] + ] + [count(./tr[./td/input])=3] +' + ); + } + + public function testEmptyCollection() + { + $form = $this->factory->createNamed('name', 'collection', array(), array( + 'type' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [./tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="name__token"]]] + [count(./tr[./td/input])=1] +' + ); + } + + public function testForm() + { + $view = $this->factory->createNamedBuilder('name', 'form') + ->setMethod('PUT') + ->setAction('http://example.com') + ->add('firstName', 'text') + ->add('lastName', 'text') + ->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', 'form') + ->add('firstName', 'text') + ->add('lastName', 'text') + ->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', 'form') + ->add($this->factory + ->createNamedBuilder('child', 'form', null, array('error_bubbling' => false)) + ->add('grandChild', 'form') + ) + ->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->csrfProvider->expects($this->any()) + ->method('generateCsrfToken') + ->will($this->returnValue('foo&bar')); + + $form = $this->factory->createNamedBuilder('name', 'form') + ->add($this->factory + // No CSRF protection on nested forms + ->createNamedBuilder('child', 'form') + ->add($this->factory->createNamedBuilder('grandchild', 'text')) + ) + ->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', 'repeated', 'foobar', array( + 'type' => 'text', + )); + + $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', 'repeated', 'foobar', array( + 'type' => 'password', + '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('name', 'collection', $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', 'form') + ->add('field1', 'text') + ->add('field2', 'text') + ->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', 'form') + ->add('field1', 'text') + ->add('field2', 'text') + ->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); + } +} 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..73c602c --- /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\Tests\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('form') + ->add('firstName', 'text') + ->add('lastName', 'text') + ->add('gender', 'choice', array( + 'choices' => array('male' => 'Male', 'female' => 'Female'), + 'required' => false, + )) + ->add('age', 'number') + ->add('birthDate', 'birthday') + ->add('city', 'choice', 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..b240d2d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/CompoundFormTest.php @@ -0,0 +1,759 @@ + + * + * 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\HttpFoundation\HttpFoundationRequestHandler; +use Symfony\Component\Form\FormError; +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->getValidForm('firstName')); + $this->form->add($this->getValidForm('lastName')); + + $this->form->submit(array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + )); + + $this->assertTrue($this->form->isValid()); + } + + public function testInvalidIfChildIsInvalid() + { + $this->form->add($this->getValidForm('firstName')); + $this->form->add($this->getInvalidForm('lastName')); + + $this->form->submit(array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + )); + + $this->assertFalse($this->form->isValid()); + } + + 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 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']); + } + + 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 testValidIfSubmittedAndDisabledWithChildren() + { + $this->factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('name', 'text', null, array()) + ->will($this->returnValue($this->getBuilder('name'))); + + $form = $this->getBuilder('person') + ->setDisabled(true) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add('name', 'text') + ->getForm(); + $form->submit(array('name' => 'Jacques Doe')); + + $this->assertTrue($form->isValid()); + } + + public function testNotValidIfChildNotValid() + { + $child = $this->getMockForm(); + $child->expects($this->once()) + ->method('isValid') + ->will($this->returnValue(false)); + + $this->form->add($child); + $this->form->submit(array()); + + $this->assertFalse($this->form->isValid()); + } + + 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', 'text', null, array( + 'bar' => 'baz', + 'auto_initialize' => false, + )) + ->will($this->returnValue($child)); + + $this->form->add('foo', 'text', 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', 'text', null, array( + 'bar' => 'baz', + 'auto_initialize' => false, + )) + ->will($this->returnValue($child)); + + // in order to make casting unnecessary + $this->form->add(0, 'text', 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 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() + { + $test = $this; + $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, $test) { + $test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $test->assertSame(array($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 testSetDataMapsViewDataToChildren() + { + $test = $this; + $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, $test) { + $test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $test->assertSame(array('firstName' => $child1, 'lastName' => $child2), iterator_to_array($iterator)); + })); + + $form->setData('foo'); + } + + public function testSubmitMapsSubmittedChildrenOntoExistingViewData() + { + $test = $this; + $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, $test) { + $test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $test->assertSame(array('firstName' => $child1, 'lastName' => $child2), iterator_to_array($iterator)); + $test->assertEquals('Bernhard', $child1->getData()); + $test->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() + { + $test = $this; + $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, $test) { + $test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $test->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) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $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) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $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) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $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) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $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() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $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() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $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 testGetErrorsAsStringDeep() + { + $parent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + + $this->form->addError(new FormError('Error!')); + + $parent->add($this->form); + $parent->add($this->getBuilder('foo')->getForm()); + + $this->assertEquals("name:\n ERROR: Error!\nfoo:\n No errors\n", $parent->getErrorsAsString()); + } + + 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/ChoiceList/ChoiceListTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ChoiceListTest.php new file mode 100644 index 0000000..63eae9b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ChoiceListTest.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\Form\Tests\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class ChoiceListTest extends \PHPUnit_Framework_TestCase +{ + private $obj1; + + private $obj2; + + private $obj3; + + private $obj4; + + private $list; + + protected function setUp() + { + parent::setUp(); + + $this->obj1 = new \stdClass(); + $this->obj2 = new \stdClass(); + $this->obj3 = new \stdClass(); + $this->obj4 = new \stdClass(); + + $this->list = new ChoiceList( + array( + 'Group 1' => array($this->obj1, $this->obj2), + 'Group 2' => array($this->obj3, $this->obj4), + ), + array( + 'Group 1' => array('A', 'B'), + 'Group 2' => array('C', 'D'), + ), + array($this->obj2, $this->obj3) + ); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->obj1 = null; + $this->obj2 = null; + $this->obj3 = null; + $this->obj4 = null; + $this->list = null; + } + + public function testInitArray() + { + $this->list = new ChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + array('A', 'B', 'C', 'D'), + array($this->obj2) + ); + + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3'), $this->list->getValues()); + $this->assertEquals(array(1 => new ChoiceView($this->obj2, '1', 'B')), $this->list->getPreferredViews()); + $this->assertEquals(array(0 => new ChoiceView($this->obj1, '0', 'A'), 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D')), $this->list->getRemainingViews()); + } + + /** + * Necessary for interoperability with MongoDB cursors or ORM relations as + * choices parameter. A choice itself that is an object implementing \Traversable + * is not treated as hierarchical structure, but as-is. + */ + public function testInitNestedTraversable() + { + $traversableChoice = new \ArrayIterator(array($this->obj3, $this->obj4)); + + $this->list = new ChoiceList( + new \ArrayIterator(array( + 'Group' => array($this->obj1, $this->obj2), + 'Not a Group' => $traversableChoice + )), + array( + 'Group' => array('A', 'B'), + 'Not a Group' => 'C', + ), + array($this->obj2) + ); + + $this->assertSame(array($this->obj1, $this->obj2, $traversableChoice), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2'), $this->list->getValues()); + $this->assertEquals(array( + 'Group' => array(1 => new ChoiceView($this->obj2, '1', 'B')) + ), $this->list->getPreferredViews()); + $this->assertEquals(array( + 'Group' => array(0 => new ChoiceView($this->obj1, '0', 'A')), + 2 => new ChoiceView($traversableChoice, '2', 'C') + ), $this->list->getRemainingViews()); + } + + public function testInitNestedArray() + { + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3'), $this->list->getValues()); + $this->assertEquals(array( + 'Group 1' => array(1 => new ChoiceView($this->obj2, '1', 'B')), + 'Group 2' => array(2 => new ChoiceView($this->obj3, '2', 'C')) + ), $this->list->getPreferredViews()); + $this->assertEquals(array( + 'Group 1' => array(0 => new ChoiceView($this->obj1, '0', 'A')), + 'Group 2' => array(3 => new ChoiceView($this->obj4, '3', 'D')) + ), $this->list->getRemainingViews()); + } + + public function testGetIndicesForChoices() + { + $choices = array($this->obj2, $this->obj3); + $this->assertSame(array(1, 2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForChoicesIgnoresNonExistingChoices() + { + $choices = array($this->obj2, $this->obj3, 'foobar'); + $this->assertSame(array(1, 2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForValues() + { + // values and indices are always the same + $values = array('1', '2'); + $this->assertSame(array(1, 2), $this->list->getIndicesForValues($values)); + } + + public function testGetIndicesForValuesIgnoresNonExistingValues() + { + $values = array('1', '2', '5'); + $this->assertSame(array(1, 2), $this->list->getIndicesForValues($values)); + } + + public function testGetChoicesForValues() + { + $values = array('1', '2'); + $this->assertSame(array($this->obj2, $this->obj3), $this->list->getChoicesForValues($values)); + } + + public function testGetChoicesForValuesCorrectOrderingOfResult() + { + $values = array('2', '1'); + $this->assertSame(array($this->obj3, $this->obj2), $this->list->getChoicesForValues($values)); + } + + public function testGetChoicesForValuesIgnoresNonExistingValues() + { + $values = array('1', '2', '5'); + $this->assertSame(array($this->obj2, $this->obj3), $this->list->getChoicesForValues($values)); + } + + public function testGetValuesForChoices() + { + $choices = array($this->obj2, $this->obj3); + $this->assertSame(array('1', '2'), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesIgnoresNonExistingChoices() + { + $choices = array($this->obj2, $this->obj3, 'foobar'); + $this->assertSame(array('1', '2'), $this->list->getValuesForChoices($choices)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testNonMatchingLabels() + { + $this->list = new ChoiceList( + array($this->obj1, $this->obj2), + array('A') + ); + } + + public function testLabelsContainingNull() + { + $this->list = new ChoiceList( + array($this->obj1, $this->obj2), + array('A', null) + ); + + $this->assertEquals( + array(0 => new ChoiceView($this->obj1, '0', 'A'), 1 => new ChoiceView($this->obj2, '1', null)), + $this->list->getRemainingViews() + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/LazyChoiceListTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/LazyChoiceListTest.php new file mode 100644 index 0000000..bcd309e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/LazyChoiceListTest.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\Form\Tests\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; +use Symfony\Component\Form\Extension\Core\ChoiceList\LazyChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class LazyChoiceListTest extends \PHPUnit_Framework_TestCase +{ + private $list; + + protected function setUp() + { + parent::setUp(); + + $this->list = new LazyChoiceListTest_Impl(new SimpleChoiceList(array( + 'a' => 'A', + 'b' => 'B', + 'c' => 'C', + ), array('b'))); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->list = null; + } + + public function testGetChoices() + { + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c'), $this->list->getChoices()); + } + + public function testGetValues() + { + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c'), $this->list->getValues()); + } + + public function testGetPreferredViews() + { + $this->assertEquals(array(1 => new ChoiceView('b', 'b', 'B')), $this->list->getPreferredViews()); + } + + public function testGetRemainingViews() + { + $this->assertEquals(array(0 => new ChoiceView('a', 'a', 'A'), 2 => new ChoiceView('c', 'c', 'C')), $this->list->getRemainingViews()); + } + + public function testGetIndicesForChoices() + { + $choices = array('b', 'c'); + $this->assertSame(array(1, 2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForValues() + { + $values = array('b', 'c'); + $this->assertSame(array(1, 2), $this->list->getIndicesForValues($values)); + } + + public function testGetChoicesForValues() + { + $values = array('b', 'c'); + $this->assertSame(array('b', 'c'), $this->list->getChoicesForValues($values)); + } + + public function testGetValuesForChoices() + { + $choices = array('b', 'c'); + $this->assertSame(array('b', 'c'), $this->list->getValuesForChoices($choices)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testLoadChoiceListShouldReturnChoiceList() + { + $list = new LazyChoiceListTest_InvalidImpl(); + + $list->getChoices(); + } +} + +class LazyChoiceListTest_Impl extends LazyChoiceList +{ + private $choiceList; + + public function __construct($choiceList) + { + $this->choiceList = $choiceList; + } + + protected function loadChoiceList() + { + return $this->choiceList; + } +} + +class LazyChoiceListTest_InvalidImpl extends LazyChoiceList +{ + protected function loadChoiceList() + { + return new \stdClass(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php new file mode 100644 index 0000000..69c5aa0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.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\Form\Tests\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class ObjectChoiceListTest_EntityWithToString +{ + private $property; + + public function __construct($property) + { + $this->property = $property; + } + + public function __toString() + { + return $this->property; + } +} + +class ObjectChoiceListTest extends \PHPUnit_Framework_TestCase +{ + private $obj1; + + private $obj2; + + private $obj3; + + private $obj4; + + /** + * @var ObjectChoiceList + */ + private $list; + + protected function setUp() + { + parent::setUp(); + + $this->obj1 = (object) array('name' => 'A'); + $this->obj2 = (object) array('name' => 'B'); + $this->obj3 = (object) array('name' => 'C'); + $this->obj4 = (object) array('name' => 'D'); + + $this->list = new ObjectChoiceList( + array( + 'Group 1' => array($this->obj1, $this->obj2), + 'Group 2' => array($this->obj3, $this->obj4), + ), + 'name', + array($this->obj2, $this->obj3) + ); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->obj1 = null; + $this->obj2 = null; + $this->obj3 = null; + $this->obj4 = null; + $this->list = null; + } + + public function testInitArray() + { + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array($this->obj2) + ); + + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3'), $this->list->getValues()); + $this->assertEquals(array(1 => new ChoiceView($this->obj2, '1', 'B')), $this->list->getPreferredViews()); + $this->assertEquals(array(0 => new ChoiceView($this->obj1, '0', 'A'), 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D')), $this->list->getRemainingViews()); + } + + public function testInitNestedArray() + { + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3'), $this->list->getValues()); + $this->assertEquals(array( + 'Group 1' => array(1 => new ChoiceView($this->obj2, '1', 'B')), + 'Group 2' => array(2 => new ChoiceView($this->obj3, '2', 'C')) + ), $this->list->getPreferredViews()); + $this->assertEquals(array( + 'Group 1' => array(0 => new ChoiceView($this->obj1, '0', 'A')), + 'Group 2' => array(3 => new ChoiceView($this->obj4, '3', 'D')) + ), $this->list->getRemainingViews()); + } + + public function testInitArrayWithGroupPath() + { + $this->obj1 = (object) array('name' => 'A', 'category' => 'Group 1'); + $this->obj2 = (object) array('name' => 'B', 'category' => 'Group 1'); + $this->obj3 = (object) array('name' => 'C', 'category' => 'Group 2'); + $this->obj4 = (object) array('name' => 'D', 'category' => 'Group 2'); + + // Objects with NULL groups are not grouped + $obj5 = (object) array('name' => 'E', 'category' => null); + + // Objects without the group property are not grouped either + // see https://github.com/symfony/symfony/commit/d9b7abb7c7a0f28e0ce970afc5e305dce5dccddf + $obj6 = (object) array('name' => 'F'); + + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4, $obj5, $obj6), + 'name', + array($this->obj2, $this->obj3), + 'category' + ); + + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4, $obj5, $obj6), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3', '4', '5'), $this->list->getValues()); + $this->assertEquals(array( + 'Group 1' => array(1 => new ChoiceView($this->obj2, '1', 'B')), + 'Group 2' => array(2 => new ChoiceView($this->obj3, '2', 'C')) + ), $this->list->getPreferredViews()); + $this->assertEquals(array( + 'Group 1' => array(0 => new ChoiceView($this->obj1, '0', 'A')), + 'Group 2' => array(3 => new ChoiceView($this->obj4, '3', 'D')), + 4 => new ChoiceView($obj5, '4', 'E'), + 5 => new ChoiceView($obj6, '5', 'F'), + ), $this->list->getRemainingViews()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInitArrayWithGroupPathThrowsExceptionIfNestedArray() + { + $this->obj1 = (object) array('name' => 'A', 'category' => 'Group 1'); + $this->obj2 = (object) array('name' => 'B', 'category' => 'Group 1'); + $this->obj3 = (object) array('name' => 'C', 'category' => 'Group 2'); + $this->obj4 = (object) array('name' => 'D', 'category' => 'Group 2'); + + new ObjectChoiceList( + array( + 'Group 1' => array($this->obj1, $this->obj2), + 'Group 2' => array($this->obj3, $this->obj4), + ), + 'name', + array($this->obj2, $this->obj3), + 'category' + ); + } + + public function testInitArrayWithValuePath() + { + $this->obj1 = (object) array('name' => 'A', 'id' => 10); + $this->obj2 = (object) array('name' => 'B', 'id' => 20); + $this->obj3 = (object) array('name' => 'C', 'id' => 30); + $this->obj4 = (object) array('name' => 'D', 'id' => 40); + + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array($this->obj2, $this->obj3), + null, + 'id' + ); + + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('10', '20', '30', '40'), $this->list->getValues()); + $this->assertEquals(array(1 => new ChoiceView($this->obj2, '20', 'B'), 2 => new ChoiceView($this->obj3, '30', 'C')), $this->list->getPreferredViews()); + $this->assertEquals(array(0 => new ChoiceView($this->obj1, '10', 'A'), 3 => new ChoiceView($this->obj4, '40', 'D')), $this->list->getRemainingViews()); + } + + public function testInitArrayUsesToString() + { + $this->obj1 = new ObjectChoiceListTest_EntityWithToString('A'); + $this->obj2 = new ObjectChoiceListTest_EntityWithToString('B'); + $this->obj3 = new ObjectChoiceListTest_EntityWithToString('C'); + $this->obj4 = new ObjectChoiceListTest_EntityWithToString('D'); + + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4) + ); + + $this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices()); + $this->assertSame(array('0', '1', '2', '3'), $this->list->getValues()); + $this->assertEquals(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')), $this->list->getRemainingViews()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\StringCastException + */ + public function testInitArrayThrowsExceptionIfToStringNotFound() + { + $this->obj1 = new ObjectChoiceListTest_EntityWithToString('A'); + $this->obj2 = new ObjectChoiceListTest_EntityWithToString('B'); + $this->obj3 = (object) array('name' => 'C'); + $this->obj4 = new ObjectChoiceListTest_EntityWithToString('D'); + + new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4) + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/SimpleChoiceListTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/SimpleChoiceListTest.php new file mode 100644 index 0000000..69d27a1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/SimpleChoiceListTest.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\Form\Tests\Extension\Core\ChoiceList; + +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList; +use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class SimpleChoiceListTest extends \PHPUnit_Framework_TestCase +{ + private $list; + + private $numericList; + + protected function setUp() + { + parent::setUp(); + + $choices = array( + 'Group 1' => array('a' => 'A', 'b' => 'B'), + 'Group 2' => array('c' => 'C', 'd' => 'D'), + ); + $numericChoices = array( + 'Group 1' => array(0 => 'A', 1 => 'B'), + 'Group 2' => array(2 => 'C', 3 => 'D'), + ); + + $this->list = new SimpleChoiceList($choices, array('b', 'c')); + + // Use COPY_CHOICE strategy to test for the various associated problems + $this->numericList = new SimpleChoiceList($numericChoices, array(1, 2)); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->list = null; + $this->numericList = null; + } + + public function testInitArray() + { + $choices = array('a' => 'A', 'b' => 'B', 'c' => 'C'); + $this->list = new SimpleChoiceList($choices, array('b')); + + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c'), $this->list->getChoices()); + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c'), $this->list->getValues()); + $this->assertEquals(array(1 => new ChoiceView('b', 'b', 'B')), $this->list->getPreferredViews()); + $this->assertEquals(array(0 => new ChoiceView('a', 'a', 'A'), 2 => new ChoiceView('c', 'c', 'C')), $this->list->getRemainingViews()); + } + + public function testInitNestedArray() + { + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd'), $this->list->getChoices()); + $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd'), $this->list->getValues()); + $this->assertEquals(array( + 'Group 1' => array(1 => new ChoiceView('b', 'b', 'B')), + 'Group 2' => array(2 => new ChoiceView('c', 'c', 'C')) + ), $this->list->getPreferredViews()); + $this->assertEquals(array( + 'Group 1' => array(0 => new ChoiceView('a', 'a', 'A')), + 'Group 2' => array(3 => new ChoiceView('d', 'd', 'D')) + ), $this->list->getRemainingViews()); + } + + public function testGetIndicesForChoices() + { + $choices = array('b', 'c'); + $this->assertSame(array(1, 2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForChoicesIgnoresNonExistingChoices() + { + $choices = array('b', 'c', 'foobar'); + $this->assertSame(array(1, 2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForChoicesDealsWithNumericChoices() + { + // Pass choices as strings although they are integers + $choices = array('0', '1'); + $this->assertSame(array(0, 1), $this->numericList->getIndicesForChoices($choices)); + } + + public function testGetIndicesForValues() + { + $values = array('b', 'c'); + $this->assertSame(array(1, 2), $this->list->getIndicesForValues($values)); + } + + public function testGetIndicesForValuesIgnoresNonExistingValues() + { + $values = array('b', 'c', '100'); + $this->assertSame(array(1, 2), $this->list->getIndicesForValues($values)); + } + + public function testGetIndicesForValuesDealsWithNumericValues() + { + // Pass values as strings although they are integers + $values = array('0', '1'); + $this->assertSame(array(0, 1), $this->numericList->getIndicesForValues($values)); + } + + public function testGetChoicesForValues() + { + $values = array('b', 'c'); + $this->assertSame(array('b', 'c'), $this->list->getChoicesForValues($values)); + } + + public function testGetChoicesForValuesIgnoresNonExistingValues() + { + $values = array('b', 'c', '100'); + $this->assertSame(array('b', 'c'), $this->list->getChoicesForValues($values)); + } + + public function testGetChoicesForValuesDealsWithNumericValues() + { + // Pass values as strings although they are integers + $values = array('0', '1'); + $this->assertSame(array(0, 1), $this->numericList->getChoicesForValues($values)); + } + + public function testGetValuesForChoices() + { + $choices = array('b', 'c'); + $this->assertSame(array('b', 'c'), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesIgnoresNonExistingValues() + { + $choices = array('b', 'c', 'foobar'); + $this->assertSame(array('b', 'c'), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesDealsWithNumericValues() + { + // Pass values as strings although they are integers + $values = array('0', '1'); + + $this->assertSame(array('0', '1'), $this->numericList->getValuesForChoices($values)); + } + + /** + * @dataProvider dirtyValuesProvider + */ + public function testGetValuesForChoicesDealsWithDirtyValues($choice, $value) + { + $choices = array( + '0' => 'Zero', + '1' => 'One', + '' => 'Empty', + '1.23' => 'Float', + 'foo' => 'Foo', + 'foo10' => 'Foo 10', + ); + + // use COPY_CHOICE strategy to test the problems + $this->list = new SimpleChoiceList($choices, array()); + + $this->assertSame(array($value), $this->list->getValuesForChoices(array($choice))); + } + + public function dirtyValuesProvider() + { + return array( + array(0, '0'), + array('0', '0'), + array('1', '1'), + array(false, '0'), + array(true, '1'), + array('', ''), + array(null, ''), + array('1.23', '1.23'), + array('foo', 'foo'), + array('foo10', 'foo10'), + ); + } +} 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..ee2e335 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.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\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() + { + if (!class_exists('Symfony\Component\EventDispatcher\Event')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccess')) { + $this->markTestSkipped('The "PropertyAccess" component is not available'); + } + + $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 Boolean $synchronized + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getForm(FormConfigInterface $config, $synchronized = true) + { + $form = $this->getMockBuilder('Symfony\Component\Form\Form') + ->setConstructorArgs(array($config)) + ->setMethods(array('isSynchronized')) + ->getMock(); + + $form->expects($this->any()) + ->method('isSynchronized') + ->will($this->returnValue($synchronized)); + + return $form; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + 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 testMapDataToFormsIgnoresEmptyData() + { + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('getValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $form = $this->getForm($config); + + $this->mapper->mapDataToForms(null, array($form)); + + $this->assertNull($form->getData()); + } + + 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 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..bafe5c0 --- /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/BooleanToStringTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php new file mode 100644 index 0000000..41f8f95 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.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\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\BooleanToStringTransformer; + +class BooleanToStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + const TRUE_VALUE = '1'; + + 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)); + $this->assertNull($this->transformer->transform(null)); + } + + public function testTransformExpectsBoolean() + { + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $this->transformer->transform('1'); + } + + public function testReverseTransformExpectsString() + { + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $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..bbae062 --- /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\Extension\Core\ChoiceList\SimpleChoiceList; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; + +class ChoiceToValueTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected $transformer; + + protected function setUp() + { + $list = new SimpleChoiceList(array('' => 'A', 0 => 'B', 1 => 'C')); + $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, '0'), + array(false, '0'), + array('', ''), + ); + } + + /** + * @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', 0), + array('', null), + array(null, null), + ); + } + + /** + * @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..5729719 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.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\Extension\Core\ChoiceList\SimpleChoiceList; + +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; + +class ChoicesToValuesTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected $transformer; + + protected function setUp() + { + $list = new SimpleChoiceList(array(0 => 'A', 1 => 'B', 2 => 'C')); + $this->transformer = new ChoicesToValuesTransformer($list); + } + + protected function tearDown() + { + $this->transformer = null; + } + + public function testTransform() + { + // Value strategy in SimpleChoiceList is to copy and convert to string + $in = array(0, 1, 2); + $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(0, 1, 2); + + $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..f7722c4 --- /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('c'), $actual->format('c')); + } +} 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..4898b88 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToArrayTransformerTest.php @@ -0,0 +1,512 @@ + + * + * 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)); + } + + /** + * @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', + )); + } +} 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..cb50fc3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -0,0 +1,275 @@ + + * + * 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)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformRequiresValidDateTime() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + $transformer->transform('2010-01-01'); + } + + public function testTransformWrapsIntlErrors() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + + // HOW TO REPRODUCE? + + //$this->setExpectedException('Symfony\Component\Form\Extension\Core\DataTransformer\Transdate_formationFailedException'); + + //$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..98aeb77 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.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\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'), + )); + } + + /** + * @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)); + } + + /** + * @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..987df1d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.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\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'), + + // 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'), + ); + + // This test will fail < 5.3.9 - see https://bugs.php.net/51994 + if (version_compare(phpversion(), '5.3.9', '>=')) { + $data[] = 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 testTransformExpectsDateTime() + { + $transformer = new DateTimeToStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->transform('1234'); + } + + /** + * @dataProvider dataProvider + */ + public function testReverseTransformUsingPipe($format, $input, $output) + { + if (version_compare(phpversion(), '5.3.7', '<')) { + $this->markTestSkipped('Pipe usage requires PHP 5.3.7 or newer.'); + } + + $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..b54f0c4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToTimestampTransformerTest.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\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 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..a90fa91 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.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\IntegerToLocalizedStringTransformer; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class IntegerToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + parent::setUp(); + + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + } + + public function testReverseTransform() + { + $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() + { + $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')); + } + + /** + * @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..8b91fe1 --- /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 +{ + protected function setUp() + { + parent::setUp(); + + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + } + + public function testTransform() + { + $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() + { + $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..c58e3f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.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\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(); + + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + } + + 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) + { + \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) + { + \Locale::setDefault($locale); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $this->assertSame($to, $transformer->transform($from)); + } + + public function testTransformWithPrecision() + { + $transformer = new NumberToLocalizedStringTransformer(2); + + $this->assertEquals('1234,50', $transformer->transform(1234.5)); + $this->assertEquals('678,92', $transformer->transform(678.916)); + } + + public function testTransformWithRoundingMode() + { + $transformer = new NumberToLocalizedStringTransformer(null, null, NumberToLocalizedStringTransformer::ROUND_DOWN); + $this->assertEquals('1234,547', $transformer->transform(1234.547), '->transform() only applies rounding mode if precision set'); + + $transformer = new NumberToLocalizedStringTransformer(2, null, NumberToLocalizedStringTransformer::ROUND_DOWN); + $this->assertEquals('1234,54', $transformer->transform(1234.547), '->transform() rounding-mode works'); + + } + + /** + * @dataProvider provideTransformations + */ + public function testReverseTransform($to, $from, $locale) + { + \Locale::setDefault($locale); + + $transformer = new NumberToLocalizedStringTransformer(); + + $this->assertEquals($to, $transformer->reverseTransform($from)); + } + + /** + * @dataProvider provideTransformationsWithGrouping + */ + public function testReverseTransformWithGrouping($to, $from, $locale) + { + \Locale::setDefault($locale); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $this->assertEquals($to, $transformer->reverseTransform($from)); + } + + // https://github.com/symfony/symfony/issues/7609 + public function testReverseTransformWithGroupingAndFixedSpaces() + { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('The "mbstring" extension is required for this test.'); + } + + \Locale::setDefault('ru'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $this->assertEquals(1234.5, $transformer->reverseTransform("1\xc2\xa0234,5")); + } + + public function testReverseTransformWithGroupingButWithoutGroupSeparator() + { + $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 testDecimalSeparatorMayBeDotIfGroupingSeparatorIsNotDot() + { + \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() + { + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1.234.5'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDotWithNoGroupSep() + { + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1234.5'); + } + + public function testDecimalSeparatorMayBeDotIfGroupingSeparatorIsDotButNoGroupingUsed() + { + \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() + { + \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() + { + \Locale::setDefault('en'); + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1,234,5'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testDecimalSeparatorMayNotBeCommaIfGroupingSeparatorIsCommaWithNoGroupSep() + { + \Locale::setDefault('en'); + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1234,5'); + } + + public function testDecimalSeparatorMayBeCommaIfGroupingSeparatorIsCommaButNoGroupingUsed() + { + \Locale::setDefault('en'); + $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() + { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('The "mbstring" extension is required for this test.'); + } + + \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() + { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('The "mbstring" extension is required for this test.'); + } + + \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() + { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('The "mbstring" extension is required for this test.'); + } + + \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..104941c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.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\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(); + + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + } + + 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 testTransformWithPrecision() + { + $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 testReverseTransformWithPrecision() + { + $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..2c5298d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.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\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)); + } + + /** + * @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/FixRadioInputListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixRadioInputListenerTest.php new file mode 100644 index 0000000..a5d5c78 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixRadioInputListenerTest.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\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener; +use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; + +class FixRadioInputListenerTest extends \PHPUnit_Framework_TestCase +{ + private $choiceList; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + parent::setUp(); + + $this->choiceList = new SimpleChoiceList(array('' => 'Empty', 0 => 'A', 1 => 'B')); + } + + protected function tearDown() + { + parent::tearDown(); + + $listener = null; + } + + public function testFixRadio() + { + $data = '1'; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $listener = new FixRadioInputListener($this->choiceList, true); + $listener->preSubmit($event); + + // Indices in SimpleChoiceList are zero-based generated integers + $this->assertEquals(array(2 => '1'), $event->getData()); + } + + public function testFixZero() + { + $data = '0'; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $listener = new FixRadioInputListener($this->choiceList, true); + $listener->preSubmit($event); + + // Indices in SimpleChoiceList are zero-based generated integers + $this->assertEquals(array(1 => '0'), $event->getData()); + } + + public function testFixEmptyString() + { + $data = ''; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $listener = new FixRadioInputListener($this->choiceList, true); + $listener->preSubmit($event); + + // Indices in SimpleChoiceList are zero-based generated integers + $this->assertEquals(array(0 => ''), $event->getData()); + } + + public function testConvertEmptyStringToPlaceholderIfNotFound() + { + $list = new SimpleChoiceList(array(0 => 'A', 1 => 'B')); + + $data = ''; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $listener = new FixRadioInputListener($list, true); + $listener->preSubmit($event); + + $this->assertEquals(array('placeholder' => ''), $event->getData()); + } + + public function testDontConvertEmptyStringToPlaceholderIfNoPlaceholderUsed() + { + $list = new SimpleChoiceList(array(0 => 'A', 1 => 'B')); + + $data = ''; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $listener = new FixRadioInputListener($list, false); + $listener->preSubmit($event); + + $this->assertEquals(array(), $event->getData()); + } +} 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..2b84e4f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.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\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener; + +class FixUrlProtocolListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + } + + 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..dbd28c6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php @@ -0,0 +1,259 @@ + + * + * 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() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $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..1367b3e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.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\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() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $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]', 'max_length' => 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]', 'max_length' => 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('max_length' => '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]', 'max_length' => 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('max_length' => 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')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testPreSubmitRequiresArrayOrTraversable() + { + $data = 'no array or traversable'; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->preSubmit($event); + } + + 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()); + } +} 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..4e36893 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.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\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Core\EventListener\TrimListener; + +class TrimListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + } + + 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()); + } + + /** + * @dataProvider codePointProvider + */ + public function testTrimUtf8($chars) + { + if (!function_exists('mb_check_encoding')) { + $this->markTestSkipped('The "mb_check_encoding" function is not available'); + } + + $data = mb_convert_encoding(pack('H*', implode('', $chars)), 'UTF-8', 'UCS-2BE'); + $data = $data."ab\ncd".$data; + + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new TrimListener(); + $filter->preSubmit($event); + + $this->assertSame("ab\ncd", $event->getData(), 'TrimListener should trim character(s): '.implode(', ', $chars)); + } + + public function codePointProvider() + { + return array( + 'General category: Separator' => array(array('0020', '00A0', '1680', '180E', '2000', '2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '200A', '2028', '2029', '202F', '205F', '3000')), + 'General category: Other, control' => array(array('0009', '000A', '000B', '000C', '000D', '0085')), + //'General category: Other, format. ZERO WIDTH SPACE' => array(array('200B')), + ); + } +} 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..ef36f1c --- /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', 'form') + ->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', 'form') + ->add('child', 'form'); + $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', 'form', 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', 'form', 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', 'form') + ->add('child', $this->getTestedType()) + ->getForm() + ->createView(); + + $this->assertEquals('messages', $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/ButtonTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php new file mode 100644 index 0000000..55835e7 --- /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('button')); + } + + protected function getTestedType() + { + return 'button'; + } +} 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..c782ada --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.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\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\CallbackTransformer; + +class CheckboxTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testPassValueToView() + { + $form = $this->factory->create('checkbox', null, array('value' => 'foobar')); + $view = $form->createView(); + + $this->assertEquals('foobar', $view->vars['value']); + } + + public function testCheckedIfDataTrue() + { + $form = $this->factory->create('checkbox'); + $form->setData(true); + $view = $form->createView(); + + $this->assertTrue($view->vars['checked']); + } + + public function testCheckedIfDataTrueWithEmptyValue() + { + $form = $this->factory->create('checkbox', null, array('value' => '')); + $form->setData(true); + $view = $form->createView(); + + $this->assertTrue($view->vars['checked']); + } + + public function testNotCheckedIfDataFalse() + { + $form = $this->factory->create('checkbox'); + $form->setData(false); + $view = $form->createView(); + + $this->assertFalse($view->vars['checked']); + } + + public function testSubmitWithValueChecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => 'foobar', + )); + $form->submit('foobar'); + + $this->assertTrue($form->getData()); + $this->assertEquals('foobar', $form->getViewData()); + } + + public function testSubmitWithRandomValueChecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => 'foobar', + )); + $form->submit('krixikraxi'); + + $this->assertTrue($form->getData()); + $this->assertEquals('foobar', $form->getViewData()); + } + + public function testSubmitWithValueUnchecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => 'foobar', + )); + $form->submit(null); + + $this->assertFalse($form->getData()); + $this->assertNull($form->getViewData()); + } + + public function testSubmitWithEmptyValueChecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => '', + )); + $form->submit(''); + + $this->assertTrue($form->getData()); + $this->assertSame('', $form->getViewData()); + } + + public function testSubmitWithEmptyValueUnchecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => '', + )); + $form->submit(null); + + $this->assertFalse($form->getData()); + $this->assertNull($form->getViewData()); + } + + public function testBindWithEmptyValueAndFalseUnchecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => '', + )); + $form->bind(false); + + $this->assertFalse($form->getData()); + $this->assertNull($form->getViewData()); + } + + public function testBindWithEmptyValueAndTrueChecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => '', + )); + $form->bind(true); + + $this->assertTrue($form->getData()); + $this->assertSame('', $form->getViewData()); + } + + /** + * @dataProvider provideTransformedData + */ + public function testTransformedData($data, $expected) + { + // present a binary status field as a checkbox + $transformer = new CallbackTransformer( + function ($value) { + return 'expedited' == $value; + }, + function ($value) { + return $value ? 'expedited' : 'standard'; + } + ); + + $form = $this->builder + ->create('expedited_shipping', 'checkbox') + ->addModelTransformer($transformer) + ->getForm(); + $form->setData($data); + $view = $form->createView(); + + $this->assertEquals($expected, $view->vars['checked']); + } + + public function provideTransformedData() + { + return array( + array('expedited', true), + array('standard', 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..0685946 --- /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('choice', 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..219e818 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -0,0 +1,949 @@ + + * + * 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\Extension\Core\ChoiceList\ObjectChoiceList; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; + +class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + private $choices = array( + 'a' => 'Bernhard', + 'b' => 'Fabien', + 'c' => 'Kris', + 'd' => 'Jon', + 'e' => 'Roman', + ); + + private $numericChoices = array( + 0 => 'Bernhard', + 1 => 'Fabien', + 2 => 'Kris', + 3 => 'Jon', + 4 => 'Roman', + ); + + private $objectChoices; + + protected $groupedChoices = array( + 'Symfony' => array( + 'a' => 'Bernhard', + 'b' => 'Fabien', + 'c' => 'Kris', + ), + 'Doctrine' => array( + 'd' => 'Jon', + 'e' => 'Roman', + ) + ); + + 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 \PHPUnit_Framework_Error + */ + public function testChoicesOptionExpectsArray() + { + $this->factory->create('choice', null, array( + 'choices' => new \ArrayObject(), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testChoiceListOptionExpectsChoiceListInterface() + { + $this->factory->create('choice', null, array( + 'choice_list' => array('foo' => 'foo'), + )); + } + + public function testChoiceListAndChoicesCanBeEmpty() + { + $this->factory->create('choice'); + } + + public function testExpandedChoicesOptionsTurnIntoChildren() + { + $form = $this->factory->create('choice', 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('choice', 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('choice', 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('choice', 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('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => array( + '' => 'Empty', + 1 => 'Not empty', + ), + )); + + $this->assertFalse(isset($form['placeholder'])); + $this->assertCount(2, $form, 'Each choice should become a new field'); + } + + public function testExpandedChoicesOptionsAreFlattened() + { + $form = $this->factory->create('choice', 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 testExpandedCheckboxesAreNeverRequired() + { + $form = $this->factory->create('choice', 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('choice', 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('choice', 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('choice', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit('b'); + + $this->assertEquals('b', $form->getData()); + $this->assertEquals('b', $form->getViewData()); + } + + public function testSubmitSingleNonExpandedObjectChoices() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => false, + 'choice_list' => new ObjectChoiceList( + $this->objectChoices, + // label path + 'name', + array(), + null, + // value path + 'id' + ), + )); + + // "id" value of the second entry + $form->submit('2'); + + $this->assertEquals($this->objectChoices[1], $form->getData()); + $this->assertEquals('2', $form->getViewData()); + } + + public function testSubmitMultipleNonExpanded() + { + $form = $this->factory->create('choice', 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()); + } + + public function testSubmitMultipleNonExpandedObjectChoices() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => false, + 'choice_list' => new ObjectChoiceList( + $this->objectChoices, + // label path + 'name', + array(), + null, + // value path + 'id' + ), + )); + + $form->submit(array('2', '3')); + + $this->assertEquals(array($this->objectChoices[1], $this->objectChoices[2]), $form->getData()); + $this->assertEquals(array('2', '3'), $form->getViewData()); + } + + public function testSubmitSingleExpandedRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $form->submit('b'); + + $this->assertSame('b', $form->getData()); + $this->assertSame(array( + 0 => false, + 1 => true, + 2 => false, + 3 => false, + 4 => false, + ), $form->getViewData()); + + $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 testSubmitSingleExpandedNonRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $form->submit('b'); + + $this->assertSame('b', $form->getData()); + $this->assertSame(array( + 0 => false, + 1 => true, + 2 => false, + 3 => false, + 4 => false, + 'placeholder' => false, + ), $form->getViewData()); + + $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 testSubmitSingleExpandedRequiredNothingChecked() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertSame(array( + 0 => false, + 1 => false, + 2 => false, + 3 => false, + 4 => false, + ), $form->getViewData()); + + $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 testSubmitSingleExpandedNonRequiredNothingChecked() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertSame(array( + 0 => false, + 1 => false, + 2 => false, + 3 => false, + 4 => false, + 'placeholder' => true, + ), $form->getViewData()); + + $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()); + } + + public function testSubmitFalseToSingleExpandedRequiredDoesNotProduceExtraChildrenError() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $form->submit(false); + + $this->assertEmpty($form->getExtraData()); + $this->assertNull($form->getData()); + } + + public function testSubmitFalseToSingleExpandedNonRequiredDoesNotProduceExtraChildrenError() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $form->submit(false); + + $this->assertEmpty($form->getExtraData()); + $this->assertNull($form->getData()); + } + + public function testSubmitSingleExpandedWithEmptyChild() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'choices' => array( + '' => 'Empty', + 1 => 'Not empty', + ), + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $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('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'choice_list' => new ObjectChoiceList( + $this->objectChoices, + // label path + 'name', + array(), + null, + // value path + 'id' + ), + )); + + $form->submit('2'); + + $this->assertSame($this->objectChoices[1], $form->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[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 testSubmitSingleExpandedNumericChoices() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'choices' => $this->numericChoices, + )); + + $form->submit('1'); + + $this->assertSame(1, $form->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[0]->getViewData()); + $this->assertSame('1', $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('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => $this->choices, + )); + + $form->submit(array('a', 'c')); + + $this->assertSame(array('a', 'c'), $form->getData()); + $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 testSubmitMultipleExpandedEmpty() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => $this->choices, + )); + + $form->submit(array()); + + $this->assertSame(array(), $form->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->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 testSubmitMultipleExpandedWithEmptyChild() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => array( + '' => 'Empty', + 1 => 'Not Empty', + 2 => 'Not Empty 2', + ) + )); + + $form->submit(array('', '2')); + + $this->assertSame(array('', 2), $form->getData()); + $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('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'choice_list' => new ObjectChoiceList( + $this->objectChoices, + // label path + 'name', + array(), + null, + // value path + 'id' + ), + )); + + $form->submit(array('1', '2')); + + $this->assertSame(array($this->objectChoices[0], $this->objectChoices[1]), $form->getData()); + $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 testSubmitMultipleExpandedNumericChoices() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => $this->numericChoices, + )); + + $form->submit(array('1', '2')); + + $this->assertSame(array(1, 2), $form->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertTrue($form[1]->getData()); + $this->assertTrue($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertSame('1', $form[1]->getViewData()); + $this->assertSame('2', $form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + /* + * We need this functionality to create choice fields for Boolean types, + * e.g. false => 'No', true => 'Yes' + */ + public function testSetDataSingleNonExpandedAcceptsBoolean() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->numericChoices, + )); + + $form->setData(false); + + $this->assertFalse($form->getData()); + $this->assertEquals('0', $form->getViewData()); + } + + public function testSetDataMultipleNonExpandedAcceptsBoolean() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->numericChoices, + )); + + $form->setData(array(false, true)); + + $this->assertEquals(array(false, true), $form->getData()); + $this->assertEquals(array('0', '1'), $form->getViewData()); + } + + public function testPassRequiredToView() + { + $form = $this->factory->create('choice', null, array( + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertTrue($view->vars['required']); + } + + public function testPassNonRequiredToView() + { + $form = $this->factory->create('choice', null, array( + 'required' => false, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertFalse($view->vars['required']); + } + + public function testPassMultipleToView() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertTrue($view->vars['multiple']); + } + + public function testPassExpandedToView() + { + $form = $this->factory->create('choice', null, array( + 'expanded' => true, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertTrue($view->vars['expanded']); + } + + public function testEmptyValueIsNullByDefaultIfRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'required' => true, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertNull($view->vars['empty_value']); + } + + public function testEmptyValueIsEmptyStringByDefaultIfNotRequired() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'required' => false, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertSame('', $view->vars['empty_value']); + } + + /** + * @dataProvider getOptionsWithEmptyValue + */ + public function testPassEmptyValueToView($multiple, $expanded, $required, $emptyValue, $viewValue) + { + $form = $this->factory->create('choice', null, array( + 'multiple' => $multiple, + 'expanded' => $expanded, + 'required' => $required, + 'empty_value' => $emptyValue, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertEquals($viewValue, $view->vars['empty_value']); + } + + /** + * @dataProvider getOptionsWithEmptyValue + */ + public function testDontPassEmptyValueIfContainedInChoices($multiple, $expanded, $required, $emptyValue, $viewValue) + { + $form = $this->factory->create('choice', null, array( + 'multiple' => $multiple, + 'expanded' => $expanded, + 'required' => $required, + 'empty_value' => $emptyValue, + 'choices' => array('a' => 'A', '' => 'Empty'), + )); + $view = $form->createView(); + + $this->assertNull($view->vars['empty_value']); + } + + public function getOptionsWithEmptyValue() + { + 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('choice', 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('choice', 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('choice', null, array( + 'choices' => $this->groupedChoices, + 'preferred_choices' => array('b', 'd'), + )); + $view = $form->createView(); + + $this->assertEquals(array( + 'Symfony' => array( + 0 => new ChoiceView('a', 'a', 'Bernhard'), + 2 => new ChoiceView('c', 'c', 'Kris'), + ), + 'Doctrine' => array( + 4 => new ChoiceView('e', 'e', 'Roman'), + ), + ), $view->vars['choices']); + $this->assertEquals(array( + 'Symfony' => array( + 1 => new ChoiceView('b', 'b', 'Fabien'), + ), + '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('choice', null, array( + 'choice_list' => new ObjectChoiceList(array($obj1, $obj2, $obj3, $obj4), 'label', array(), null, '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', 'choice', 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', 'choice', 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('choice', null, array( + 'choice_list' => new ObjectChoiceList(array($obj1, $obj2, $obj3, $obj4), 'label', array(), null, '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..be3ad9d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.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\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Form; + +class CollectionTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testContainsNoChildByDefault() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + )); + + $this->assertCount(0, $form); + } + + public function testSetDataAdjustsSize() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + 'options' => array( + 'max_length' => 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()); + $this->assertEquals(20, $form[0]->getConfig()->getOption('max_length')); + $this->assertEquals(20, $form[1]->getConfig()->getOption('max_length')); + + $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()); + $this->assertEquals(20, $form[0]->getConfig()->getOption('max_length')); + } + + public function testThrowsExceptionIfObjectIsNotTraversable() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + )); + $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException'); + $form->setData(new \stdClass()); + } + + public function testNotResizedIfSubmittedWithMissingData() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + )); + $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('collection', null, array( + 'type' => 'text', + '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 testNotResizedIfSubmittedWithExtraData() + { + $form = $this->factory->create('collection', null, array( + 'type' => 'text', + )); + $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('collection', null, array( + 'type' => 'text', + '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('collection', null, array( + 'type' => 'form', + 'allow_add' => true, + 'prototype' => false, + )); + + $this->assertFalse($form->has('__name__')); + } + + public function testPrototypeMultipartPropagation() + { + $form = $this->factory + ->create('collection', null, array( + 'type' => 'file', + 'allow_add' => true, + 'prototype' => true, + )) + ; + + $this->assertTrue($form->createView()->vars['multipart']); + } + + public function testGetDataDoesNotContainsPrototypeNameBeforeDataAreSet() + { + $form = $this->factory->create('collection', array(), array( + 'type' => 'file', + 'prototype' => true, + 'allow_add' => true, + )); + + $data = $form->getData(); + $this->assertFalse(isset($data['__name__'])); + } + + public function testGetDataDoesNotContainsPrototypeNameAfterDataAreSet() + { + $form = $this->factory->create('collection', array(), array( + 'type' => 'file', + '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('collection', null, array( + 'type' => 'form', + 'prototype' => true, + 'allow_add' => true, + )); + + $this->assertSame('__name__', $form->getConfig()->getAttribute('prototype')->getName(), '__name__ is the default'); + + $form = $this->factory->create('collection', null, array( + 'type' => 'form', + 'prototype' => true, + 'allow_add' => true, + 'prototype_name' => '__test__', + )); + + $this->assertSame('__test__', $form->getConfig()->getAttribute('prototype')->getName()); + } + + public function testPrototypeDefaultLabel() + { + $form = $this->factory->create('collection', array(), array( + 'type' => 'file', + 'allow_add' => true, + 'prototype' => true, + 'prototype_name' => '__test__', + )); + + $this->assertSame('__test__label__', $form->createView()->vars['prototype']->vars['label']); + } +} 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..1d56e2a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.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\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class CountryTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testCountriesAreSelectable() + { + $form = $this->factory->create('country'); + $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('country', 'country'); + $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..b0eb6dc --- /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\Extension\Core\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class CurrencyTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testCurrenciesAreSelectable() + { + $form = $this->factory->create('currency'); + $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..b9c1eba --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php @@ -0,0 +1,477 @@ + + * + * 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\Intl\Util\IntlTestHelper; + +class DateTimeTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testSubmitDateTime() + { + $form = $this->factory->create('datetime', 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('datetime', 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('datetime', 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('datetime', 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('datetime', 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('datetime', 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('datetime', 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('datetime', 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('datetime', 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('datetime', 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()); + } + + // Bug fix + public function testInitializeWithDateTime() + { + // Throws an exception if "data_class" option is not explicitly set + // to null in the type + $this->factory->create('datetime', new \DateTime()); + } + + public function testSingleTextWidgetShouldUseTheRightInputType() + { + $form = $this->factory->create('datetime', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertEquals('datetime', $view->vars['type']); + } + + public function testPassDefaultEmptyValueToViewIfNotRequired() + { + $form = $this->factory->create('datetime', null, array( + 'required' => false, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('', $view['date']['year']->vars['empty_value']); + $this->assertSame('', $view['date']['month']->vars['empty_value']); + $this->assertSame('', $view['date']['day']->vars['empty_value']); + $this->assertSame('', $view['time']['hour']->vars['empty_value']); + $this->assertSame('', $view['time']['minute']->vars['empty_value']); + $this->assertSame('', $view['time']['second']->vars['empty_value']); + } + + public function testPassNoEmptyValueToViewIfRequired() + { + $form = $this->factory->create('datetime', null, array( + 'required' => true, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertNull($view['date']['year']->vars['empty_value']); + $this->assertNull($view['date']['month']->vars['empty_value']); + $this->assertNull($view['date']['day']->vars['empty_value']); + $this->assertNull($view['time']['hour']->vars['empty_value']); + $this->assertNull($view['time']['minute']->vars['empty_value']); + $this->assertNull($view['time']['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsString() + { + $form = $this->factory->create('datetime', null, array( + 'empty_value' => 'Empty', + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty', $view['date']['year']->vars['empty_value']); + $this->assertSame('Empty', $view['date']['month']->vars['empty_value']); + $this->assertSame('Empty', $view['date']['day']->vars['empty_value']); + $this->assertSame('Empty', $view['time']['hour']->vars['empty_value']); + $this->assertSame('Empty', $view['time']['minute']->vars['empty_value']); + $this->assertSame('Empty', $view['time']['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsArray() + { + $form = $this->factory->create('datetime', null, array( + 'empty_value' => 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['empty_value']); + $this->assertSame('Empty month', $view['date']['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['date']['day']->vars['empty_value']); + $this->assertSame('Empty hour', $view['time']['hour']->vars['empty_value']); + $this->assertSame('Empty minute', $view['time']['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['time']['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddEmptyIfNotRequired() + { + $form = $this->factory->create('datetime', null, array( + 'required' => false, + 'empty_value' => 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['empty_value']); + $this->assertSame('', $view['date']['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['date']['day']->vars['empty_value']); + $this->assertSame('Empty hour', $view['time']['hour']->vars['empty_value']); + $this->assertSame('', $view['time']['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['time']['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddNullIfRequired() + { + $form = $this->factory->create('datetime', null, array( + 'required' => true, + 'empty_value' => 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['empty_value']); + $this->assertNull($view['date']['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['date']['day']->vars['empty_value']); + $this->assertSame('Empty hour', $view['time']['hour']->vars['empty_value']); + $this->assertNull($view['time']['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['time']['second']->vars['empty_value']); + } + + public function testPassHtml5TypeIfSingleTextAndHtml5Format() + { + $form = $this->factory->create('datetime', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertSame('datetime', $view->vars['type']); + } + + public function testDontPassHtml5TypeIfNotHtml5Format() + { + $form = $this->factory->create('datetime', 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('datetime', null, array( + 'widget' => 'text', + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function testDateTypeChoiceErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('datetime', null); + + $form['date']->addError($error); + + $this->assertSame(array(), $form['date']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + public function testDateTypeSingleTextErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('datetime', null, array( + 'date_widget' => 'single_text' + )); + + $form['date']->addError($error); + + $this->assertSame(array(), $form['date']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + public function testTimeTypeChoiceErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('datetime', null); + + $form['time']->addError($error); + + $this->assertSame(array(), $form['time']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + public function testTimeTypeSingleTextErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('datetime', null, array( + 'time_widget' => 'single_text' + )); + + $form['time']->addError($error); + + $this->assertSame(array(), $form['time']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + +} 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..da6b465 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -0,0 +1,758 @@ + + * + * 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\Extension\Core\View\ChoiceView; +use Symfony\Component\Form\FormError; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class DateTypeTest extends TypeTestCase +{ + protected function setUp() + { + parent::setUp(); + + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testInvalidWidgetOption() + { + $this->factory->create('date', null, array( + 'widget' => 'fake_widget', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testInvalidInputOption() + { + $this->factory->create('date', null, array( + 'input' => 'fake_input', + )); + } + + public function testSubmitFromSingleTextDateTimeWithDefaultFormat() + { + $form = $this->factory->create('date', 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() + { + $form = $this->factory->create('date', 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() + { + $form = $this->factory->create('date', 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() + { + $form = $this->factory->create('date', 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() + { + $form = $this->factory->create('date', 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('date', 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('date', 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('date', 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('date', 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('date', 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('date', 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('date', 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()); + } + + /** + * This test is to check that the strings '0', '1', '2', '3' are no accepted + * as valid IntlDateFormatter constants for FULL, LONG, MEDIUM or SHORT respectively. + * + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatIsNoPattern() + { + $this->factory->create('date', null, array( + 'format' => '0', + 'widget' => 'single_text', + 'input' => 'string', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatDoesNotContainYearMonthAndDay() + { + $this->factory->create('date', null, array( + 'months' => array(6, 7), + 'format' => 'yy', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatIsNoConstant() + { + $this->factory->create('date', null, array( + 'format' => 105, + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatIsInvalid() + { + $this->factory->create('date', null, array( + 'format' => array(), + )); + } + + public function testSetDataWithDifferentTimezones() + { + $form = $this->factory->create('date', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Pacific/Tahiti', + 'input' => 'string', + 'widget' => 'single_text', + )); + + $form->setData('2010-06-02'); + + $this->assertEquals('01.06.2010', $form->getViewData()); + } + + public function testSetDataWithDifferentTimezonesDateTime() + { + $form = $this->factory->create('date', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Pacific/Tahiti', + 'input' => 'datetime', + 'widget' => 'single_text', + )); + + $dateTime = new \DateTime('2010-06-02 America/New_York'); + + $form->setData($dateTime); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + $this->assertEquals('01.06.2010', $form->getViewData()); + } + + public function testYearsOption() + { + $form = $this->factory->create('date', 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('date', null, array( + 'months' => array(6, 7), + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('6', '6', '06'), + new ChoiceView('7', '7', '07'), + ), $view['month']->vars['choices']); + } + + public function testMonthsOptionShortFormat() + { + $form = $this->factory->create('date', 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() + { + $form = $this->factory->create('date', 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() + { + $form = $this->factory->create('date', 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('date', 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('date', 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('date', 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('date', 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('date', 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() + { + $form = $this->factory->create('date'); + $view = $form->createView(); + + $this->assertSame('{{ day }}{{ month }}{{ year }}', $view->vars['date_pattern']); + } + + public function testPassDatePatternToViewDifferentFormat() + { + $form = $this->factory->create('date', null, array( + 'format' => \IntlDateFormatter::LONG, + )); + + $view = $form->createView(); + + $this->assertSame('{{ day }}{{ month }}{{ year }}', $view->vars['date_pattern']); + } + + public function testPassDatePatternToViewDifferentPattern() + { + $form = $this->factory->create('date', null, array( + 'format' => 'MMyyyydd' + )); + + $view = $form->createView(); + + $this->assertSame('{{ month }}{{ year }}{{ day }}', $view->vars['date_pattern']); + } + + public function testPassDatePatternToViewDifferentPatternWithSeparators() + { + $form = $this->factory->create('date', 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('date', null, array( + 'widget' => 'single_text', + )); + $view = $form->createView(); + + $this->assertFalse(isset($view->vars['date_pattern'])); + } + + public function testPassWidgetToView() + { + $form = $this->factory->create('date', null, array( + 'widget' => 'single_text', + )); + $view = $form->createView(); + + $this->assertSame('single_text', $view->vars['widget']); + } + + // Bug fix + public function testInitializeWithDateTime() + { + // Throws an exception if "data_class" option is not explicitly set + // to null in the type + $this->factory->create('date', new \DateTime()); + } + + public function testSingleTextWidgetShouldUseTheRightInputType() + { + $form = $this->factory->create('date', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertEquals('date', $view->vars['type']); + } + + public function testPassDefaultEmptyValueToViewIfNotRequired() + { + $form = $this->factory->create('date', null, array( + 'required' => false, + )); + + $view = $form->createView(); + $this->assertSame('', $view['year']->vars['empty_value']); + $this->assertSame('', $view['month']->vars['empty_value']); + $this->assertSame('', $view['day']->vars['empty_value']); + } + + public function testPassNoEmptyValueToViewIfRequired() + { + $form = $this->factory->create('date', null, array( + 'required' => true, + )); + + $view = $form->createView(); + $this->assertNull($view['year']->vars['empty_value']); + $this->assertNull($view['month']->vars['empty_value']); + $this->assertNull($view['day']->vars['empty_value']); + } + + public function testPassEmptyValueAsString() + { + $form = $this->factory->create('date', null, array( + 'empty_value' => 'Empty', + )); + + $view = $form->createView(); + $this->assertSame('Empty', $view['year']->vars['empty_value']); + $this->assertSame('Empty', $view['month']->vars['empty_value']); + $this->assertSame('Empty', $view['day']->vars['empty_value']); + } + + public function testPassEmptyValueAsArray() + { + $form = $this->factory->create('date', null, array( + 'empty_value' => array( + 'year' => 'Empty year', + 'month' => 'Empty month', + 'day' => 'Empty day', + ), + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['year']->vars['empty_value']); + $this->assertSame('Empty month', $view['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['day']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddEmptyIfNotRequired() + { + $form = $this->factory->create('date', null, array( + 'required' => false, + 'empty_value' => array( + 'year' => 'Empty year', + 'day' => 'Empty day', + ), + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['year']->vars['empty_value']); + $this->assertSame('', $view['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['day']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddNullIfRequired() + { + $form = $this->factory->create('date', null, array( + 'required' => true, + 'empty_value' => array( + 'year' => 'Empty year', + 'day' => 'Empty day', + ), + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['year']->vars['empty_value']); + $this->assertNull($view['month']->vars['empty_value']); + $this->assertSame('Empty day', $view['day']->vars['empty_value']); + } + + public function testPassHtml5TypeIfSingleTextAndHtml5Format() + { + $form = $this->factory->create('date', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertSame('date', $view->vars['type']); + } + + public function testDontPassHtml5TypeIfNotHtml5Format() + { + $form = $this->factory->create('date', null, array( + 'widget' => 'single_text', + 'format' => \IntlDateFormatter::MEDIUM, + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function testDontPassHtml5TypeIfNotSingleText() + { + $form = $this->factory->create('date', 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('date', null, array( + 'widget' => $widget, + )); + $form['year']->addError($error); + + $this->assertSame(array(), $form['year']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testMonthErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('date', null, array( + 'widget' => $widget, + )); + $form['month']->addError($error); + + $this->assertSame(array(), $form['month']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testDayErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('date', null, array( + 'widget' => $widget, + )); + $form['day']->addError($error); + + $this->assertSame(array(), $form['day']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } +} 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..63556eb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.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\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('file')->getForm(); + $data = $this->createUploadedFileMock('abcdef', 'original.jpg', true); + + $form->setData($data); + + $this->assertSame($data, $form->getData()); + } + + public function testSubmit() + { + $form = $this->factory->createBuilder('file')->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('file')->getForm(); + + $form->submit(null); + + $this->assertNull($form->getData()); + } + + public function testDontPassValueToView() + { + $form = $this->factory->create('file'); + $form->submit(array( + 'file' => $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') + ->disableOriginalConstructor() + ->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..cced82f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php @@ -0,0 +1,578 @@ + + * + * 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\Form; +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('form')); + } + + public function testPassRequiredAsOption() + { + $form = $this->factory->create('form', null, array('required' => false)); + + $this->assertFalse($form->isRequired()); + + $form = $this->factory->create('form', null, array('required' => true)); + + $this->assertTrue($form->isRequired()); + } + + public function testSubmittedDataIsTrimmedBeforeTransforming() + { + $form = $this->factory->createBuilder('form') + ->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('form', 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', 'form', null, array('read_only' => true)) + ->add('child', 'form') + ->getForm() + ->createView(); + + $this->assertTrue($view['child']->vars['read_only']); + } + + public function testReadOnlyFormWithNonReadOnlyParentIsReadOnly() + { + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', 'form', array('read_only' => true)) + ->getForm() + ->createView(); + + $this->assertTrue($view['child']->vars['read_only']); + } + + public function testNonReadOnlyFormWithNonReadOnlyParentIsNotReadOnly() + { + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', 'form') + ->getForm() + ->createView(); + + $this->assertFalse($view['child']->vars['read_only']); + } + + public function testPassMaxLengthToView() + { + $form = $this->factory->create('form', null, array('max_length' => 10)); + $view = $form->createView(); + + $this->assertSame(10, $view->vars['max_length']); + } + + public function testSubmitWithEmptyDataCreatesObjectIfClassAvailable() + { + $builder = $this->factory->createBuilder('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => false, + )); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $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('form', null, array( + // data class is inferred from the passed object + 'data' => new Author(), + 'required' => false, + )); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $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('form', null, array( + 'data_class' => null, + 'required' => false, + )); + $builder->add('firstName', 'text'); + $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('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => false, + )); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $form = $builder->getForm(); + + $form->setData(null); + $form->submit(array('firstName' => '', 'lastName' => '')); + + $this->assertNull($form->getData()); + } + + public function testSubmitEmptyWithEmptyDataCreatesObjectIfRequired() + { + $builder = $this->factory->createBuilder('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => true, + )); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $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('form') + ->add('firstName', 'text') + ->getForm(); + + $form->setData(null); + $form->submit(array('firstName' => 'Bernhard')); + + $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); + } + + public function testSubmitWithEmptyDataPassesEmptyStringToTransformerIfNotCompound() + { + $form = $this->factory->createBuilder('form') + ->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('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'empty_data' => $author, + )); + $builder->add('firstName', 'text'); + $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('form', 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('form', null, array('attr' => '')); + } + + public function testNameCanBeEmptyString() + { + $form = $this->factory->createNamed('', 'form'); + + $this->assertEquals('', $form->getName()); + } + + public function testSubformDoesntCallSetters() + { + $author = new FormTest_AuthorWithoutRefSetter(new Author()); + + $builder = $this->factory->createBuilder('form', $author); + $builder->add('reference', 'form', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + )); + $builder->get('reference')->add('firstName', 'text'); + $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('form', $author); + $builder->add('referenceCopy', 'form', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + )); + $builder->get('referenceCopy')->add('firstName', 'text'); + $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('form', $author); + $builder->add('referenceCopy', 'form', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'by_reference' => false + )); + $builder->get('referenceCopy')->add('firstName', 'text'); + $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('form', $author); + $builder->add('referenceCopy', 'form'); + $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('form'); + $builder->setData($author); + $builder->add('referenceCopy', 'form'); + $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('form') + ->add('foo', 'text') + ->add('bar', 'file') + ->getForm() + ->createView(); + + $this->assertTrue($view->vars['multipart']); + } + + public function testViewIsNotRenderedByDefault() + { + $view = $this->factory->createBuilder('form') + ->add('foo', 'form') + ->getForm() + ->createView(); + + $this->assertFalse($view->isRendered()); + } + + public function testErrorBubblingIfCompound() + { + $form = $this->factory->create('form', null, array( + 'compound' => true, + )); + + $this->assertTrue($form->getConfig()->getErrorBubbling()); + } + + public function testNoErrorBubblingIfNotCompound() + { + $form = $this->factory->create('form', null, array( + 'compound' => false, + )); + + $this->assertFalse($form->getConfig()->getErrorBubbling()); + } + + public function testOverrideErrorBubbling() + { + $form = $this->factory->create('form', null, array( + 'compound' => false, + 'error_bubbling' => true, + )); + + $this->assertTrue($form->getConfig()->getErrorBubbling()); + } + + public function testPropertyPath() + { + $form = $this->factory->create('form', 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', 'form', null, array( + 'property_path' => null, + )); + + $this->assertEquals(new PropertyPath('name'), $form->getPropertyPath()); + $this->assertTrue($form->getConfig()->getMapped()); + } + + public function testNotMapped() + { + $form = $this->factory->create('form', 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('form'); + $view = $form->createView(); + $this->assertTrue($view->vars['valid']); + } + + public function testViewNotValidSubmitted() + { + $form = $this->factory->create('form'); + $form->submit(array()); + $form->addError(new FormError('An error')); + $view = $form->createView(); + $this->assertFalse($view->vars['valid']); + } + + public function testDataOptionSupersedesSetDataCalls() + { + $form = $this->factory->create('form', null, array( + 'data' => 'default', + 'compound' => false, + )); + + $form->setData('foobar'); + + $this->assertSame('default', $form->getData()); + } + + public function testNormDataIsPassedToView() + { + $view = $this->factory->createBuilder('form') + ->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('form', null, array( + 'label' => '0' + )) + ->createView(); + + $this->assertSame('0', $view->vars['label']); + } + + protected function getTestedType() + { + return 'form'; + } +} 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..998bbe0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.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\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Intl\Util\IntlTestHelper; + +class IntegerTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testSubmitCastsToInteger() + { + $form = $this->factory->create('integer'); + + $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..24434b2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.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\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class LanguageTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testCountriesAreSelectable() + { + $form = $this->factory->create('language'); + $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', 'U.S. 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('language', 'language'); + $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..e402cb4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.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\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class LocaleTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testLocalesAreSelectable() + { + $form = $this->factory->create('locale'); + $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..97fc37f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.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\Extension\Core\Type; + +use Symfony\Component\Intl\Util\IntlTestHelper; + +class MoneyTypeTest extends TypeTestCase +{ + 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('money'); + $view = $form->createView(); + + $this->assertSame('{{ widget }} €', $view->vars['money_pattern']); + } + + public function testMoneyPatternWorksForYen() + { + \Locale::setDefault('en_US'); + + $form = $this->factory->create('money', null, array('currency' => 'JPY')); + $view = $form->createView(); + $this->assertTrue((Boolean) strstr($view->vars['money_pattern'], '¥')); + } + + // https://github.com/symfony/symfony/issues/5458 + public function testPassDifferentPatternsForDifferentCurrencies() + { + \Locale::setDefault('de_DE'); + + $form1 = $this->factory->create('money', null, array('currency' => 'GBP')); + $form2 = $this->factory->create('money', 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..e5b3dd7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.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\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Intl\Util\IntlTestHelper; + +class NumberTypeTest extends TypeTestCase +{ + 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('number'); + $form->setData('12345.67890'); + $view = $form->createView(); + + $this->assertSame('12345,679', $view->vars['value']); + } + + public function testDefaultFormattingWithGrouping() + { + $form = $this->factory->create('number', null, array('grouping' => true)); + $form->setData('12345.67890'); + $view = $form->createView(); + + $this->assertSame('12.345,679', $view->vars['value']); + } + + public function testDefaultFormattingWithPrecision() + { + $form = $this->factory->create('number', null, array('precision' => 2)); + $form->setData('12345.67890'); + $view = $form->createView(); + + $this->assertSame('12345,68', $view->vars['value']); + } + + public function testDefaultFormattingWithRounding() + { + $form = $this->factory->create('number', null, array('precision' => 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..bccb6f7 --- /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('password'); + $form->setData('pAs5w0rd'); + $view = $form->createView(); + + $this->assertSame('', $view->vars['value']); + } + + public function testEmptyIfSubmitted() + { + $form = $this->factory->create('password'); + $form->submit('pAs5w0rd'); + $view = $form->createView(); + + $this->assertSame('', $view->vars['value']); + } + + public function testNotEmptyIfSubmittedAndNotAlwaysEmpty() + { + $form = $this->factory->create('password', null, array('always_empty' => false)); + $form->submit('pAs5w0rd'); + $view = $form->createView(); + + $this->assertSame('pAs5w0rd', $view->vars['value']); + } + + public function testNotTrimmed() + { + $form = $this->factory->create('password', 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..9e125d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.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\Type; + +class RepeatedTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + protected $form; + + protected function setUp() + { + parent::setUp(); + + $this->form = $this->factory->create('repeated', null, array( + 'type' => 'text', + )); + $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('repeated', null, array( + 'type' => 'text', + '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('repeated', null, array( + // the global required value cannot be overridden + 'type' => 'text', + '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('repeated', null, array( + 'required' => false, + 'type' => 'text', + )); + + $this->assertFalse($form['first']->isRequired()); + $this->assertFalse($form['second']->isRequired()); + } + + public function testSetErrorBubblingToTrue() + { + $form = $this->factory->create('repeated', 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('repeated', 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('repeated', 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('repeated', null, array( + 'type' => 'text', + '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..8cc7228 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/SubmitTypeTest.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\Type; + +/** + * @author Bernhard Schussek + */ +class SubmitTypeTest extends TypeTestCase +{ + public function testCreateSubmitButtonInstances() + { + $this->assertInstanceOf('Symfony\Component\Form\SubmitButton', $this->factory->create('submit')); + } + + public function testNotClickedByDefault() + { + $button = $this->factory->create('submit'); + + $this->assertFalse($button->isClicked()); + } + + public function testNotClickedIfSubmittedWithNull() + { + $button = $this->factory->create('submit'); + $button->submit(null); + + $this->assertFalse($button->isClicked()); + } + + public function testClickedIfSubmittedWithEmptyString() + { + $button = $this->factory->create('submit'); + $button->submit(''); + + $this->assertTrue($button->isClicked()); + } + + public function testClickedIfSubmittedWithUnemptyString() + { + $button = $this->factory->create('submit'); + $button->submit('foo'); + + $this->assertTrue($button->isClicked()); + } +} 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..9bdfe15 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.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 Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Form\FormError; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class TimeTypeTest extends TypeTestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testSubmitDateTime() + { + $form = $this->factory->create('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', 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('time', null, array( + 'widget' => 'choice', + 'with_seconds' => true, + )); + + $form->submit(array( + 'hour' => '0', + 'minute' => '0', + 'second' => '', + )); + + $this->assertTrue($form->isPartiallyFilled()); + } + + // Bug fix + public function testInitializeWithDateTime() + { + // Throws an exception if "data_class" option is not explicitly set + // to null in the type + $this->factory->create('time', new \DateTime()); + } + + public function testSingleTextWidgetShouldUseTheRightInputType() + { + $form = $this->factory->create('time', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertEquals('time', $view->vars['type']); + } + + public function testPassDefaultEmptyValueToViewIfNotRequired() + { + $form = $this->factory->create('time', null, array( + 'required' => false, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('', $view['hour']->vars['empty_value']); + $this->assertSame('', $view['minute']->vars['empty_value']); + $this->assertSame('', $view['second']->vars['empty_value']); + } + + public function testPassNoEmptyValueToViewIfRequired() + { + $form = $this->factory->create('time', null, array( + 'required' => true, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertNull($view['hour']->vars['empty_value']); + $this->assertNull($view['minute']->vars['empty_value']); + $this->assertNull($view['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsString() + { + $form = $this->factory->create('time', null, array( + 'empty_value' => 'Empty', + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty', $view['hour']->vars['empty_value']); + $this->assertSame('Empty', $view['minute']->vars['empty_value']); + $this->assertSame('Empty', $view['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsArray() + { + $form = $this->factory->create('time', null, array( + 'empty_value' => array( + 'hour' => 'Empty hour', + 'minute' => 'Empty minute', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty hour', $view['hour']->vars['empty_value']); + $this->assertSame('Empty minute', $view['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddEmptyIfNotRequired() + { + $form = $this->factory->create('time', null, array( + 'required' => false, + 'empty_value' => array( + 'hour' => 'Empty hour', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty hour', $view['hour']->vars['empty_value']); + $this->assertSame('', $view['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['second']->vars['empty_value']); + } + + public function testPassEmptyValueAsPartialArrayAddNullIfRequired() + { + $form = $this->factory->create('time', null, array( + 'required' => true, + 'empty_value' => array( + 'hour' => 'Empty hour', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty hour', $view['hour']->vars['empty_value']); + $this->assertNull($view['minute']->vars['empty_value']); + $this->assertSame('Empty second', $view['second']->vars['empty_value']); + } + + public function provideCompoundWidgets() + { + return array( + array('text'), + array('choice'), + ); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testHourErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('time', null, array( + 'widget' => $widget, + )); + $form['hour']->addError($error); + + $this->assertSame(array(), $form['hour']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testMinuteErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('time', null, array( + 'widget' => $widget, + )); + $form['minute']->addError($error); + + $this->assertSame(array(), $form['minute']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testSecondErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('time', null, array( + 'widget' => $widget, + 'with_seconds' => true, + )); + $form['second']->addError($error); + + $this->assertSame(array(), $form['second']->getErrors()); + $this->assertSame(array($error), $form->getErrors()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidConfigurationException + */ + public function testInitializeWithSecondsAndWithoutMinutes() + { + $this->factory->create('time', null, array( + 'with_minutes' => false, + 'with_seconds' => true, + )); + } +} 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..81df20c --- /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\Extension\Core\View\ChoiceView; + +class TimezoneTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testTimezonesAreSelectable() + { + $form = $this->factory->create('timezone'); + $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/TypeTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.php new file mode 100644 index 0000000..733546e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.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\Extension\Core\Type; + +use Symfony\Component\Form\Test\TypeTestCase as BaseTypeTestCase; + +/** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use Symfony\Component\Form\Test\TypeTestCase instead. + */ +abstract class TypeTestCase extends BaseTypeTestCase +{ +} 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..254b2a8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.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\Form\Tests\Extension\Core\Type; + +class UrlTypeTest extends TypeTestCase +{ + public function testSubmitAddsDefaultProtocolIfNoneIsIncluded() + { + $form = $this->factory->create('url', '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('url', 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('url', null, array( + 'default_protocol' => 'http', + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertSame('', $form->getViewData()); + } + + public function testSubmitAddsNoDefaultProtocolIfSetToNull() + { + $form = $this->factory->create('url', null, array( + 'default_protocol' => null, + )); + + $form->submit('www.domain.com'); + + $this->assertSame('www.domain.com', $form->getData()); + $this->assertSame('www.domain.com', $form->getViewData()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/DefaultCsrfProviderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/DefaultCsrfProviderTest.php new file mode 100644 index 0000000..a99b544 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/DefaultCsrfProviderTest.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\Form\Tests\Extension\Csrf\CsrfProvider; + +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; + +/** + * @runTestsInSeparateProcesses + */ +class DefaultCsrfProviderTest extends \PHPUnit_Framework_TestCase +{ + protected $provider; + + public static function setUpBeforeClass() + { + ini_set('session.save_handler', 'files'); + ini_set('session.save_path', sys_get_temp_dir()); + } + + protected function setUp() + { + $this->provider = new DefaultCsrfProvider('SECRET'); + } + + protected function tearDown() + { + $this->provider = null; + } + + public function testGenerateCsrfToken() + { + session_start(); + + $token = $this->provider->generateCsrfToken('foo'); + + $this->assertEquals(sha1('SECRET'.'foo'.session_id()), $token); + } + + public function testGenerateCsrfTokenOnUnstartedSession() + { + session_id('touti'); + + if (!version_compare(PHP_VERSION, '5.4', '>=')) { + $this->markTestSkipped('This test requires PHP >= 5.4'); + } + + $this->assertSame(PHP_SESSION_NONE, session_status()); + + $token = $this->provider->generateCsrfToken('foo'); + + $this->assertEquals(sha1('SECRET'.'foo'.session_id()), $token); + $this->assertSame(PHP_SESSION_ACTIVE, session_status()); + } + + public function testIsCsrfTokenValidSucceeds() + { + session_start(); + + $token = sha1('SECRET'.'foo'.session_id()); + + $this->assertTrue($this->provider->isCsrfTokenValid('foo', $token)); + } + + public function testIsCsrfTokenValidFails() + { + session_start(); + + $token = sha1('SECRET'.'bar'.session_id()); + + $this->assertFalse($this->provider->isCsrfTokenValid('foo', $token)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php new file mode 100644 index 0000000..1dcc6b4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.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\CsrfProvider; + +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider; + +class SessionCsrfProviderTest extends \PHPUnit_Framework_TestCase +{ + protected $provider; + protected $session; + + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Session\Session')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $this->session = $this->getMock( + 'Symfony\Component\HttpFoundation\Session\Session', + array(), + array(), + '', + false // don't call constructor + ); + $this->provider = new SessionCsrfProvider($this->session, 'SECRET'); + } + + protected function tearDown() + { + $this->provider = null; + $this->session = null; + } + + public function testGenerateCsrfToken() + { + $this->session->expects($this->once()) + ->method('getId') + ->will($this->returnValue('ABCDEF')); + + $token = $this->provider->generateCsrfToken('foo'); + + $this->assertEquals(sha1('SECRET'.'foo'.'ABCDEF'), $token); + } + + public function testIsCsrfTokenValidSucceeds() + { + $this->session->expects($this->once()) + ->method('getId') + ->will($this->returnValue('ABCDEF')); + + $token = sha1('SECRET'.'foo'.'ABCDEF'); + + $this->assertTrue($this->provider->isCsrfTokenValid('foo', $token)); + } + + public function testIsCsrfTokenValidFails() + { + $this->session->expects($this->once()) + ->method('getId') + ->will($this->returnValue('ABCDEF')); + + $token = sha1('SECRET'.'bar'.'ABCDEF'); + + $this->assertFalse($this->provider->isCsrfTokenValid('foo', $token)); + } +} 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..0bcfe74 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.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\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 $csrfProvider; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); + $this->form = $this->getBuilder('post') + ->setDataMapper($this->getDataMapper()) + ->getForm(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->factory = null; + $this->csrfProvider = 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->csrfProvider, '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..0a1f0dc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.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\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; + +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', 'text'); + } + + public function getName() + { + return 'csrf_collection_test'; + } +} + +class FormTypeCsrfExtensionTest extends TypeTestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $csrfProvider; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $translator; + + protected function setUp() + { + $this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); + $this->translator = $this->getMock('Symfony\Component\Translation\TranslatorInterface'); + + parent::setUp(); + } + + protected function tearDown() + { + $this->csrfProvider = null; + $this->translator = null; + + parent::tearDown(); + } + + protected function getExtensions() + { + return array_merge(parent::getExtensions(), array( + new CsrfExtension($this->csrfProvider, $this->translator), + )); + } + + public function testCsrfProtectionByDefaultIfRootAndCompound() + { + $view = $this->factory + ->create('form', null, array( + 'csrf_field_name' => 'csrf', + 'compound' => true, + )) + ->createView(); + + $this->assertTrue(isset($view['csrf'])); + } + + public function testNoCsrfProtectionByDefaultIfCompoundButNotRoot() + { + $view = $this->factory + ->createNamedBuilder('root', 'form') + ->add($this->factory + ->createNamedBuilder('form', 'form', null, array( + 'csrf_field_name' => 'csrf', + 'compound' => true, + )) + ) + ->getForm() + ->get('form') + ->createView(); + + $this->assertFalse(isset($view['csrf'])); + } + + public function testNoCsrfProtectionByDefaultIfRootButNotCompound() + { + $view = $this->factory + ->create('form', null, array( + 'csrf_field_name' => 'csrf', + 'compound' => false, + )) + ->createView(); + + $this->assertFalse(isset($view['csrf'])); + } + + public function testCsrfProtectionCanBeDisabled() + { + $view = $this->factory + ->create('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_protection' => false, + 'compound' => true, + )) + ->createView(); + + $this->assertFalse(isset($view['csrf'])); + } + + public function testGenerateCsrfToken() + { + $this->csrfProvider->expects($this->once()) + ->method('generateCsrfToken') + ->with('%INTENTION%') + ->will($this->returnValue('token')); + + $view = $this->factory + ->create('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%', + '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->csrfProvider->expects($this->once()) + ->method('isCsrfTokenValid') + ->with('%INTENTION%', 'token') + ->will($this->returnValue($valid)); + + $form = $this->factory + ->createBuilder('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%', + 'compound' => true, + )) + ->add('child', 'text') + ->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->csrfProvider->expects($this->never()) + ->method('isCsrfTokenValid'); + + $form = $this->factory + ->createBuilder('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%', + 'compound' => true, + )) + ->add('child', 'text') + ->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->csrfProvider->expects($this->never()) + ->method('isCsrfTokenValid'); + + $form = $this->factory + ->createNamedBuilder('root', 'form') + ->add($this->factory + ->createNamedBuilder('form', 'form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%', + 'compound' => true, + )) + ) + ->getForm() + ->get('form'); + + $form->submit(array( + 'child' => 'foobar', + 'csrf' => 'token', + )); + } + + public function testDontValidateTokenIfRootButNotCompound() + { + $this->csrfProvider->expects($this->never()) + ->method('isCsrfTokenValid'); + + $form = $this->factory + ->create('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%', + 'compound' => false, + )); + + $form->submit(array( + 'csrf' => 'token', + )); + } + + public function testNoCsrfProtectionOnPrototype() + { + $prototypeView = $this->factory + ->create('collection', null, array( + 'type' => new FormTypeCsrfExtensionTest_ChildType(), + '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->csrfProvider->expects($this->once()) + ->method('isCsrfTokenValid') + ->with('%INTENTION%', 'token') + ->will($this->returnValue(false)); + + $this->translator->expects($this->once()) + ->method('trans') + ->with('Foobar') + ->will($this->returnValue('[trans]Foobar[/trans]')); + + $form = $this->factory + ->createBuilder('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'csrf_message' => 'Foobar', + 'intention' => '%INTENTION%', + 'compound' => true, + )) + ->getForm(); + + $form->submit(array( + 'csrf' => 'token', + )); + + $errors = $form->getErrors(); + + $this->assertGreaterThan(0, count($errors)); + $this->assertEquals(new FormError('[trans]Foobar[/trans]'), $errors[0]); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/EventListener/BindRequestListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/EventListener/BindRequestListenerTest.php new file mode 100644 index 0000000..2ff072b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/EventListener/BindRequestListenerTest.php @@ -0,0 +1,286 @@ + + * + * 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\EventListener; + +use Symfony\Component\Form\Extension\HttpFoundation\EventListener\BindRequestListener; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Test\DeprecationErrorHandler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\File\UploadedFile; + +/** + * @author Bernhard Schussek + */ +class BindRequestListenerTest extends \PHPUnit_Framework_TestCase +{ + private $values; + + private $filesPlain; + + private $filesNested; + + /** + * @var UploadedFile + */ + private $uploadedFile; + + protected function setUp() + { + $path = tempnam(sys_get_temp_dir(), 'sf2'); + touch($path); + + $this->values = array( + 'name' => 'Bernhard', + 'image' => array('filename' => 'foobar.png'), + ); + + $this->filesPlain = array( + 'image' => array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'upload.png', + 'size' => 123, + 'tmp_name' => $path, + 'type' => 'image/png' + ), + ); + + $this->filesNested = 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'), + ); + + $this->uploadedFile = new UploadedFile($path, 'upload.png', 'image/png', 123, UPLOAD_ERR_OK); + } + + protected function tearDown() + { + unlink($this->uploadedFile->getRealPath()); + } + + public function requestMethodProvider() + { + return array( + array('POST'), + array('PUT'), + array('DELETE'), + array('PATCH'), + ); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitRequest($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $values = array('author' => $this->values); + $files = array('author' => $this->filesNested); + $request = new Request(array(), $values, array(), array(), $files, array( + 'REQUEST_METHOD' => $method, + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertEquals(array( + 'name' => 'Bernhard', + 'image' => $this->uploadedFile, + ), $event->getData()); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitRequestWithEmptyName($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request(array(), $this->values, array(), array(), $this->filesPlain, array( + 'REQUEST_METHOD' => $method, + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('', null, $dispatcher); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertEquals(array( + 'name' => 'Bernhard', + 'image' => $this->uploadedFile, + ), $event->getData()); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitEmptyRequestToCompoundForm($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request(array(), array(), array(), array(), array(), array( + 'REQUEST_METHOD' => $method, + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $config->setCompound(true); + $config->setDataMapper($this->getMock('Symfony\Component\Form\DataMapperInterface')); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + // Default to empty array + $this->assertEquals(array(), $event->getData()); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitEmptyRequestToSimpleForm($method) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request(array(), array(), array(), array(), array(), array( + 'REQUEST_METHOD' => $method, + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $config->setCompound(false); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + // Default to null + $this->assertNull($event->getData()); + } + + public function testSubmitGetRequest() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $values = array('author' => $this->values); + $request = new Request($values, array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertEquals(array( + 'name' => 'Bernhard', + 'image' => array('filename' => 'foobar.png'), + ), $event->getData()); + } + + public function testSubmitGetRequestWithEmptyName() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request($this->values, array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('', null, $dispatcher); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertEquals(array( + 'name' => 'Bernhard', + 'image' => array('filename' => 'foobar.png'), + ), $event->getData()); + } + + public function testSubmitEmptyGetRequestToCompoundForm() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request(array(), array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $config->setCompound(true); + $config->setDataMapper($this->getMock('Symfony\Component\Form\DataMapperInterface')); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertEquals(array(), $event->getData()); + } + + public function testSubmitEmptyGetRequestToSimpleForm() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $request = new Request(array(), array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $config = new FormConfigBuilder('author', null, $dispatcher); + $config->setCompound(false); + $form = new Form($config); + $event = new FormEvent($form, $request); + + $listener = new BindRequestListener(); + DeprecationErrorHandler::preBind($listener, $event); + + $this->assertNull($event->getData()); + } +} 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..2d5cf77 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.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\HttpFoundation; + +use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler; +use Symfony\Component\Form\Tests\AbstractRequestHandlerTest; +use Symfony\Component\HttpFoundation\Request; + +/** + * @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(); + } + + protected function getMockFile() + { + return $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') + ->disableOriginalConstructor() + ->getMock(); + } +} 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..a8bdde8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -0,0 +1,748 @@ + + * + * 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\Constraint; +use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Validator\Constraints\NotBlank; + +/** + * @author Bernhard Schussek + */ +class FormValidatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $factory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $serverParams; + + /** + * @var FormValidator + */ + private $validator; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\Event')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $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') + ); + $this->validator = new FormValidator($this->serverParams); + } + + public function testValidate() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => array('group1', 'group2')); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testValidateConstraints() + { + $context = $this->getMockExecutionContext(); + $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 + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + // Then custom constraints + $context->expects($this->at(2)) + ->method('validateValue') + ->with($object, $constraint1, 'data', 'group1'); + $context->expects($this->at(3)) + ->method('validateValue') + ->with($object, $constraint2, 'data', 'group2'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontValidateIfParentWithoutCascadeValidation() + { + $context = $this->getMockExecutionContext(); + $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); + + $context->expects($this->never()) + ->method('validate'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testValidateConstraintsEvenIfNoCascadeValidation() + { + $context = $this->getMockExecutionContext(); + $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); + + $context->expects($this->at(0)) + ->method('validateValue') + ->with($object, $constraint1, 'data', 'group1'); + $context->expects($this->at(1)) + ->method('validateValue') + ->with($object, $constraint2, 'data', 'group2'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontValidateIfNoValidationGroups() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $form = $this->getBuilder('name', '\stdClass', array( + 'validation_groups' => array(), + )) + ->setData($object) + ->getForm(); + + $form->setData($object); + + $context->expects($this->never()) + ->method('validate'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontValidateConstraintsIfNoValidationGroups() + { + $context = $this->getMockExecutionContext(); + $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()); + + $context->expects($this->never()) + ->method('validate'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontValidateIfNotSynchronized() + { + $context = $this->getMockExecutionContext(); + $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'); + + $context->expects($this->never()) + ->method('validate'); + + $context->expects($this->once()) + ->method('addViolation') + ->with( + 'invalid_message_key', + array('{{ value }}' => 'foo', '{{ foo }}' => 'bar'), + 'foo' + ); + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testAddInvalidErrorEvenIfNoValidationGroups() + { + $context = $this->getMockExecutionContext(); + $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'); + + $context->expects($this->never()) + ->method('validate'); + + $context->expects($this->once()) + ->method('addViolation') + ->with( + 'invalid_message_key', + array('{{ value }}' => 'foo', '{{ foo }}' => 'bar'), + 'foo' + ); + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontValidateConstraintsIfNotSynchronized() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint'); + $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint'); + + $options = array( + '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(array()); + + $context->expects($this->never()) + ->method('validate'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + // https://github.com/symfony/symfony/issues/4359 + public function testDontMarkInvalidIfAnyChildIsNotSynchronized() + { + $context = $this->getMockExecutionContext(); + $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')); + + $context->expects($this->never()) + ->method('addViolation'); + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testHandleCallbackValidationGroups() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => array($this, 'getValidationGroups')); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontExecuteFunctionNames() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => 'header'); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $context->expects($this->once()) + ->method('validate') + ->with($object, 'data', 'header', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testHandleClosureValidationGroups() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => function(FormInterface $form){ + return array('group1', 'group2'); + }); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testUseValidationGroupOfClickedButton() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent', null, array('cascade_validation' => true)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getForm('name', '\stdClass', array( + 'validation_groups' => 'form_group', + )); + + $parent->add($form); + $parent->add($this->getClickedSubmitButton('submit', array( + 'validation_groups' => 'button_group', + ))); + + $form->setData($object); + + $context->expects($this->once()) + ->method('validate') + ->with($object, 'data', 'button_group', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontUseValidationGroupOfUnclickedButton() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent', null, array('cascade_validation' => true)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getForm('name', '\stdClass', array( + 'validation_groups' => 'form_group', + )); + + $parent->add($form); + $parent->add($this->getSubmitButton('submit', array( + 'validation_groups' => 'button_group', + ))); + + $form->setData($object); + + $context->expects($this->once()) + ->method('validate') + ->with($object, 'data', 'form_group', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testUseInheritedValidationGroup() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $parentOptions = array( + 'validation_groups' => 'group', + 'cascade_validation' => true, + ); + $parent = $this->getBuilder('parent', null, $parentOptions) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getBuilder('name', '\stdClass')->getForm(); + $parent->add($form); + + $form->setData($object); + + $context->expects($this->once()) + ->method('validate') + ->with($object, 'data', 'group', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testUseInheritedCallbackValidationGroup() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $parentOptions = array( + 'validation_groups' => array($this, 'getValidationGroups'), + 'cascade_validation' => true, + ); + $parent = $this->getBuilder('parent', null, $parentOptions) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getBuilder('name', '\stdClass')->getForm(); + $parent->add($form); + + $form->setData($object); + + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testUseInheritedClosureValidationGroup() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + + $parentOptions = array( + 'validation_groups' => function(FormInterface $form){ + return array('group1', 'group2'); + }, + 'cascade_validation' => true, + ); + $parent = $this->getBuilder('parent', null, $parentOptions) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getBuilder('name', '\stdClass')->getForm(); + $parent->add($form); + + $form->setData($object); + + $context->expects($this->at(0)) + ->method('validate') + ->with($object, 'data', 'group1', true); + $context->expects($this->at(1)) + ->method('validate') + ->with($object, 'data', 'group2', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testAppendPropertyPath() + { + $context = $this->getMockExecutionContext(); + $object = $this->getMock('\stdClass'); + $form = $this->getBuilder('name', '\stdClass') + ->setData($object) + ->getForm(); + + $context->expects($this->once()) + ->method('validate') + ->with($object, 'data', 'Default', true); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testDontWalkScalars() + { + $context = $this->getMockExecutionContext(); + + $form = $this->getBuilder() + ->setData('scalar') + ->getForm(); + + $context->expects($this->never()) + ->method('validate'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function testViolationIfExtraData() + { + $context = $this->getMockExecutionContext(); + + $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')); + + $context->expects($this->once()) + ->method('addViolation') + ->with( + 'Extra!', + array('{{ extra_fields }}' => 'foo'), + array('foo' => 'bar') + ); + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + /** + * @dataProvider getPostMaxSizeFixtures + */ + public function testPostMaxSizeViolation($contentLength, $iniMax, $nbViolation, array $params = array()) + { + $this->serverParams->expects($this->once()) + ->method('getContentLength') + ->will($this->returnValue($contentLength)); + $this->serverParams->expects($this->any()) + ->method('getNormalizedIniPostMaxSize') + ->will($this->returnValue($iniMax)); + + $context = $this->getMockExecutionContext(); + $options = array('post_max_size_message' => 'Max {{ max }}!'); + $form = $this->getBuilder('name', null, $options)->getForm(); + + for ($i = 0; $i < $nbViolation; ++$i) { + if (0 === $i && count($params) > 0) { + $context->expects($this->at($i)) + ->method('addViolation') + ->with($options['post_max_size_message'], $params); + } else { + $context->expects($this->at($i)) + ->method('addViolation'); + } + } + + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + public function getPostMaxSizeFixtures() + { + return array( + array(pow(1024, 3) + 1, '1G', 1, array('{{ max }}' => '1G')), + array(pow(1024, 3), '1G', 0), + array(pow(1024, 2) + 1, '1M', 1, array('{{ max }}' => '1M')), + array(pow(1024, 2), '1M', 0), + array(1024 + 1, '1K', 1, array('{{ max }}' => '1K')), + array(1024, '1K', 0), + array(null, '1K', 0), + array(1024, '', 0), + array(1024, 0, 0), + ); + } + + public function testNoViolationIfNotRoot() + { + $this->serverParams->expects($this->once()) + ->method('getContentLength') + ->will($this->returnValue(1025)); + $this->serverParams->expects($this->never()) + ->method('getNormalizedIniPostMaxSize'); + + $context = $this->getMockExecutionContext(); + $parent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getForm(); + $parent->add($form); + + $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() + { + return $this->getMock('Symfony\Component\Validator\ExecutionContextInterface'); + } + + /** + * @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(); + } + + private function getClickedSubmitButton($name = 'name', array $options = array()) + { + return $this->getSubmitButton($name, $options)->submit(''); + } + + /** + * @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..528f946 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.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\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; + +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() + { + if (!class_exists('Symfony\Component\EventDispatcher\Event')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->validator = $this->getMock('Symfony\Component\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); + } + + 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::ERR_INVALID); + $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)); + } +} 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..6619410 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.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\Validator\Type; + +use Symfony\Component\Form\FormInterface; + +class FormTypeValidatorExtensionTest extends TypeTestCase +{ + public function testValidationGroupNullByDefault() + { + $form = $this->factory->create('form'); + + $this->assertNull($form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsTransformedToArray() + { + $form = $this->factory->create('form', null, array( + 'validation_groups' => 'group', + )); + + $this->assertEquals(array('group'), $form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsCanBeSetToArray() + { + $form = $this->factory->create('form', null, array( + 'validation_groups' => array('group1', 'group2'), + )); + + $this->assertEquals(array('group1', 'group2'), $form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsCanBeSetToFalse() + { + $form = $this->factory->create('form', null, array( + 'validation_groups' => false, + )); + + $this->assertEquals(array(), $form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsCanBeSetToCallback() + { + $form = $this->factory->create('form', null, array( + 'validation_groups' => array($this, 'testValidationGroupsCanBeSetToCallback'), + )); + + $this->assertTrue(is_callable($form->getConfig()->getOption('validation_groups'))); + } + + public function testValidationGroupsCanBeSetToClosure() + { + $form = $this->factory->create('form', null, array( + 'validation_groups' => function(FormInterface $form){ return null; }, + )); + + $this->assertTrue(is_callable($form->getConfig()->getOption('validation_groups'))); + } + + public function testSubmitValidatesData() + { + $builder = $this->factory->createBuilder('form', null, array( + 'validation_groups' => 'group', + )); + $builder->add('firstName', 'form'); + $form = $builder->getForm(); + + $this->validator->expects($this->once()) + ->method('validate') + ->with($this->equalTo($form)); + + // specific data is irrelevant + $form->submit(array()); + } +} 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..d94d896 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.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\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() + { + if (!class_exists('Symfony\Component\Validator\Constraint')) { + $this->markTestSkipped('The "Validator" component is not available'); + } + + $this->validator = $this->getMock('Symfony\Component\Validator\ValidatorInterface'); + $metadataFactory = $this->getMock('Symfony\Component\Validator\MetadataFactoryInterface'); + $this->validator->expects($this->once())->method('getMetadataFactory')->will($this->returnValue($metadataFactory)); + $metadata = $this->getMockBuilder('Symfony\Component\Validator\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); + $metadataFactory->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..7ad5b77 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.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\Form\Tests\Extension\Validator\Util; + +class ServerParamsTest extends \PHPUnit_Framework_TestCase +{ + /** @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/ViolationMapper/ViolationMapperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php new file mode 100644 index 0000000..c802ea7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php @@ -0,0 +1,1481 @@ + + * + * 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\FormConfigBuilder; +use Symfony\Component\Form\FormError; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\Validator\ConstraintViolation; + +/** + * @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() + { + if (!class_exists('Symfony\Component\EventDispatcher\Event')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $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() + { + return new FormError($this->message, $this->messageTemplate, $this->params); + } + + 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); + + $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()), $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); + + $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()), $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); + + // submit to 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); + + // submit to 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 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_1, 'address', 'address', 'street', 'street', 'children[address][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address][street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address][street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address][street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'children[address][street]'), + array(self::LEVEL_1, '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); + + $this->mapper->mapViolation($violation, $parent); + + if (self::LEVEL_0 === $target) { + $this->assertEquals(array($this->getFormError()), $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()), $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()), $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); + } + + $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()), $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()), $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()), $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); + + $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()), $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()), $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()), $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()), $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); + + $this->mapper->mapViolation($violation, $parent); + + if (self::LEVEL_0 === $target) { + $this->assertEquals(array($this->getFormError()), $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()), $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()), $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..02df8f4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.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\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), + ), 'children[address]'), + 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 = $parentPath === null ? 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/AlternatingRowType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php new file mode 100644 index 0000000..ee7d135 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php @@ -0,0 +1,27 @@ +getFormFactory(); + + $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formFactory) { + $form = $event->getForm(); + $type = $form->getName() % 2 === 0 ? 'text' : 'textarea'; + $form->add('title', $type); + }); + } + + public function getName() + { + return 'alternating_row'; + } +} 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..1120489 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Author.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\Fixtures; + +class Author +{ + public $firstName; + private $lastName; + private $australian; + public $child; + private $readPermissions; + + private $privateProperty; + + 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/AuthorType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AuthorType.php new file mode 100644 index 0000000..147f6e4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AuthorType.php @@ -0,0 +1,30 @@ +add('firstName') + ->add('lastName') + ; + } + + public function getName() + { + return 'author'; + } + + public function setDefaultOptions(OptionsResolverInterface $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/FixedDataTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php new file mode 100644 index 0000000..a5a3124 --- /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\RuntimeException; + +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 RuntimeException(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 RuntimeException(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/FooSubType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooSubType.php new file mode 100644 index 0000000..4f7ba6d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooSubType.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\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class FooSubType extends AbstractType +{ + public function getName() + { + return 'foo_sub_type'; + } + + public function getParent() + { + return 'foo'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooSubTypeWithParentInstance.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooSubTypeWithParentInstance.php new file mode 100644 index 0000000..468b5a3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooSubTypeWithParentInstance.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\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class FooSubTypeWithParentInstance extends AbstractType +{ + public function getName() + { + return 'foo_sub_type_parent_instance'; + } + + public function getParent() + { + return new 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..d26d3f7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooType.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\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +class FooType extends AbstractType +{ + public function getName() + { + return 'foo'; + } + + public function getParent() + { + return null; + } +} 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..c5f92e1 --- /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 'foo'; + } +} 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..2e36475 --- /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 'foo'; + } +} 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..f9de560 --- /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[$type->getName()] = $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/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/FormBuilderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormBuilderTest.php new file mode 100644 index 0000000..e076c97 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormBuilderTest.php @@ -0,0 +1,232 @@ + + * + * 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; + +class FormBuilderTest extends \PHPUnit_Framework_TestCase +{ + private $dispatcher; + + private $factory; + + private $builder; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $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', 'text', array('bar' => 'baz')); + $this->assertSame($builder, $this->builder); + } + + public function testAdd() + { + $this->assertFalse($this->builder->has('foo')); + $this->builder->add('foo', 'text'); + $this->assertTrue($this->builder->has('foo')); + } + + public function testAddIntegerName() + { + $this->assertFalse($this->builder->has(0)); + $this->builder->add(0, 'text'); + $this->assertTrue($this->builder->has(0)); + } + + public function testAll() + { + $this->factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('foo', 'text') + ->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', 'text'); + $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', 'text'); + $this->builder->add($this->getFormBuilder('bar')); + $this->builder->add('baz', 'text'); + + $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', 'text'); + $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', 'text'); + $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', 'text', null, array()) + ; + + $this->builder->create('foo'); + } + + 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 = 'text'; + $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..961dfd3 --- /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 + // Symfony2. + // 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..a1292db --- /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($this->type->getName())); + $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..ea872b0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -0,0 +1,506 @@ + + * + * 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; +use Symfony\Component\Form\Tests\Fixtures\Author; +use Symfony\Component\Form\Tests\Fixtures\FooType; +use Symfony\Component\Form\Tests\Fixtures\FooSubType; +use Symfony\Component\Form\Tests\Fixtures\FooSubTypeWithParentInstance; + +/** + * @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 FormFactory + */ + private $factory; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $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->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'); + $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('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', 'type', null, $options)); + } + + public function testCreateNamedBuilderWithTypeInstance() + { + $options = array('a' => '1', 'b' => '2'); + $type = new FooType(); + $resolvedType = $this->getMockResolvedType(); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', $type, null, $options)); + } + + public function testCreateNamedBuilderWithTypeInstanceWithParentType() + { + $options = array('a' => '1', 'b' => '2'); + $type = new FooSubType(); + $resolvedType = $this->getMockResolvedType(); + $parentResolvedType = $this->getMockResolvedType(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('foo') + ->will($this->returnValue($parentResolvedType)); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type, array(), $parentResolvedType) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', $type, null, $options)); + } + + public function testCreateNamedBuilderWithTypeInstanceWithParentTypeInstance() + { + $options = array('a' => '1', 'b' => '2'); + $type = new FooSubTypeWithParentInstance(); + $resolvedType = $this->getMockResolvedType(); + $parentResolvedType = $this->getMockResolvedType(); + + $this->resolvedTypeFactory->expects($this->at(0)) + ->method('createResolvedType') + ->with($type->getParent()) + ->will($this->returnValue($parentResolvedType)); + + $this->resolvedTypeFactory->expects($this->at(1)) + ->method('createResolvedType') + ->with($type, array(), $parentResolvedType) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', $type, null, $options)); + } + + public function testCreateNamedBuilderWithResolvedTypeInstance() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedType = $this->getMockResolvedType(); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', $resolvedType, null, $options)); + } + + public function testCreateNamedBuilderFillsDataOption() + { + $givenOptions = array('a' => '1', 'b' => '2'); + $expectedOptions = array_merge($givenOptions, array('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('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', 'type', 'DATA', $givenOptions)); + } + + public function testCreateNamedBuilderDoesNotOverrideExistingDataOption() + { + $options = array('a' => '1', 'b' => '2', '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('BUILDER')); + + $this->assertSame('BUILDER', $this->factory->createNamedBuilder('name', 'type', 'DATA', $options)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + * @expectedExceptionMessage Expected argument of type "string, Symfony\Component\Form\ResolvedFormTypeInterface or Symfony\Component\Form\FormTypeInterface", "stdClass" given + */ + public function testCreateNamedBuilderThrowsUnderstandableException() + { + $this->factory->createNamedBuilder('name', new \stdClass()); + } + + public function testCreateUsesTypeNameIfTypeGivenAsString() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedType = $this->getMockResolvedType(); + $builder = $this->getMockFormBuilder(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('TYPE') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'TYPE', $options) + ->will($this->returnValue($builder)); + + $builder->expects($this->once()) + ->method('getForm') + ->will($this->returnValue('FORM')); + + $this->assertSame('FORM', $this->factory->create('TYPE', null, $options)); + } + + public function testCreateUsesTypeNameIfTypeGivenAsObject() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedType = $this->getMockResolvedType(); + $builder = $this->getMockFormBuilder(); + + $resolvedType->expects($this->once()) + ->method('getName') + ->will($this->returnValue('TYPE')); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'TYPE', $options) + ->will($this->returnValue($builder)); + + $builder->expects($this->once()) + ->method('getForm') + ->will($this->returnValue('FORM')); + + $this->assertSame('FORM', $this->factory->create($resolvedType, null, $options)); + } + + public function testCreateNamed() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedType = $this->getMockResolvedType(); + $builder = $this->getMockFormBuilder(); + + $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($builder)); + + $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', 'text', null, array()) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty('Application\Author', 'firstName'); + + $this->assertEquals('builderInstance', $builder); + } + + public function testCreateBuilderForPropertyCreatesFormWithHighestConfidence() + { + $this->guesser1->expects($this->once()) + ->method('guessType') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new TypeGuess( + 'text', + array('max_length' => 10), + Guess::MEDIUM_CONFIDENCE + ))); + + $this->guesser2->expects($this->once()) + ->method('guessType') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new TypeGuess( + 'password', + array('max_length' => 7), + Guess::HIGH_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'password', null, array('max_length' => 7)) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty('Application\Author', 'firstName'); + + $this->assertEquals('builderInstance', $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', 'text') + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty('Application\Author', 'firstName'); + + $this->assertEquals('builderInstance', $builder); + } + + public function testOptionsCanBeOverridden() + { + $this->guesser1->expects($this->once()) + ->method('guessType') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new TypeGuess( + 'text', + array('max_length' => 10), + Guess::MEDIUM_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'text', null, array('max_length' => 11)) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName', + null, + array('max_length' => 11) + ); + + $this->assertEquals('builderInstance', $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', 'text', null, array('max_length' => 20)) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName' + ); + + $this->assertEquals('builderInstance', $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', 'text', null, array('required' => false)) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName' + ); + + $this->assertEquals('builderInstance', $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', 'text', null, array('pattern' => '[a-zA-Z]')) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName' + ); + + $this->assertEquals('builderInstance', $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'); + } + + private function getMockType() + { + return $this->getMock('Symfony\Component\Form\FormTypeInterface'); + } + + private function getMockFormBuilder() + { + return $this->getMock('Symfony\Component\Form\Test\FormBuilderInterface'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormIntegrationTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormIntegrationTestCase.php new file mode 100644 index 0000000..763286c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormIntegrationTestCase.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; + +use Symfony\Component\Form\Test\FormIntegrationTestCase as BaseFormIntegrationTestCase; + +/** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use Symfony\Component\Form\Test\FormIntegrationTestCase instead. + */ +abstract class FormIntegrationTestCase extends BaseFormIntegrationTestCase +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormPerformanceTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormPerformanceTestCase.php new file mode 100644 index 0000000..39882e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormPerformanceTestCase.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; + +use Symfony\Component\Form\Test\FormPerformanceTestCase as BaseFormPerformanceTestCase; + +/** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use Symfony\Component\Form\Test\FormPerformanceTestCase instead. + */ +abstract class FormPerformanceTestCase extends BaseFormPerformanceTestCase +{ +} 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..0c8bb6b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormRegistryTest.php @@ -0,0 +1,243 @@ + + * + * 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\Tests\Fixtures\TestExtension; +use Symfony\Component\Form\Tests\Fixtures\FooSubTypeWithParentInstance; +use Symfony\Component\Form\Tests\Fixtures\FooSubType; +use Symfony\Component\Form\Tests\Fixtures\FooTypeBazExtension; +use Symfony\Component\Form\Tests\Fixtures\FooTypeBarExtension; +use Symfony\Component\Form\Tests\Fixtures\FooType; + +/** + * @author Bernhard Schussek + */ +class FormRegistryTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var FormRegistry + */ + private $registry; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + 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 = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + + $this->extension2->addType($type); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')); + + $resolvedType = $this->registry->getType('foo'); + + $this->assertSame($resolvedType, $this->registry->getType('foo')); + } + + public function testGetTypeWithTypeExtensions() + { + $type = new FooType(); + $ext1 = new FooTypeBarExtension(); + $ext2 = new FooTypeBazExtension(); + $resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + + $this->extension2->addType($type); + $this->extension1->addTypeExtension($ext1); + $this->extension2->addTypeExtension($ext2); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type, array($ext1, $ext2)) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')); + + $this->assertSame($resolvedType, $this->registry->getType('foo')); + } + + public function testGetTypeConnectsParent() + { + $parentType = new FooType(); + $type = new FooSubType(); + $parentResolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + + $this->extension1->addType($parentType); + $this->extension2->addType($type); + + $this->resolvedTypeFactory->expects($this->at(0)) + ->method('createResolvedType') + ->with($parentType) + ->will($this->returnValue($parentResolvedType)); + + $this->resolvedTypeFactory->expects($this->at(1)) + ->method('createResolvedType') + ->with($type, array(), $parentResolvedType) + ->will($this->returnValue($resolvedType)); + + $parentResolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')); + + $resolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo_sub_type')); + + $this->assertSame($resolvedType, $this->registry->getType('foo_sub_type')); + } + + public function testGetTypeConnectsParentIfGetParentReturnsInstance() + { + $type = new FooSubTypeWithParentInstance(); + $parentResolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + + $this->extension1->addType($type); + + $this->resolvedTypeFactory->expects($this->at(0)) + ->method('createResolvedType') + ->with($this->isInstanceOf('Symfony\Component\Form\Tests\Fixtures\FooType')) + ->will($this->returnValue($parentResolvedType)); + + $this->resolvedTypeFactory->expects($this->at(1)) + ->method('createResolvedType') + ->with($type, array(), $parentResolvedType) + ->will($this->returnValue($resolvedType)); + + $parentResolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')); + + $resolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo_sub_type_parent_instance')); + + $this->assertSame($resolvedType, $this->registry->getType('foo_sub_type_parent_instance')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testGetTypeThrowsExceptionIfParentNotFound() + { + $type = new FooSubType(); + + $this->extension1->addType($type); + + $this->registry->getType($type); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testGetTypeThrowsExceptionIfTypeNotFound() + { + $this->registry->getType('bar'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testGetTypeThrowsExceptionIfNoString() + { + $this->registry->getType(array()); + } + + public function testHasTypeAfterLoadingFromExtension() + { + $type = new FooType(); + $resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type) + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')); + + $this->assertFalse($this->registry->hasType('foo')); + + $this->extension2->addType($type); + + $this->assertTrue($this->registry->hasType('foo')); + } + + 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..69b048f --- /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\Test; + +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..235eb6e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Guess/GuessTest.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\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..9d3a997 --- /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(); + } + + protected function getMockFile() + { + return array( + 'name' => 'upload.txt', + 'type' => 'text/plain', + 'tmp_name' => 'owfdskjasdfsa', + '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..bb32a24 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php @@ -0,0 +1,280 @@ + + * + * 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\ResolvedFormType; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\Form; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * @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; + + protected function setUp() + { + if (!class_exists('Symfony\Component\OptionsResolver\OptionsResolver')) { + $this->markTestSkipped('The "OptionsResolver" component is not available'); + } + + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->dataMapper = $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + public function testCreateBuilder() + { + if (version_compare(\PHPUnit_Runner_Version::id(), '3.7', '<')) { + $this->markTestSkipped('This test requires PHPUnit 3.7.'); + } + + $parentType = $this->getMockFormType(); + $type = $this->getMockFormType(); + $extension1 = $this->getMockFormTypeExtension(); + $extension2 = $this->getMockFormTypeExtension(); + + $parentResolvedType = new ResolvedFormType($parentType); + $resolvedType = new ResolvedFormType($type, array($extension1, $extension2), $parentResolvedType); + + $test = $this; + $i = 0; + + $assertIndex = function ($index) use (&$i, $test) { + return function () use (&$i, $test, $index) { + /* @var \PHPUnit_Framework_TestCase $test */ + $test->assertEquals($index, $i, 'Executed at index '.$index); + + ++$i; + }; + }; + + $assertIndexAndAddOption = function ($index, $option, $default) use ($assertIndex) { + $assertIndex = $assertIndex($index); + + return function (OptionsResolverInterface $resolver) use ($assertIndex, $index, $option, $default) { + $assertIndex(); + + $resolver->setDefaults(array($option => $default)); + }; + }; + + // First the default options are generated for the super type + $parentType->expects($this->once()) + ->method('setDefaultOptions') + ->will($this->returnCallback($assertIndexAndAddOption(0, 'a', 'a_default'))); + + // The form type itself + $type->expects($this->once()) + ->method('setDefaultOptions') + ->will($this->returnCallback($assertIndexAndAddOption(1, 'b', 'b_default'))); + + // And its extensions + $extension1->expects($this->once()) + ->method('setDefaultOptions') + ->will($this->returnCallback($assertIndexAndAddOption(2, 'c', 'c_default'))); + + $extension2->expects($this->once()) + ->method('setDefaultOptions') + ->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'); + + // Then the form is built for the super type + $parentType->expects($this->once()) + ->method('buildForm') + ->with($this->anything(), $resolvedOptions) + ->will($this->returnCallback($assertIndex(4))); + + // Then the type itself + $type->expects($this->once()) + ->method('buildForm') + ->with($this->anything(), $resolvedOptions) + ->will($this->returnCallback($assertIndex(5))); + + // Then its extensions + $extension1->expects($this->once()) + ->method('buildForm') + ->with($this->anything(), $resolvedOptions) + ->will($this->returnCallback($assertIndex(6))); + + $extension2->expects($this->once()) + ->method('buildForm') + ->with($this->anything(), $resolvedOptions) + ->will($this->returnCallback($assertIndex(7))); + + $factory = $this->getMockFormFactory(); + $builder = $resolvedType->createBuilder($factory, 'name', $givenOptions); + + $this->assertSame($resolvedType, $builder->getType()); + } + + public function testCreateView() + { + $parentType = $this->getMockFormType(); + $type = $this->getMockFormType(); + $field1Type = $this->getMockFormType(); + $field2Type = $this->getMockFormType(); + $extension1 = $this->getMockFormTypeExtension(); + $extension2 = $this->getMockFormTypeExtension(); + + $parentResolvedType = new ResolvedFormType($parentType); + $resolvedType = new ResolvedFormType($type, array($extension1, $extension2), $parentResolvedType); + $field1ResolvedType = new ResolvedFormType($field1Type); + $field2ResolvedType = new ResolvedFormType($field2Type); + + $options = array('a' => '1', 'b' => '2'); + $form = $this->getBuilder('name', $options) + ->setCompound(true) + ->setDataMapper($this->dataMapper) + ->setType($resolvedType) + ->add($this->getBuilder('foo')->setType($field1ResolvedType)) + ->add($this->getBuilder('bar')->setType($field2ResolvedType)) + ->getForm(); + + $test = $this; + $i = 0; + + $assertIndexAndNbOfChildViews = function ($index, $nbOfChildViews) use (&$i, $test) { + return function (FormView $view) use (&$i, $test, $index, $nbOfChildViews) { + /* @var \PHPUnit_Framework_TestCase $test */ + $test->assertEquals($index, $i, 'Executed at index '.$index); + $test->assertCount($nbOfChildViews, $view); + + ++$i; + }; + }; + + // First the super type + $parentType->expects($this->once()) + ->method('buildView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(0, 0))); + + // Then the type itself + $type->expects($this->once()) + ->method('buildView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(1, 0))); + + // Then its extensions + $extension1->expects($this->once()) + ->method('buildView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(2, 0))); + + $extension2->expects($this->once()) + ->method('buildView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(3, 0))); + + // Now the first child form + $field1Type->expects($this->once()) + ->method('buildView') + ->will($this->returnCallback($assertIndexAndNbOfChildViews(4, 0))); + $field1Type->expects($this->once()) + ->method('finishView') + ->will($this->returnCallback($assertIndexAndNbOfChildViews(5, 0))); + + // And the second child form + $field2Type->expects($this->once()) + ->method('buildView') + ->will($this->returnCallback($assertIndexAndNbOfChildViews(6, 0))); + $field2Type->expects($this->once()) + ->method('finishView') + ->will($this->returnCallback($assertIndexAndNbOfChildViews(7, 0))); + + // Again first the parent + $parentType->expects($this->once()) + ->method('finishView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(8, 2))); + + // Then the type itself + $type->expects($this->once()) + ->method('finishView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(9, 2))); + + // Then its extensions + $extension1->expects($this->once()) + ->method('finishView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(10, 2))); + + $extension2->expects($this->once()) + ->method('finishView') + ->with($this->anything(), $form, $options) + ->will($this->returnCallback($assertIndexAndNbOfChildViews(11, 2))); + + $parentView = new FormView(); + $view = $resolvedType->createView($form, $parentView); + + $this->assertSame($parentView, $view->parent); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getMockFormType() + { + return $this->getMock('Symfony\Component\Form\FormTypeInterface'); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getMockFormTypeExtension() + { + return $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface'); + } + + /** + * @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..bedad67 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/SimpleFormTest.php @@ -0,0 +1,1045 @@ + + * + * 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()); + } + + // 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('preBind')) + ->getMock(); + $mock->expects($this->once()) + ->method('preBind') + ->with($this->callback(function ($event) { + return null === $event->getData(); + })); + + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addEventListener(FormEvents::PRE_BIND, array($mock, 'preBind')); + $form = new Form($config); + + $form->bind(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()); + } + + public function testAlwaysDisabledIfParentDisabled() + { + $parent = $this->getBuilder()->setDisabled(true)->getForm(); + $child = $this->getBuilder()->setDisabled(false)->getForm(); + + $child->setParent($parent); + + $this->assertTrue($child->isDisabled()); + } + + public function testDisabled() + { + $parent = $this->getBuilder()->setDisabled(false)->getForm(); + $child = $this->getBuilder()->setDisabled(true)->getForm(); + + $child->setParent($parent); + + $this->assertTrue($child->isDisabled()); + } + + public function testNotDisabled() + { + $parent = $this->getBuilder()->setDisabled(false)->getForm(); + $child = $this->getBuilder()->setDisabled(false)->getForm(); + + $child->setParent($parent); + + $this->assertFalse($child->isDisabled()); + } + + 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() + { + $test = $this; + $form = $this->getBuilder() + ->setEmptyData(function ($form) use ($test) { + // the form instance is passed to the closure to allow use + // of form data when creating the empty value + $test->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->assertSame(array(), $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 testGetErrorsAsString() + { + $this->form->addError(new FormError('Error!')); + + $this->assertEquals("ERROR: Error!\n", $this->form->getErrorsAsString()); + } + + 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()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\LogicException + */ + public function testViewDataMustNotBeObjectIfDataClassIsNull() + { + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => new \stdClass(), + ))); + $form = new Form($config); + + $form->setData('foo'); + } + + 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() + { + $test = $this; + + $child = $this->getBuilder('child', $this->dispatcher); + $child->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($test) { + // child form doesn't receive the wrong data that is submitted on parent + $test->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() + { + $test = $this; + $form = $this->getBuilder() + ->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($test) { + $test->assertNull($event->getData()); + }) + ->setInheritData(true) + ->getForm(); + + $form->submit('foo'); + } + + public function testSubmitIsNeverFiredIfInheritData() + { + $test = $this; + $form = $this->getBuilder() + ->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) use ($test) { + $test->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/Util/FormUtil.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/FormUtil.php new file mode 100644 index 0000000..7647691 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/FormUtil.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\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 Boolean + */ + 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..5c2c5fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/InheritDataAwareIterator.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\Util; + +/** + * Iterator that returns only forms from a form tree that do not inherit their + * parent data. + * + * If the iterator encounters a form that inherits its parent data, it enters + * the form and traverses its children as well. + * + * @author Bernhard Schussek + */ +class InheritDataAwareIterator extends VirtualFormAwareIterator +{ + /** + * Creates a new iterator. + * + * @param \Symfony\Component\Form\FormInterface[] $forms An array + */ + public function __construct(array $forms) + { + // Skip the deprecation error + \ArrayIterator::__construct($forms); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Util/VirtualFormAwareIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/VirtualFormAwareIterator.php new file mode 100644 index 0000000..24fdc8b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/VirtualFormAwareIterator.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\Util; + +/** + * Iterator that returns only forms from a form tree that do not inherit their + * parent data. + * + * If the iterator encounters a form that inherits its parent data, it enters + * the form and traverses its children as well. + * + * @author Bernhard Schussek + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link InheritDataAwareIterator} instead. + */ +class VirtualFormAwareIterator extends \ArrayIterator implements \RecursiveIterator +{ + /** + * Creates a new iterator. + * + * @param \Symfony\Component\Form\FormInterface[] $forms An array + */ + public function __construct(array $forms) + { + // Uncomment this as soon as the deprecation note should be shown + // trigger_error('VirtualFormAwareIterator is deprecated since version 2.3 and will be removed in 3.0. Use InheritDataAwareIterator instead.', E_USER_DEPRECATED); + + parent::__construct($forms); + } + + public function getChildren() + { + return new static($this->current()->all()); + } + + public function hasChildren() + { + return $this->current()->getConfig()->getInheritData(); + } +} 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..7341501 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/composer.json @@ -0,0 +1,43 @@ +{ + "name": "symfony/form", + "type": "library", + "description": "Symfony Form Component", + "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.3", + "symfony/event-dispatcher": "~2.1", + "symfony/intl": "~2.3", + "symfony/options-resolver": "~2.1", + "symfony/property-access": "~2.2" + }, + "require-dev": { + "symfony/validator": "~2.2", + "symfony/http-foundation": "~2.2" + }, + "suggest": { + "symfony/validator": "", + "symfony/http-foundation": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Form\\": "" } + }, + "target-dir": "Symfony/Component/Form", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..d0d261f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./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..48c10c1 --- /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 Boolean + */ + 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..9d4c313 --- /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 Boolean + */ + 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..06d530d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php @@ -0,0 +1,280 @@ + + * + * 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; + + /** + * Constructor. + * + * @param SplFileInfo|string $file The file to stream + * @param integer $status The response status code + * @param array $headers An array of response headers + * @param boolean $public Files are public by default + * @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename + * @param boolean $autoEtag Whether the ETag header should be automatically set + * @param boolean $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(); + } + } + + /** + * {@inheritdoc} + */ + 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 Boolean $autoEtag + * @param Boolean $autoLastModified + * + * @return BinaryFileResponse + * + * @throws FileException + */ + public function setFile($file, $contentDisposition = null, $autoEtag = false, $autoLastModified = true) + { + $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()); + $this->headers->set('Accept-Ranges', 'bytes'); + $this->headers->set('Content-Transfer-Encoding', 'binary'); + + 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(); + if (strtolower($type) == 'x-accel-redirect') { + // Do X-Accel-Mapping substitutions. + foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) { + $mapping = explode('=', $mapping, 2); + + if (2 == count($mapping)) { + $location = trim($mapping[0]); + $pathPrefix = 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; + } + + $start = max($start, 0); + $end = min($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)); + } + } + + 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); + } + + /** + * {@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; + } +} 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..41e8eb2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -0,0 +1,98 @@ +CHANGELOG +========= + +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..68fe853 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Cookie.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\HttpFoundation; + +/** + * Represents a cookie + * + * @author Johannes M. Schmitt + * + * @api + */ +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 integer|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 Boolean $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client + * @param Boolean $httpOnly Whether the cookie will be made accessible only through the HTTP protocol + * + * @throws \InvalidArgumentException + * + * @api + */ + 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 = (Boolean) $secure; + $this->httpOnly = (Boolean) $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 + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the value of the cookie. + * + * @return string + * + * @api + */ + public function getValue() + { + return $this->value; + } + + /** + * Gets the domain that the cookie is available to. + * + * @return string + * + * @api + */ + public function getDomain() + { + return $this->domain; + } + + /** + * Gets the time the cookie expires. + * + * @return integer + * + * @api + */ + public function getExpiresTime() + { + return $this->expire; + } + + /** + * Gets the path on the server in which the cookie will be available on. + * + * @return string + * + * @api + */ + public function getPath() + { + return $this->path; + } + + /** + * Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client. + * + * @return Boolean + * + * @api + */ + public function isSecure() + { + return $this->secure; + } + + /** + * Checks whether the cookie will be made accessible only through the HTTP protocol. + * + * @return Boolean + * + * @api + */ + public function isHttpOnly() + { + return $this->httpOnly; + } + + /** + * Whether this cookie is about to be cleared + * + * @return Boolean + * + * @api + */ + public function isCleared() + { + return $this->expire < time(); + } +} 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..68f827b --- /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..bd70094 --- /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..382282e --- /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..9002b51 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/File.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\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 + * + * @api + */ +class File extends \SplFileInfo +{ + /** + * Constructs a new file from the given path. + * + * @param string $path The path to the file + * @param Boolean $checkPath Whether to check the path or not + * + * @throws FileNotFoundException If the given path is not a file + * + * @api + */ + 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 + * + * @api + * + * @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 + * + * @api + */ + public function getMimeType() + { + $guesser = MimeTypeGuesser::getInstance(); + + return $guesser->guess($this->getPathname()); + } + + /** + * Returns the extension of the file. + * + * \SplFileInfo::getExtension() is not available before PHP 5.3.6 + * + * @return string The extension + * + * @api + */ + public function getExtension() + { + return pathinfo($this->getBasename(), PATHINFO_EXTENSION); + } + + /** + * 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 + * + * @api + */ + 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)) { + 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 = $directory.DIRECTORY_SEPARATOR.(null === $name ? $this->getBasename() : $this->getName($name)); + + return new File($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..cc64618 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.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\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) { + $extension = $guesser->guess($mimeType); + + if (null !== $extension) { + break; + } + } + + 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..8373696 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.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\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..f23ddd2 --- /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 Boolean + */ + public static function isSupported() + { + return !defined('PHP_WINDOWS_VERSION_BUILD') && 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 null; + } + + 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 null; + } + + $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 null; + } + + 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..a6950df --- /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 Boolean + */ + 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 null; + } + + if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) { + return null; + } + + 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..42e7b77 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php @@ -0,0 +1,805 @@ + + * + * 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/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/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..f6bf2cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.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\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; + } + + /** + * 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) { + throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)'); + } + + 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..87ea20f --- /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..1f23c35 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/UploadedFile.php @@ -0,0 +1,305 @@ + + * + * 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 + * + * @api + */ +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 Boolean + */ + 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 integer + */ + 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 integer $size The file size + * @param integer $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants) + * @param Boolean $test Whether the test mode is active + * + * @throws FileException If file_uploads is disabled + * @throws FileNotFoundException If the file does not exist + * + * @api + */ + public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false) + { + if (!ini_get('file_uploads')) { + throw new FileException(sprintf('Unable to create UploadedFile because "file_uploads" is disabled in your php.ini file (%s)', get_cfg_var('cfg_file_path'))); + } + + $this->originalName = $this->getName($originalName); + $this->mimeType = $mimeType ?: 'application/octet-stream'; + $this->size = $size; + $this->error = $error ?: UPLOAD_ERR_OK; + $this->test = (Boolean) $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 is should not be considered as a safe value. + * + * @return string|null The original name + * + * @api + */ + public function getClientOriginalName() + { + return $this->originalName; + } + + /** + * Returns the original file extension + * + * It is extracted from the original file name that was uploaded. + * Then is 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 + * + * @api + */ + 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 is should not be considered as a safe value. + * + * @return integer|null The file size + * + * @api + */ + 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 integer The upload error + * + * @api + */ + public function getError() + { + return $this->error; + } + + /** + * Returns whether the file was uploaded successfully. + * + * @return Boolean True if the file has been uploaded with HTTP and no error occurred. + * + * @api + */ + 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 + * + * @api + */ + 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($this->getError())); + } + + /** + * 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() + { + $max = strtolower(ini_get('upload_max_filesize')); + + if ('' === $max) { + return PHP_INT_MAX; + } + + if (preg_match('#^\+?(0x?)?(.*?)([kmg]?)$#', $max, $match)) { + $shifts = array('' => 0, 'k' => 10, 'm' => 20, 'g' => 30); + $bases = array('' => 10, '0' => 8, '0x' => 16); + + return intval($match[2], $bases[$match[1]]) << $shifts[$match[3]]; + } + + return 0; + } + + /** + * Returns an informative upload error message. + * + * @param int $code The error code returned by an upload attempt + * + * @return string The error message regarding the specified error code + */ + private function getErrorMessage($errorCode) + { + static $errors = array( + UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d kb).', + 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.', + ); + + $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..b2775ef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/FileBag.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; + +use Symfony\Component\HttpFoundation\File\UploadedFile; + +/** + * FileBag is a container for HTTP headers. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + * + * @api + */ +class FileBag extends ParameterBag +{ + private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type'); + + /** + * Constructor. + * + * @param array $parameters An array of HTTP files + * + * @api + */ + public function __construct(array $parameters = array()) + { + $this->replace($parameters); + } + + /** + * {@inheritdoc} + * + * @api + */ + public function replace(array $files = array()) + { + $this->parameters = array(); + $this->add($files); + } + + /** + * {@inheritdoc} + * + * @api + */ + 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} + * + * @api + */ + 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 (array_keys($data['name']) as $key) { + $files[$key] = $this->fixPhpFilesArray(array( + 'error' => $data['error'][$key], + 'name' => $data['name'][$key], + '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..b579eb9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/HeaderBag.php @@ -0,0 +1,325 @@ + + * + * 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 + * + * @api + */ +class HeaderBag implements \IteratorAggregate, \Countable +{ + protected $headers; + protected $cacheControl; + + /** + * Constructor. + * + * @param array $headers An array of HTTP headers + * + * @api + */ + public function __construct(array $headers = array()) + { + $this->cacheControl = array(); + $this->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 + * + * @api + */ + public function all() + { + return $this->headers; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 Boolean $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 + * + * @api + */ + 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 Boolean $replace Whether to replace the actual value or not (true by default) + * + * @api + */ + 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 Boolean true if the parameter exists, false otherwise + * + * @api + */ + 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 Boolean true if the value is contained in the header, false otherwise + * + * @api + */ + public function contains($key, $value) + { + return in_array($value, $this->get($key, null, false)); + } + + /** + * Removes a header. + * + * @param string $key The HTTP header name + * + * @api + */ + 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 + * + * @api + */ + 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()); + } + + /** + * 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..7c3742e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/IpUtils.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; + +/** + * Http utility functions. + * + * @author Fabien Potencier + */ +class IpUtils +{ + /** + * This class should not be instantiated + */ + private function __construct() {} + + /** + * Validates an IPv4 or IPv6 address. + * + * @param string $requestIp + * @param string|array $ips + * + * @return boolean Whether the IP is valid + */ + public static function checkIp($requestIp, $ips) + { + if (!is_array($ips)) { + $ips = array($ips); + } + + $method = false !== strpos($requestIp, ':') ? 'checkIp6': 'checkIp4'; + + foreach ($ips as $ip) { + if (self::$method($requestIp, $ip)) { + return true; + } + } + + return false; + } + + /** + * Validates an IPv4 address. + * + * @param string $requestIp + * @param string $ip + * + * @return boolean Whether the IP is valid + */ + public static function checkIp4($requestIp, $ip) + { + if (false !== strpos($ip, '/')) { + list($address, $netmask) = explode('/', $ip, 2); + + if ($netmask < 1 || $netmask > 32) { + return false; + } + } else { + $address = $ip; + $netmask = 32; + } + + return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask); + } + + /** + * Validates an IPv6 address. + * + * @author David Soria Parra + * @see https://github.com/dsp/v6tools + * + * @param string $requestIp + * @param string $ip + * + * @return boolean 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..eafccaa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/JsonResponse.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; + +/** + * 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; + + /** + * Constructor. + * + * @param mixed $data The response data + * @param integer $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 $callback + * + * @return JsonResponse + * + * @throws \InvalidArgumentException + */ + 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 + */ + public function setData($data = array()) + { + // Encode <, >, ', &, and " for RFC4627-compliant JSON, which may also be embedded into HTML. + $this->data = json_encode($data, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); + + return $this->update(); + } + + /** + * 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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Component/HttpFoundation/ParameterBag.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ParameterBag.php new file mode 100644 index 0000000..c8720cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ParameterBag.php @@ -0,0 +1,305 @@ + + * + * 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 + * + * @api + */ +class ParameterBag implements \IteratorAggregate, \Countable +{ + /** + * Parameter storage. + * + * @var array + */ + protected $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * + * @api + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Returns the parameters. + * + * @return array An array of parameters + * + * @api + */ + public function all() + { + return $this->parameters; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + * + * @api + */ + public function keys() + { + return array_keys($this->parameters); + } + + /** + * Replaces the current parameters by a new set. + * + * @param array $parameters An array of parameters + * + * @api + */ + public function replace(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Adds parameters. + * + * @param array $parameters An array of parameters + * + * @api + */ + public function add(array $parameters = array()) + { + $this->parameters = array_replace($this->parameters, $parameters); + } + + /** + * Returns a parameter by name. + * + * @param string $path The key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return mixed + * + * @throws \InvalidArgumentException + * + * @api + */ + public function get($path, $default = null, $deep = false) + { + if (!$deep || false === $pos = strpos($path, '[')) { + return array_key_exists($path, $this->parameters) ? $this->parameters[$path] : $default; + } + + $root = substr($path, 0, $pos); + if (!array_key_exists($root, $this->parameters)) { + return $default; + } + + $value = $this->parameters[$root]; + $currentKey = null; + for ($i = $pos, $c = strlen($path); $i < $c; $i++) { + $char = $path[$i]; + + if ('[' === $char) { + if (null !== $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "[" at position %d.', $i)); + } + + $currentKey = ''; + } elseif (']' === $char) { + if (null === $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "]" at position %d.', $i)); + } + + if (!is_array($value) || !array_key_exists($currentKey, $value)) { + return $default; + } + + $value = $value[$currentKey]; + $currentKey = null; + } else { + if (null === $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "%s" at position %d.', $char, $i)); + } + + $currentKey .= $char; + } + } + + if (null !== $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Path must end with "]".')); + } + + return $value; + } + + /** + * Sets a parameter by name. + * + * @param string $key The key + * @param mixed $value The value + * + * @api + */ + public function set($key, $value) + { + $this->parameters[$key] = $value; + } + + /** + * Returns true if the parameter is defined. + * + * @param string $key The key + * + * @return Boolean true if the parameter exists, false otherwise + * + * @api + */ + public function has($key) + { + return array_key_exists($key, $this->parameters); + } + + /** + * Removes a parameter. + * + * @param string $key The key + * + * @api + */ + public function remove($key) + { + unset($this->parameters[$key]); + } + + /** + * Returns the alphabetic characters of the parameter value. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return string The filtered value + * + * @api + */ + public function getAlpha($key, $default = '', $deep = false) + { + return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default, $deep)); + } + + /** + * Returns the alphabetic characters and digits of the parameter value. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return string The filtered value + * + * @api + */ + public function getAlnum($key, $default = '', $deep = false) + { + return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default, $deep)); + } + + /** + * Returns the digits of the parameter value. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return string The filtered value + * + * @api + */ + public function getDigits($key, $default = '', $deep = false) + { + // we need to remove - and + because they're allowed in the filter + return str_replace(array('-', '+'), '', $this->filter($key, $default, $deep, FILTER_SANITIZE_NUMBER_INT)); + } + + /** + * Returns the parameter value converted to integer. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return integer The filtered value + * + * @api + */ + public function getInt($key, $default = 0, $deep = false) + { + return (int) $this->get($key, $default, $deep); + } + + /** + * Filter key. + * + * @param string $key Key. + * @param mixed $default Default = null. + * @param boolean $deep Default = false. + * @param integer $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, $deep = false, $filter=FILTER_DEFAULT, $options=array()) + { + $value = $this->get($key, $default, $deep); + + // 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..ed49b4e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/README.md @@ -0,0 +1,48 @@ +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: + + 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: + + $request = Request::create('/?foo=bar', 'GET'); + echo $request->getPathInfo(); + +And here is how to create and send a Response: + + $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. + +Loading +------- + +If you are not using Composer but are using PHP 5.3.x, you must add the following to your autoloader: + + // SessionHandlerInterface + if (!interface_exists('SessionHandlerInterface')) { + $loader->registerPrefixFallback(__DIR__.'/../vendor/symfony/src/Symfony/Component/HttpFoundation/Resources/stubs'); + } + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/HttpFoundation/ + $ composer.phar install --dev + $ 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..54f5721 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RedirectResponse.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; + +/** + * RedirectResponse represents an HTTP response doing a redirect. + * + * @author Fabien Potencier + * + * @api + */ +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 integer $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 + * + * @api + */ + 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..756e484 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Request.php @@ -0,0 +1,1710 @@ + + * + * 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 + * + * @api + */ +class Request +{ + const HEADER_CLIENT_IP = 'client_ip'; + const HEADER_CLIENT_HOST = 'client_host'; + const HEADER_CLIENT_PROTO = 'client_proto'; + const HEADER_CLIENT_PORT = 'client_port'; + + protected static $trustedProxies = array(); + + /** + * Names for headers that can be trusted when + * using trusted proxies. + * + * The default names are non-standard, but widely used + * by popular reverse proxies (like Apache mod_proxy or Amazon EC2). + */ + protected static $trustedHeaders = array( + 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; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $attributes; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $request; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $query; + + /** + * @var \Symfony\Component\HttpFoundation\ServerBag + * + * @api + */ + public $server; + + /** + * @var \Symfony\Component\HttpFoundation\FileBag + * + * @api + */ + public $files; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $cookies; + + /** + * @var \Symfony\Component\HttpFoundation\HeaderBag + * + * @api + */ + public $headers; + + /** + * @var string + */ + protected $content; + + /** + * @var array + */ + protected $languages; + + /** + * @var array + */ + protected $charsets; + + /** + * @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; + + /** + * 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 $content The raw body data + * + * @api + */ + 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 $content The raw body data + * + * @api + */ + 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->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 + * + * @api + */ + public static function createFromGlobals() + { + $request = new static($_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 + * + * @api + */ + 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; + } + + if (isset($components['query'])) { + parse_str(html_entity_decode($components['query']), $qs); + $query = array_replace($qs, $query); + } + $queryString = http_build_query($query, '', '&'); + + $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : ''); + $server['QUERY_STRING'] = $queryString; + + return new static($query, $request, array(), $cookies, $files, $server, $content); + } + + /** + * 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 + * + * @api + */ + 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->acceptableContentTypes = null; + $dup->pathInfo = null; + $dup->requestUri = null; + $dup->baseUrl = null; + $dup->basePath = null; + $dup->method = null; + $dup->format = 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() + { + return + sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n". + $this->headers."\r\n". + $this->getContent(); + } + + /** + * Overrides the PHP global variables according to this request instance. + * + * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE. + * $_FILES is never override, see rfc1867 + * + * @api + */ + public function overrideGlobals() + { + $_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('variable_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 + * + * @api + */ + 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 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 getClientHost()) + * * Request::HEADER_CLIENT_PORT: defaults to X-Forwarded-Port (see getClientPort()) + * * 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. + * + * 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 Boolean True when the _method request parameter is enabled, false otherwise + */ + public static function getHttpMethodParameterOverride() + { + return self::$httpMethodParameterOverride; + } + + /** + * Gets a "parameter" value. + * + * This method is mainly useful for libraries that want to provide some flexibility. + * + * Order of precedence: GET, PATH, POST + * + * Avoid using this method in controllers: + * + * * slow + * * prefer to get from a "named" source + * + * It is better to explicitly get request parameters from the appropriate + * public property instead (query, attributes, request). + * + * @param string $key the key + * @param mixed $default the default value + * @param Boolean $deep is parameter deep in multidimensional array + * + * @return mixed + */ + public function get($key, $default = null, $deep = false) + { + return $this->query->get($key, $this->attributes->get($key, $this->request->get($key, $default, $deep), $deep), $deep); + } + + /** + * Gets the Session. + * + * @return SessionInterface|null The session + * + * @api + */ + public function getSession() + { + return $this->session; + } + + /** + * Whether the request contains a Session which was started in one of the + * previous requests. + * + * @return Boolean + * + * @api + */ + 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 Boolean true when the Request contains a Session object, false otherwise + * + * @api + */ + public function hasSession() + { + return null !== $this->session; + } + + /** + * Sets the Session. + * + * @param SessionInterface $session The Session + * + * @api + */ + public function setSession(SessionInterface $session) + { + $this->session = $session; + } + + /** + * Returns the client IP addresses. + * + * The most trusted IP address is first, and the less trusted one last. + * The "real" client IP address is the last one, but this is also the + * less trusted one. + * + * Use this method carefully; you should use getClientIp() instead. + * + * @return array The client IP addresses + * + * @see getClientIp() + */ + public function getClientIps() + { + $ip = $this->server->get('REMOTE_ADDR'); + + if (!self::$trustedProxies) { + return array($ip); + } + + if (!self::$trustedHeaders[self::HEADER_CLIENT_IP] || !$this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { + return array($ip); + } + + $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); + $clientIps[] = $ip; + + $trustedProxies = !self::$trustedProxies ? array($ip) : self::$trustedProxies; + $ip = $clientIps[0]; + + foreach ($clientIps as $key => $clientIp) { + if (IpUtils::checkIp($clientIp, $trustedProxies)) { + unset($clientIps[$key]); + + continue; + } + } + + 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 + * + * @api + */ + public function getClientIp() + { + $ipAddresses = $this->getClientIps(); + + return $ipAddresses[0]; + } + + /** + * Returns current script name. + * + * @return string + * + * @api + */ + 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) + * + * @api + */ + 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) + * + * @api + */ + 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) + * + * @api + */ + public function getBaseUrl() + { + if (null === $this->baseUrl) { + $this->baseUrl = $this->prepareBaseUrl(); + } + + return $this->baseUrl; + } + + /** + * Gets the request's scheme. + * + * @return string + * + * @api + */ + 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 + * + * @api + */ + public function getPort() + { + if (self::$trustedProxies) { + 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; + } + } + + return $this->server->get('SERVER_PORT'); + } + + /** + * Returns the user. + * + * @return string|null + */ + public function getUser() + { + return $this->server->get('PHP_AUTH_USER'); + } + + /** + * Returns the password. + * + * @return string|null + */ + public function getPassword() + { + return $this->server->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 + * + * @api + */ + 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. + * + * @return string The raw URI (i.e. not urldecoded) + * + * @api + */ + 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 for the Request. + * + * @return string A normalized URI for the Request + * + * @see getQueryString() + * + * @api + */ + 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 + * + * @api + */ + public function getUriForPath($path) + { + return $this->getSchemeAndHttpHost().$this->getBaseUrl().$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 + * + * @api + */ + 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 port 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 Boolean + * + * @api + */ + public function isSecure() + { + if (self::$trustedProxies && self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && $proto = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO])) { + return in_array(strtolower($proto), array('https', 'on', '1')); + } + + return 'on' == strtolower($this->server->get('HTTPS')) || 1 == $this->server->get('HTTPS'); + } + + /** + * Returns the host name. + * + * This method can read the client port 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 + * + * @api + */ + public function getHost() + { + if (self::$trustedProxies && 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) + if ($host && !preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host)) { + throw new \UnexpectedValueException('Invalid Host'); + } + + return $host; + } + + /** + * Sets the request method. + * + * @param string $method + * + * @api + */ + 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 + * + * @api + * + * @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) + * + * @api + */ + 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) + * + * @api + */ + 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; + } + } + + return null; + } + + /** + * 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) + * + * @api + */ + 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 + * + * @api + */ + public function getRequestFormat($default = 'html') + { + if (null === $this->format) { + $this->format = $this->get('_format', $default); + } + + return $this->format; + } + + /** + * Sets the request format. + * + * @param string $format The request format. + * + * @api + */ + 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) + * + * @api + */ + public function getContentType() + { + return $this->getFormat($this->headers->get('CONTENT_TYPE')); + } + + /** + * Sets the default locale. + * + * @param string $locale + * + * @api + */ + public function setDefaultLocale($locale) + { + $this->defaultLocale = $locale; + + if (null === $this->locale) { + $this->setPhpDefaultLocale($locale); + } + } + + /** + * Sets the locale. + * + * @param string $locale + * + * @api + */ + 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 Boolean + */ + public function isMethod($method) + { + return $this->getMethod() === strtoupper($method); + } + + /** + * Checks whether the method is safe or not. + * + * @return Boolean + * + * @api + */ + public function isMethodSafe() + { + return in_array($this->getMethod(), array('GET', 'HEAD')); + } + + /** + * Returns the request body content. + * + * @param Boolean $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) + { + if (false === $this->content || (true === $asResource && null !== $this->content)) { + throw new \LogicException('getContent() can only be called once when using the resource return type.'); + } + + if (true === $asResource) { + $this->content = false; + + return fopen('php://input', 'rb'); + } + + 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 Boolean + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + public function getLanguages() + { + if (null !== $this->languages) { + return $this->languages; + } + + $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all(); + $this->languages = array(); + foreach (array_keys($languages) as $lang) { + if (strstr($lang, '-')) { + $codes = explode('-', $lang); + if ($codes[0] == 'i') { + // 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 + * + * @api + */ + 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 content types acceptable by the client browser + * + * @return array List of content types in preferable order + * + * @api + */ + 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 set 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 Boolean true if the request is an XMLHttpRequest, false otherwise + * + * @api + */ + 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, dirname($baseUrl))) { + // directory portion of $baseUrl matches + return rtrim($prefix, '/'); + } + + $truncatedRequestUri = $requestUri; + if (($pos = strpos($requestUri, '?')) !== false) { + $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, '/'); + } + + /** + * 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); + } + + if ((null !== $baseUrl) && (false === ($pathInfo = substr($requestUri, strlen($baseUrl))))) { + // 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'), + ); + } + + /** + * 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("#^(%[[:xdigit:]]{2}|.){{$len}}#", $string, $match)) { + return $match[0]; + } + + return false; + } +} 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..769ca66 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RequestMatcher.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\HttpFoundation; + +/** + * RequestMatcher compares a pre-defined set of checks against a Request instance. + * + * @author Fabien Potencier + * + * @api + */ +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(); + + /** + * @param string|null $path + * @param string|null $host + * @param string|string[]|null $methods + * @param string|string[]|null $ips + * @param array $attributes + */ + public function __construct($path = null, $host = null, $methods = null, $ips = null, array $attributes = array()) + { + $this->matchPath($path); + $this->matchHost($host); + $this->matchMethod($methods); + $this->matchIps($ips); + foreach ($attributes as $k => $v) { + $this->matchAttribute($k, $v); + } + } + + /** + * 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[]|null $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} + * + * @api + */ + public function matches(Request $request) + { + 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..695fd21 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.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; + +/** + * RequestMatcherInterface is an interface for strategies to match a Request. + * + * @author Fabien Potencier + * + * @api + */ +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 Boolean true if the request matches, false otherwise + * + * @api + */ + public function matches(Request $request); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php new file mode 100644 index 0000000..b6bbfc2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * SessionHandlerInterface + * + * Provides forward compatibility with PHP 5.4 + * + * Extensive documentation can be found at php.net, see links: + * + * @see http://php.net/sessionhandlerinterface + * @see http://php.net/session.customhandler + * @see http://php.net/session-set-save-handler + * + * @author Drak + */ +interface SessionHandlerInterface +{ + /** + * Open session. + * + * @see http://php.net/sessionhandlerinterface.open + * + * @param string $savePath Save path. + * @param string $sessionName Session Name. + * + * @throws \RuntimeException If something goes wrong starting the session. + * + * @return boolean + */ + public function open($savePath, $sessionName); + + /** + * Close session. + * + * @see http://php.net/sessionhandlerinterface.close + * + * @return boolean + */ + public function close(); + + /** + * Read session. + * + * @param string $sessionId + * + * @see http://php.net/sessionhandlerinterface.read + * + * @throws \RuntimeException On fatal error but not "record not found". + * + * @return string String as stored in persistent storage or empty string in all other cases. + */ + public function read($sessionId); + + /** + * Commit session to storage. + * + * @see http://php.net/sessionhandlerinterface.write + * + * @param string $sessionId Session ID. + * @param string $data Session serialized data to save. + * + * @return boolean + */ + public function write($sessionId, $data); + + /** + * Destroys this session. + * + * @see http://php.net/sessionhandlerinterface.destroy + * + * @param string $sessionId Session ID. + * + * @throws \RuntimeException On fatal error. + * + * @return boolean + */ + public function destroy($sessionId); + + /** + * Garbage collection for storage. + * + * @see http://php.net/sessionhandlerinterface.gc + * + * @param integer $lifetime Max lifetime in seconds to keep sessions stored. + * + * @throws \RuntimeException On fatal error. + * + * @return boolean + */ + public function gc($lifetime); +} 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..c4c5a53 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Response.php @@ -0,0 +1,1188 @@ + + * + * 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 + * + * @api + */ +class Response +{ + /** + * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag + */ + public $headers; + + /** + * @var string + */ + protected $content; + + /** + * @var string + */ + protected $version; + + /** + * @var integer + */ + 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 2012-02-13). + * + * 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', + 306 => 'Reserved', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', // RFC-reschke-http-status-308-07 + 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 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested 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 string $content The response content + * @param integer $status The response status code + * @param array $headers An array of response headers + * + * @throws \InvalidArgumentException When the HTTP status code is not valid + * + * @api + */ + public function __construct($content = '', $status = 200, $headers = array()) + { + $this->headers = new ResponseHeaderBag($headers); + $this->setContent($content); + $this->setStatusCode($status); + $this->setProtocolVersion('1.0'); + if (!$this->headers->has('Date')) { + $this->setDate(new \DateTime(null, new \DateTimeZone('UTC'))); + } + } + + /** + * Factory method for chainability + * + * Example: + * + * return Response::create($body, 200) + * ->setSharedMaxAge(300); + * + * @param string $content The response content + * @param integer $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() || in_array($this->statusCode, array(204, 304))) { + $this->setContent(null); + } + + // 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 === strpos($headers->get('Content-Type'), 'text/') && false === strpos($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; + } + + // status + header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)); + + // headers + foreach ($this->headers->allPreserveCase() as $name => $values) { + foreach ($values as $value) { + header($name.': '.$value, false); + } + } + + // 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 + * + * @api + */ + public function send() + { + $this->sendHeaders(); + $this->sendContent(); + + if (function_exists('fastcgi_finish_request')) { + fastcgi_finish_request(); + } elseif ('cli' !== PHP_SAPI) { + // ob_get_level() never returns 0 on some Windows configurations, so if + // the level is the same two times in a row, the loop should be stopped. + $previous = null; + $obStatus = ob_get_status(1); + while (($level = ob_get_level()) > 0 && $level !== $previous) { + $previous = $level; + if ($obStatus[$level - 1] && isset($obStatus[$level - 1]['del']) && $obStatus[$level - 1]['del']) { + ob_end_flush(); + } + } + flush(); + } + + return $this; + } + + /** + * Sets the response content. + * + * Valid types are strings, numbers, and objects that implement a __toString() method. + * + * @param mixed $content + * + * @return Response + * + * @throws \UnexpectedValueException + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + public function setProtocolVersion($version) + { + $this->version = $version; + + return $this; + } + + /** + * Gets the HTTP protocol version. + * + * @return string The HTTP protocol version + * + * @api + */ + public function getProtocolVersion() + { + return $this->version; + } + + /** + * Sets the response status code. + * + * @param integer $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 + * + * @api + */ + 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] : ''; + + return $this; + } + + if (false === $text) { + $this->statusText = ''; + + return $this; + } + + $this->statusText = $text; + + return $this; + } + + /** + * Retrieves the status code for the current web response. + * + * @return integer Status code + * + * @api + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Sets the response charset. + * + * @param string $charset Character set + * + * @return Response + * + * @api + */ + public function setCharset($charset) + { + $this->charset = $charset; + + return $this; + } + + /** + * Retrieves the response charset. + * + * @return string Character set + * + * @api + */ + 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 Boolean true if the response is worth caching, false otherwise + * + * @api + */ + 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 Boolean true if the response is fresh, false otherwise + * + * @api + */ + 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 Boolean true if the response is validateable, false otherwise + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 Boolean true if the response must be revalidated by a cache, false otherwise + * + * @api + */ + public function mustRevalidate() + { + return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->has('proxy-revalidate'); + } + + /** + * Returns the Date header as a DateTime instance. + * + * @return \DateTime A \DateTime instance + * + * @throws \RuntimeException When the header is not parseable + * + * @api + */ + public function getDate() + { + return $this->headers->getDate('Date', new \DateTime()); + } + + /** + * Sets the Date header. + * + * @param \DateTime $date A \DateTime instance + * + * @return Response + * + * @api + */ + 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 integer 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 integer|null Number of seconds + * + * @api + */ + 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'); + } + + return null; + } + + /** + * 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 integer $value Number of seconds + * + * @return Response + * + * @api + */ + 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 integer $value Number of seconds + * + * @return Response + * + * @api + */ + 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 integer|null The TTL in seconds + * + * @api + */ + public function getTtl() + { + if (null !== $maxAge = $this->getMaxAge()) { + return $maxAge - $this->getAge(); + } + + return null; + } + + /** + * Sets the response's time-to-live for shared caches. + * + * This method adjusts the Cache-Control/s-maxage directive. + * + * @param integer $seconds Number of seconds + * + * @return Response + * + * @api + */ + 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 integer $seconds Number of seconds + * + * @return Response + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 Boolean $weak Whether you want a weak ETag or not + * + * @return Response + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 Boolean true if the response includes a Vary header, false otherwise + * + * @api + */ + 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 + * + * @api + */ + public function getVary() + { + if (!$vary = $this->headers->get('Vary')) { + return array(); + } + + return is_array($vary) ? $vary : preg_split('/[\s,]+/', $vary); + } + + /** + * Sets the Vary header. + * + * @param string|array $headers + * @param Boolean $replace Whether to replace the actual value of not (true by default) + * + * @return Response + * + * @api + */ + 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 Boolean true if the Response validators match the Request, false otherwise + * + * @api + */ + public function isNotModified(Request $request) + { + if (!$request->isMethodSafe()) { + return false; + } + + $lastModified = $request->headers->get('If-Modified-Since'); + $notModified = false; + if ($etags = $request->getEtags()) { + $notModified = (in_array($this->getEtag(), $etags) || in_array('*', $etags)) && (!$lastModified || $this->headers->get('Last-Modified') == $lastModified); + } elseif ($lastModified) { + $notModified = $lastModified == $this->headers->get('Last-Modified'); + } + + if ($notModified) { + $this->setNotModified(); + } + + return $notModified; + } + + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + /** + * Is response invalid? + * + * @return Boolean + * + * @api + */ + public function isInvalid() + { + return $this->statusCode < 100 || $this->statusCode >= 600; + } + + /** + * Is response informative? + * + * @return Boolean + * + * @api + */ + public function isInformational() + { + return $this->statusCode >= 100 && $this->statusCode < 200; + } + + /** + * Is response successful? + * + * @return Boolean + * + * @api + */ + public function isSuccessful() + { + return $this->statusCode >= 200 && $this->statusCode < 300; + } + + /** + * Is the response a redirect? + * + * @return Boolean + * + * @api + */ + public function isRedirection() + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + + /** + * Is there a client error? + * + * @return Boolean + * + * @api + */ + public function isClientError() + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + + /** + * Was there a server side error? + * + * @return Boolean + * + * @api + */ + public function isServerError() + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + + /** + * Is the response OK? + * + * @return Boolean + * + * @api + */ + public function isOk() + { + return 200 === $this->statusCode; + } + + /** + * Is the response forbidden? + * + * @return Boolean + * + * @api + */ + public function isForbidden() + { + return 403 === $this->statusCode; + } + + /** + * Is the response a not found error? + * + * @return Boolean + * + * @api + */ + public function isNotFound() + { + return 404 === $this->statusCode; + } + + /** + * Is the response a redirect of some form? + * + * @param string $location + * + * @return Boolean + * + * @api + */ + 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 Boolean + * + * @api + */ + public function isEmpty() + { + return in_array($this->statusCode, array(201, 204, 304)); + } + + /** + * Check 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 (intval(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..f52f048 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.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\HttpFoundation; + +/** + * ResponseHeaderBag is a container for Response HTTP headers. + * + * @author Fabien Potencier + * + * @api + */ +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 + * + * @api + */ + 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} + * + * @api + */ + public function replace(array $headers = array()) + { + $this->headerNames = array(); + + parent::replace($headers); + + if (!isset($this->headers['cache-control'])) { + $this->set('Cache-Control', ''); + } + } + + /** + * {@inheritdoc} + * + * @api + */ + 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} + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + public function clearCookie($name, $path = '/', $domain = null) + { + $this->setCookie(new Cookie($name, null, 1, $path, $domain)); + } + + /** + * 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..d7aadc6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ServerBag.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; + +/** + * 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']; + } + + // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic + if ((null !== $authorizationHeader) && (0 === stripos($authorizationHeader, 'basic'))) { + $exploded = explode(':', base64_decode(substr($authorizationHeader, 6))); + if (count($exploded) == 2) { + list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded; + } + } + } + + // 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']); + } + + 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..e9d0257 --- /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..5f1f37b --- /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 Boolean 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 + */ + 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..25dcd22 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.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\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) + { + $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) + { + $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 (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 boolean $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 (strpos($name, $this->namespaceCharacter) !== false) { + $name = substr($name, strrpos($name, $this->namespaceCharacter)+1, strlen($name)); + } + + 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..c6e41de --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.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\HttpFoundation\Session\Flash; + +/** + * AutoExpireFlashBag flash message container. + * + * @author Drak + */ +class AutoExpireFlashBag 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; + $this->flashes = array('display' => array(), 'new' => array()); + } + + /** + * {@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..d62e383 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.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\HttpFoundation\Session\Flash; + +/** + * FlashBag flash message container. + * + * @author Drak + */ +class FlashBag implements FlashBagInterface, \IteratorAggregate +{ + 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(); + } + + /** + * Returns an iterator for flashes. + * + * @return \ArrayIterator An \ArrayIterator instance + */ + public function getIterator() + { + return new \ArrayIterator($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..a68dcfd --- /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 boolean + */ + 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..15df097 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Session.php @@ -0,0 +1,252 @@ + + * + * 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\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; + +/** + * Session. + * + * @author Fabien Potencier + * @author Drak + * + * @api + */ +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..f8d3d32 --- /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..a94fad0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/SessionInterface.php @@ -0,0 +1,208 @@ + + * + * 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 Boolean True if session started. + * + * @throws \RuntimeException If session fails to start. + * + * @api + */ + public function start(); + + /** + * Returns the session ID. + * + * @return string The session ID. + * + * @api + */ + public function getId(); + + /** + * Sets the session ID + * + * @param string $id + * + * @api + */ + public function setId($id); + + /** + * Returns the session name. + * + * @return mixed The session name. + * + * @api + */ + public function getName(); + + /** + * Sets the session name. + * + * @param string $name + * + * @api + */ + 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 integer $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 Boolean True if session invalidated, false if error. + * + * @api + */ + public function invalidate($lifetime = null); + + /** + * Migrates the current session to a new session id while maintaining all + * session attributes. + * + * @param Boolean $destroy Whether to delete the old session or leave it to garbage collection. + * @param integer $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 Boolean True if session migrated, false if error. + * + * @api + */ + 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 Boolean true if the attribute is defined, false otherwise + * + * @api + */ + public function has($name); + + /** + * Returns an attribute. + * + * @param string $name The attribute name + * @param mixed $default The default value if not found. + * + * @return mixed + * + * @api + */ + public function get($name, $default = null); + + /** + * Sets an attribute. + * + * @param string $name + * @param mixed $value + * + * @api + */ + public function set($name, $value); + + /** + * Returns attributes. + * + * @return array Attributes + * + * @api + */ + 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 + * + * @api + */ + public function remove($name); + + /** + * Clears all attributes. + * + * @api + */ + public function clear(); + + /** + * Checks if the session was started. + * + * @return Boolean + */ + 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..4a5e639 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.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\HttpFoundation\Session\Storage\Handler; + +/** + * MemcacheSessionHandler. + * + * @author Drak + */ +class MemcacheSessionHandler implements \SessionHandlerInterface +{ + /** + * @var \Memcache Memcache driver. + */ + private $memcache; + + /** + * @var integer 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($lifetime) + { + // not required here because memcache will auto expire the records anyhow. + return true; + } +} 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..b3ca0bd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.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\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 integer 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($lifetime) + { + // not required here because memcached will auto expire the records anyhow. + return true; + } +} 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..69ebae9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.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\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] + * + * @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', + ), $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($lifetime) + { + /* Note: MongoDB 2.2+ supports TTL collections, which may be used in + * place of this method by indexing the "time_field" field with an + * "expireAfterSeconds" option. Regardless of whether TTL collections + * are used, consider indexing this field to make the remove query more + * efficient. + * + * See: http://docs.mongodb.org/manual/tutorial/expire-data/ + */ + $time = new \MongoDate(time() - $lifetime); + + $this->getCollection()->remove(array( + $this->options['time_field'] => array('$lt' => $time), + )); + + return true; + } + + /** + * {@inheritDoc] + */ + public function write($sessionId, $data) + { + $this->getCollection()->update( + array($this->options['id_field'] => $sessionId), + array('$set' => array( + $this->options['data_field'] => new \MongoBinData($data, \MongoBinData::BYTE_ARRAY), + $this->options['time_field'] => new \MongoDate(), + )), + array('upsert' => true, 'multiple' => false) + ); + + return true; + } + + /** + * {@inheritDoc} + */ + public function read($sessionId) + { + $dbData = $this->getCollection()->findOne(array( + $this->options['id_field'] => $sessionId, + )); + + 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; + } +} 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..1260ad0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.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\HttpFoundation\Session\Storage\Handler; + +/** + * Adds SessionHandler functionality if available. + * + * @see http://php.net/sessionhandler + */ + +if (version_compare(phpversion(), '5.4.0', '>=')) { + class NativeSessionHandler extends \SessionHandler {} +} else { + class NativeSessionHandler {} +} 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..62068af --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.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\Storage\Handler; + +/** + * NullSessionHandler. + * + * Can be used in unit testing or in a situations where persisted sessions are not desired. + * + * @author Drak + * + * @api + */ +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($lifetime) + { + 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..347cbee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -0,0 +1,243 @@ + + * + * 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; + +/** + * PdoSessionHandler. + * + * @author Fabien Potencier + * @author Michael Williams + */ +class PdoSessionHandler implements \SessionHandlerInterface +{ + /** + * @var \PDO PDO instance. + */ + private $pdo; + + /** + * @var array Database options. + */ + private $dbOptions; + + /** + * Constructor. + * + * List of available options: + * * db_table: The name of the table [required] + * * 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_time_col: The column where to store the timestamp [default: sess_time] + * + * @param \PDO $pdo A \PDO instance + * @param array $dbOptions An associative array of DB options + * + * @throws \InvalidArgumentException When "db_table" option is not provided + */ + public function __construct(\PDO $pdo, array $dbOptions = array()) + { + if (!array_key_exists('db_table', $dbOptions)) { + throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.'); + } + if (\PDO::ERRMODE_EXCEPTION !== $pdo->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 = $pdo; + $this->dbOptions = array_merge(array( + 'db_id_col' => 'sess_id', + 'db_data_col' => 'sess_data', + 'db_time_col' => 'sess_time', + ), $dbOptions); + } + + /** + * {@inheritDoc} + */ + public function open($path, $name) + { + return true; + } + + /** + * {@inheritDoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function destroy($id) + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbIdCol = $this->dbOptions['db_id_col']; + + // delete the record associated with this id + $sql = "DELETE FROM $dbTable WHERE $dbIdCol = :id"; + + try { + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + $stmt->execute(); + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function gc($lifetime) + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbTimeCol = $this->dbOptions['db_time_col']; + + // delete the session records that have expired + $sql = "DELETE FROM $dbTable WHERE $dbTimeCol < :time"; + + try { + $stmt = $this->pdo->prepare($sql); + $stmt->bindValue(':time', time() - $lifetime, \PDO::PARAM_INT); + $stmt->execute(); + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function read($id) + { + // get table/columns + $dbTable = $this->dbOptions['db_table']; + $dbDataCol = $this->dbOptions['db_data_col']; + $dbIdCol = $this->dbOptions['db_id_col']; + + try { + $sql = "SELECT $dbDataCol FROM $dbTable WHERE $dbIdCol = :id"; + + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + + $stmt->execute(); + // it is recommended to use fetchAll so that PDO can close the DB cursor + // we anyway expect either no rows, or one row with one column. fetchColumn, seems to be buggy #4777 + $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM); + + if (count($sessionRows) == 1) { + return base64_decode($sessionRows[0][0]); + } + + // session does not exist, create it + $this->createNewSession($id); + + return ''; + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e); + } + } + + /** + * {@inheritDoc} + */ + public function write($id, $data) + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbDataCol = $this->dbOptions['db_data_col']; + $dbIdCol = $this->dbOptions['db_id_col']; + $dbTimeCol = $this->dbOptions['db_time_col']; + + //session data can contain non binary safe characters so we need to encode it + $encoded = base64_encode($data); + + try { + $driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); + + if ('mysql' === $driver) { + // MySQL would report $stmt->rowCount() = 0 on UPDATE when the data is left unchanged + // it could result in calling createNewSession() whereas the session already exists in + // the DB which would fail as the id is unique + $stmt = $this->pdo->prepare( + "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time) " . + "ON DUPLICATE KEY UPDATE $dbDataCol = VALUES($dbDataCol), $dbTimeCol = VALUES($dbTimeCol)" + ); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + $stmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + } elseif ('oci' === $driver) { + $stmt = $this->pdo->prepare("MERGE INTO $dbTable USING DUAL ON($dbIdCol = :id) ". + "WHEN NOT MATCHED THEN INSERT ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, sysdate) " . + "WHEN MATCHED THEN UPDATE SET $dbDataCol = :data WHERE $dbIdCol = :id"); + + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + $stmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $stmt->execute(); + } else { + $stmt = $this->pdo->prepare("UPDATE $dbTable SET $dbDataCol = :data, $dbTimeCol = :time WHERE $dbIdCol = :id"); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + $stmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + + if (!$stmt->rowCount()) { + // No session exists in the database to update. This happens when we have called + // session_regenerate_id() + $this->createNewSession($id, $data); + } + } + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * Creates a new session with the given $id and $data + * + * @param string $id + * @param string $data + * + * @return boolean True. + */ + private function createNewSession($id, $data = '') + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbDataCol = $this->dbOptions['db_data_col']; + $dbIdCol = $this->dbOptions['db_id_col']; + $dbTimeCol = $this->dbOptions['db_time_col']; + + $sql = "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time)"; + + //session data can contain non binary safe characters so we need to encode it + $encoded = base64_encode($data); + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + $stmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + + return true; + } +} 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..892d004 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.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\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(); + + /** + * Unix timestamp. + * + * @var integer + */ + private $lastUsed; + + /** + * Constructor. + * + * @param string $storageKey The key used to store bag in the session. + */ + public function __construct($storageKey = '_sf2_meta') + { + $this->storageKey = $storageKey; + $this->meta = array(self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0); + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$array) + { + $this->meta = &$array; + + if (isset($array[self::CREATED])) { + $this->lastUsed = $this->meta[self::UPDATED]; + $this->meta[self::UPDATED] = time(); + } else { + $this->stampCreated(); + } + } + + /** + * Gets the lifetime that the session cookie was set with. + * + * @return integer + */ + public function getLifetime() + { + return $this->meta[self::LIFETIME]; + } + + /** + * Stamps a new session's metadata. + * + * @param integer $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 integer Unix timestamp + */ + public function getCreated() + { + return $this->meta[self::CREATED]; + } + + /** + * Gets the last used metadata. + * + * @return integer 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..a1fcf53 --- /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; +use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; + +/** + * 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 boolean + */ + protected $started = false; + + /** + * @var boolean + */ + 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 && !$this->closed) { + 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; + } + + /** + * {@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 sha1(uniqid(mt_rand())); + } + + 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..2806309 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.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\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; + + /** + * @var array + */ + private $sessionData; + + /** + * 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..36d87a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php @@ -0,0 +1,417 @@ + + * + * 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\MetadataBag; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy; +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 Boolean + */ + protected $started = false; + + /** + * @var Boolean + */ + 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, "nocache" (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); + + if (version_compare(phpversion(), '5.4.0', '>=')) { + session_register_shutdown(); + } else { + register_shutdown_function('session_write_close'); + } + + $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 && !$this->closed) { + return true; + } + + if (version_compare(phpversion(), '5.4.0', '>=') && \PHP_SESSION_ACTIVE === session_status()) { + throw new \RuntimeException('Failed to start the session: already started by PHP.'); + } + + if (version_compare(phpversion(), '5.4.0', '<') && isset($_SESSION) && session_id()) { + // not 100% fool-proof, but is the most reliable way to determine if a session is active in PHP 5.3 + throw new \RuntimeException('Failed to start the session: already started by PHP ($_SESSION is set).'); + } + + 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(); + if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) { + // This condition matches only PHP 5.3 with internal save handlers + $this->saveHandler->setActive(true); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + if (!$this->started) { + return ''; // returning empty is consistent with session_id() behaviour + } + + 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) + { + if (null !== $lifetime) { + ini_set('session.cookie_lifetime', $lifetime); + } + + if ($destroy) { + $this->metadataBag->stampNew(); + } + + return session_regenerate_id($destroy); + } + + /** + * {@inheritdoc} + */ + public function save() + { + session_write_close(); + + if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) { + // This condition matches only PHP 5.3 with internal save handlers + $this->saveHandler->setActive(false); + } + + $this->closed = true; + } + + /** + * {@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 = version_compare(phpversion(), '5.4.0', '>=') ? + new SessionHandlerProxy(new \SessionHandler()) : new NativeProxy(); + } + $this->saveHandler = $saveHandler; + + if ($this->saveHandler instanceof \SessionHandlerInterface) { + if (version_compare(phpversion(), '5.4.0', '>=')) { + session_set_save_handler($this->saveHandler, false); + } else { + session_set_save_handler( + array($this->saveHandler, 'open'), + array($this->saveHandler, 'close'), + array($this->saveHandler, 'read'), + array($this->saveHandler, 'write'), + array($this->saveHandler, 'destroy'), + array($this->saveHandler, 'gc') + ); + } + } + } + + /** + * 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..0f00203 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/PhpBridgeSessionStorage.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\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; +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 Symfony2 + * + * @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 && !$this->closed) { + return true; + } + + $this->loadSession(); + if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) { + // This condition matches only PHP 5.3 + internal save handlers + $this->saveHandler->setActive(true); + } + + 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..ee6eb89 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.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\Session\Storage\Proxy; + +/** + * AbstractProxy. + * + * @author Drak + */ +abstract class AbstractProxy +{ + /** + * Flag if handler wraps an internal PHP session handler (using \SessionHandler). + * + * @var boolean + */ + protected $wrapper = false; + + /** + * @var boolean + */ + protected $active = 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 boolean + */ + public function isSessionHandlerInterface() + { + return ($this instanceof \SessionHandlerInterface); + } + + /** + * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler. + * + * @return Boolean + */ + public function isWrapper() + { + return $this->wrapper; + } + + /** + * Has a session started? + * + * @return Boolean + */ + public function isActive() + { + if (version_compare(phpversion(), '5.4.0', '>=')) { + return $this->active = \PHP_SESSION_ACTIVE === session_status(); + } + + return $this->active; + } + + /** + * Sets the active flag. + * + * Has no effect under PHP 5.4+ as status is detected + * automatically in isActive() + * + * @internal + * + * @param Boolean $flag + * + * @throws \LogicException + */ + public function setActive($flag) + { + if (version_compare(phpversion(), '5.4.0', '>=')) { + throw new \LogicException('This method is disabled in PHP 5.4.0+'); + } + + $this->active = (bool) $flag; + } + + /** + * 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..23eebb3 --- /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 Boolean 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..e1f4fff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.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\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); + + if (true === $return) { + $this->active = true; + } + + return $return; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->active = false; + + return (bool) $this->handler->close(); + } + + /** + * {@inheritdoc} + */ + public function read($id) + { + return (string) $this->handler->read($id); + } + + /** + * {@inheritdoc} + */ + public function write($id, $data) + { + return (bool) $this->handler->write($id, $data); + } + + /** + * {@inheritdoc} + */ + public function destroy($id) + { + return (bool) $this->handler->destroy($id); + } + + /** + * {@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..711eaa2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.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\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; + +/** + * StorageInterface. + * + * @author Fabien Potencier + * @author Drak + * + * @api + */ +interface SessionStorageInterface +{ + /** + * Starts the session. + * + * @throws \RuntimeException If something goes wrong starting the session. + * + * @return boolean True if started. + * + * @api + */ + public function start(); + + /** + * Checks if the session is started. + * + * @return boolean True if started, false otherwise. + */ + public function isStarted(); + + /** + * Returns the session ID + * + * @return string The session ID or empty. + * + * @api + */ + public function getId(); + + /** + * Sets the session ID + * + * @param string $id + * + * @api + */ + public function setId($id); + + /** + * Returns the session name + * + * @return mixed The session name. + * + * @api + */ + public function getName(); + + /** + * Sets the session name + * + * @param string $name + * + * @api + */ + 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. + * + * @param Boolean $destroy Destroy session when regenerating? + * @param integer $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 Boolean True if session regenerated, false if error + * + * @throws \RuntimeException If an error occurs while regenerating this storage + * + * @api + */ + 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..ae579be --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/StreamedResponse.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\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 + * + * @api + */ +class StreamedResponse extends Response +{ + protected $callback; + protected $streamed; + + /** + * Constructor. + * + * @param mixed $callback A valid PHP callback + * @param integer $status The response status code + * @param array $headers An array of response headers + * + * @api + */ + public function __construct($callback = null, $status = 200, $headers = array()) + { + parent::__construct(null, $status, $headers); + + if (null !== $callback) { + $this->setCallback($callback); + } + $this->streamed = false; + } + + /** + * {@inheritDoc} + */ + 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 mixed $callback A valid PHP callback + * + * @throws \LogicException + */ + public function setCallback($callback) + { + if (!is_callable($callback)) { + throw new \LogicException('The Response callback must be a valid PHP callable.'); + } + $this->callback = $callback; + } + + /** + * {@inheritdoc} + */ + public function prepare(Request $request) + { + $this->headers->set('Cache-Control', 'no-cache'); + + return parent::prepare($request); + } + + /** + * {@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..582fbde --- /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..965a7d2 --- /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..c3d324f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.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\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; + +class BinaryFileResponseTest extends ResponseTestCase +{ + public function testConstruction() + { + $response = new BinaryFileResponse('README.md', 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('README.md', 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('README.md'); + $response->setContent('foo'); + } + + public function testGetContent() + { + $response = new BinaryFileResponse('README.md'); + $this->assertFalse($response->getContent()); + } + + /** + * @dataProvider provideRanges + */ + public function testRequests($requestRange, $offset, $length, $responseRange) + { + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif')->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('binary', $response->headers->get('Content-Transfer-Encoding')); + $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=-35', 0, 35, 'bytes 0-34/35'), + array('bytes=-40', 0, 35, 'bytes 0-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'), + array('bytes=30-40', 30, 5, 'bytes 30-34/35') + ); + } + + public function testXSendfile() + { + $request = Request::create('/'); + $request->headers->set('X-Sendfile-Type', 'X-Sendfile'); + + BinaryFileResponse::trustXSendfileTypeHeader(); + $response = BinaryFileResponse::create('README.md'); + $response->prepare($request); + + $this->expectOutputString(''); + $response->sendContent(); + + $this->assertContains('README.md', $response->headers->get('X-Sendfile')); + } + + /** + * @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 = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\File') + ->disableOriginalConstructor() + ->getMock(); + $file->expects($this->any()) + ->method('getRealPath') + ->will($this->returnValue($realpath)); + $file->expects($this->any()) + ->method('isReadable') + ->will($this->returnValue(true)); + + BinaryFileResponse::trustXSendFileTypeHeader(); + $response = new BinaryFileResponse('README.md'); + $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 getSampleXAccelMappings() + { + return array( + array('/var/www/var/www/files/foo.txt', '/files/=/var/www/', '/files/var/www/files/foo.txt'), + array('/home/foo/bar.txt', '/files/=/var/www/,/baz/=/home/foo/', '/baz/bar.txt'), + ); + } + + protected function provideResponse() + { + return new BinaryFileResponse('README.md'); + } +} 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..f4c9ec1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/CookieTest.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\Tests; + +use Symfony\Component\HttpFoundation\Cookie; + +/** + * CookieTest + * + * @author John Kary + * @author Hugo Hamon + */ +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 + * @covers Symfony\Component\HttpFoundation\Cookie::__construct + */ + public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name) + { + new Cookie($name); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidExpiration() + { + $cookie = new Cookie('MyCookie', 'foo','bar'); + } + + /** + * @covers Symfony\Component\HttpFoundation\Cookie::getValue + */ + 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'); + } + + 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/File/FileTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/FileTest.php new file mode 100644 index 0000000..b64d5f5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/FileTest.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\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()); + } + + 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); + } + + public function testGetExtension() + { + $file = new File(__DIR__.'/Fixtures/test.gif'); + $this->assertEquals('gif', $file->getExtension()); + } + + 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/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..7bf10a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/MimeType/MimeTypeTest.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\HttpFoundation\Tests\File; + +use Symfony\Component\HttpFoundation\File\File; +use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +class MimeTypeTest extends \PHPUnit_Framework_TestCase +{ + protected $path; + + public function testGuessImageWithoutExtension() + { + if (extension_loaded('fileinfo')) { + $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test')); + } else { + $this->assertNull(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()); + if (extension_loaded('fileinfo')) { + $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test')); + } else { + $this->assertNull(MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test')); + } + } + + public function testGuessImageWithKnownExtension() + { + if (extension_loaded('fileinfo')) { + $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif')); + } else { + $this->assertNull(MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif')); + } + } + + public function testGuessFileWithUnknownExtension() + { + if (extension_loaded('fileinfo')) { + $this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension')); + } else { + $this->assertNull(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 (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Can not verify chmod operations on Windows'); + } + + if (in_array(get_current_user(), array('root'))) { + $this->markTestSkipped('This test will fail if run under superuser'); + } + + $path = __DIR__.'/../Fixtures/to_delete'; + touch($path); + @chmod($path, 0333); + + if (get_current_user() != 'root' && 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..f6ea340 --- /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..1f29d56 --- /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..b1bfefb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.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\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\HeaderBag; + +class HeaderBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\HttpFoundation\HeaderBag::__construct + */ + 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')); + } + + /** + * @covers Symfony\Component\HttpFoundation\HeaderBag::all + */ + 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'); + } + + /** + * @covers Symfony\Component\HttpFoundation\HeaderBag::replace + */ + 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'); + } + + /** + * @covers Symfony\Component\HttpFoundation\HeaderBag::get + */ + 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'); + } + + /** + * @covers Symfony\Component\HttpFoundation\HeaderBag::contains + */ + 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')); + } + + /** + * @covers Symfony\Component\HttpFoundation\HeaderBag::getIterator + */ + 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); + } + + /** + * @covers Symfony\Component\HttpFoundation\HeaderBag::count + */ + 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..726ba6a --- /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/33'), + 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')), + ); + } + + /** + * @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 + */ + public function testAnIpv6WithOptionDisabledIpv6() + { + if (!extension_loaded('sockets')) { + $this->markTestSkipped('Only works when the socket extension is enabled'); + } + + 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..8f1383f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.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\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 testSetCallbackInvalidIdentifier() + { + $response = new JsonResponse('foo'); + + $this->setExpectedException('InvalidArgumentException'); + $response->setCallback('+invalid'); + } + + public function testJsonEncodeFlags() + { + $response = new JsonResponse('<>\'&"'); + + $this->assertEquals('"\u003C\u003E\u0027\u0026\u0022"', $response->getContent()); + } +} 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..6cd42bf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.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\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\ParameterBag; + +class ParameterBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::__construct + */ + public function testConstructor() + { + $this->testAll(); + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::all + */ + 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()); + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::replace + */ + 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'); + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::get + */ + 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]')); + } + + /** + * @dataProvider getInvalidPaths + * @expectedException \InvalidArgumentException + */ + public function testGetDeepWithInvalidPaths($path) + { + $bag = new ParameterBag(array('foo' => array('bar' => 'moo'))); + + $bag->get($path, null, true); + } + + public function getInvalidPaths() + { + return array( + array('foo[['), + array('foo[d'), + array('foo[bar]]'), + array('foo[bar]d'), + ); + } + + public function testGetDeep() + { + $bag = new ParameterBag(array('foo' => array('bar' => array('moo' => 'boo')))); + + $this->assertEquals(array('moo' => 'boo'), $bag->get('foo[bar]', null, true)); + $this->assertEquals('boo', $bag->get('foo[bar][moo]', null, true)); + $this->assertEquals('default', $bag->get('foo[bar][foo]', 'default', true)); + $this->assertEquals('default', $bag->get('bar[moo][foo]', 'default', true)); + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::set + */ + 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'); + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::has + */ + 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'); + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::getAlpha + */ + 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'); + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::getAlnum + */ + 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'); + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::getDigits + */ + 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'); + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::getInt + */ + 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'); + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::filter + */ + 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', '', false, FILTER_SANITIZE_NUMBER_INT), '->filter() gets a value of parameter as integer filtering out invalid characters'); + + $this->assertEquals('example@example.com', $bag->filter('email', '', false, FILTER_VALIDATE_EMAIL), '->filter() gets a value of parameter as email'); + + $this->assertEquals('http://example.com/foo', $bag->filter('url', '', false, 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', '', false, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED), '->filter() gets a value of parameter as url with a path'); + + $this->assertFalse($bag->filter('dec', '', false, 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', '', false, 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', '', false), '->filter() gets a value of parameter as an array'); + + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::getIterator + */ + 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); + } + + /** + * @covers Symfony\Component\HttpFoundation\ParameterBag::count + */ + public function testCount() + { + $parameters = array('foo' => 'bar', 'hello' => 'world'); + $bag = new ParameterBag($parameters); + + $this->assertEquals(count($parameters), count($bag)); + } +} 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..330d9fe --- /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..0e1a0f5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestMatcherTest.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\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), + ); + } + + /** + * @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/RequestTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php new file mode 100644 index 0000000..5ba94f7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -0,0 +1,1512 @@ + + * + * 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 +{ + /** + * @covers Symfony\Component\HttpFoundation\Request::__construct + */ + public function testConstructor() + { + $this->testInitialize(); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::initialize + */ + 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 fourth 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); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::create + */ + 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()); + + $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://test:test@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('test', $request->getUser()); + $this->assertEquals('test', $request->getPassword()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://testnopass@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('testnopass', $request->getUser()); + $this->assertNull($request->getPassword()); + $this->assertFalse($request->isSecure()); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::create + */ + 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()); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::duplicate + */ + 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'); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::getFormat + * @covers Symfony\Component\HttpFoundation\Request::setFormat + * @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)); + } + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::getFormat + */ + public function testGetFormatFromMimeTypeWithParameters() + { + $request = new Request(); + $this->assertEquals('json', $request->getFormat('application/json; charset=utf-8')); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::getMimeType + * @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')), + ); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::getUri + */ + 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()); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::getUriForPath + */ + 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')); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::getUserInfo + */ + public function testGetUserInfo() + { + $request = new Request(); + + $server['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()); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::getSchemeAndHttpHost + */ + public function testGetSchemeAndHttpHost() + { + $request = new Request(); + + $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()); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::getQueryString + * @covers Symfony\Component\HttpFoundation\Request::normalizeQueryString + * @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.exemple.com')); + $this->assertEquals('www.exemple.com', $request->getHost(), '->getHost() from Host Header'); + + // Host header with port number + $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.exemple.com:8080')); + $this->assertEquals('www.exemple.com', $request->getHost(), '->getHost() from Host Header with port number'); + + // Server values + $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.exemple.com')); + $this->assertEquals('www.exemple.com', $request->getHost(), '->getHost() from server name'); + + $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.exemple.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' + )); + $port = $request->getPort(); + + $this->assertEquals(8443, $port, 'With PROTO and PORT set PORT takes precedence.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'https' + )); + $port = $request->getPort(); + + $this->assertEquals(443, $port, 'With only PROTO set getPort() defaults to 443.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'http' + )); + $port = $request->getPort(); + + $this->assertEquals(80, $port, 'If X_FORWARDED_PROTO is set to http return 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(); + } + + /** + * @covers Symfony\Component\HttpFoundation\Request::setMethod + * @covers Symfony\Component\HttpFoundation\Request::getMethod + */ + 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()); + } + + 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')), + ); + } + + 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)); + } + + /** + * @expectedException LogicException + * @dataProvider getContentCantBeCalledTwiceWithResourcesProvider + */ + public function testGetContentCantBeCalledTwiceWithResources($first, $second) + { + $req = new Request; + $req->getContent($first); + $req->getContent($second); + } + + public function getContentCantBeCalledTwiceWithResourcesProvider() + { + return array( + 'Resource then fetch' => array(true, false), + 'Resource then resource' => array(true, true), + 'Fetch then resource' => array(false, 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->assertTrue($request->isSecure()); + Request::setTrustedProxies(array()); + + $request->overrideGlobals(); + + $this->assertArrayHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER); + + // 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 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()); + } + + public function testIntlLocale() + { + if (!extension_loaded('intl')) { + $this->markTestSkipped('The intl extension is needed to run this test.'); + } + + $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 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)); + } + + 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( + '/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; + } + + 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()); + + // 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'); + } + + /** + * @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' + ) + ); + } +} + +class RequestContentProxy extends Request +{ + public function getContent($asResource = false) + { + return http_build_query(array('_method' => 'PUT', 'content' => 'mycontent')); + } +} 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..1cb3278 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php @@ -0,0 +1,286 @@ + + * + * 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; + +class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\HttpFoundation\ResponseHeaderBag::allPreserveCase + * @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->assertContains("Set-Cookie: foo=deleted; expires=".gmdate("D, d-M-Y H:i:s T", time() - 31536001)."; path=/; httponly", explode("\r\n", $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..3a08495 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -0,0 +1,717 @@ + + * + * 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; + +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 testIsCacheableWithSetTtl() + { + $response = new Response(); + $response->setTtl(10); + $this->assertTrue($response->isCacheable()); + } + + public function testMustRevalidate() + { + $response = new Response(); + $this->assertFalse($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 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() + { + $response = new Response('', 200, array('Date' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822))); + $this->assertEquals(0, $this->createDateTimeOneHourAgo()->diff($response->getDate())->format('%s'), '->getDate() returns the Date header if present'); + + $response = new Response(); + $date = $response->getDate(); + $this->assertLessThan(1, $date->diff(new \DateTime(), true)->format('%s'), '->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)); + $this->assertEquals(0, $now->diff($response->getDate())->format('%s'), '->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->assertLessThan(1, 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->assertLessThan(1, 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'); + } + + 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'), $response->getVary(), '->setVary() doesn\'t change the Vary header 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'); + + $response->prepare($request); + + $this->assertEquals('', $response->getContent()); + } + + 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 = new \DateTime(); + $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 = new \DateTime(); + $response->setExpires($now); + + $this->assertEquals($response->getExpires()->getTimestamp(), $now->getTimestamp()); + } + + public function testSetLastModified() + { + $response = new Response(); + $response->setLastModified(new \DateTime()); + $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, ''), + 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(201, 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' => new \DateTime, + '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() + { + $date = new \DateTime(); + + return $date->sub(new \DateInterval('PT1H')); + } + + protected function createDateTimeOneHourLater() + { + $date = new \DateTime(); + + return $date->add(new \DateInterval('PT1H')); + } + + protected function createDateTimeNow() + { + return new \DateTime(); + } + + 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..f8e487d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ServerBagTest.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\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 testHttpBasicAuthWithPhpCgiRedirect() + { + $bag = new ServerBag(array('REDIRECT_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 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 testOAuthBearerAuth() + { + $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo'; + $bag = new ServerBag(array('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..93c634c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/AttributeBagTest.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\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), + ); + } + + /** + * @covers Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag::getIterator + */ + public function testGetIterator() + { + $i = 0; + foreach ($this->bag as $key => $val) { + $this->assertEquals($this->array[$key], $val); + $i++; + } + + $this->assertEquals(count($this->array), $i); + } + + /** + * @covers Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag::count + */ + 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..c11d0d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/NamespacedAttributeBagTest.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\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 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..5d44b78 --- /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); + } + + public 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..1dbfd2f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/FlashBagTest.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\FlashBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; + +/** + * FlashBagTest + * + * @author Drak + */ +class FlashBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\HttpFoundation\SessionFlash\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); + } + + public 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() + ); + } + + /** + * @covers Symfony\Component\HttpFoundation\Session\Flash\FlashBag::getIterator + */ + public function testGetIterator() + { + $flashes = array('hello' => 'world', 'beep' => 'boop', 'notice' => 'nope'); + foreach ($flashes as $key => $val) { + $this->bag->set($key, $val); + } + + $i = 0; + foreach ($this->bag as $key => $val) { + $this->assertEquals(array($flashes[$key]), $val); + $i++; + } + + $this->assertEquals(count($flashes), $i); + $this->assertEquals(0, count($this->bag->all())); + } +} 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..9d5ad35 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/SessionTest.php @@ -0,0 +1,227 @@ + + * + * 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', 'symfony2 is great', array('great' => 'symfony2 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()); + } + + /** + * @covers Symfony\Component\HttpFoundation\Session\Session::getIterator + */ + public function testGetIterator() + { + $attributes = array('hello' => 'world', 'symfony2' => '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); + } + + /** + * @covers \Symfony\Component\HttpFoundation\Session\Session::count + */ + public function testGetCount() + { + $this->session->set('hello', 'world'); + $this->session->set('symfony2', 'rocks'); + + $this->assertEquals(2, count($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..da0440d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.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\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler; + +class MemcacheSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + const PREFIX = 'prefix_'; + const TTL = 1000; + /** + * @var MemcacheSessionHandler + */ + protected $storage; + + protected $memcache; + + protected function setUp() + { + if (!class_exists('Memcache')) { + $this->markTestSkipped('Skipped tests Memcache class is not present'); + } + + $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; + } + + 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), + ); + } +} 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..7fbfd0a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.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\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler; + +class MemcachedSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + const PREFIX = 'prefix_'; + const TTL = 1000; + + /** + * @var MemcachedSessionHandler + */ + protected $storage; + + protected $memcached; + + protected function setUp() + { + if (!class_exists('Memcached')) { + $this->markTestSkipped('Skipped tests Memcache class is not present'); + } + + $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; + } + + 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), + ); + } +} 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..1cfd117 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.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\HttpFoundation\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; + +/** + * @author Markus Bachmann + */ +class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $mongo; + private $storage; + public $options; + + protected function setUp() + { + if (!extension_loaded('mongo')) { + $this->markTestSkipped('MongoDbSessionHandler requires the PHP "mongo" extension.'); + } + + $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', + '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 testWrite() + { + $collection = $this->getMockBuilder('MongoCollection') + ->disableOriginalConstructor() + ->getMock(); + + $this->mongo->expects($this->once()) + ->method('selectCollection') + ->with($this->options['database'], $this->options['collection']) + ->will($this->returnValue($collection)); + + $that = $this; + $data = array(); + + $collection->expects($this->once()) + ->method('update') + ->will($this->returnCallback(function($criteria, $updateData, $options) use ($that, &$data) { + $that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria); + $that->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); + $that->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); + } + + public function testReplaceSessionData() + { + $collection = $this->getMockBuilder('MongoCollection') + ->disableOriginalConstructor() + ->getMock(); + + $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->getMockBuilder('MongoCollection') + ->disableOriginalConstructor() + ->getMock(); + + $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->getMockBuilder('MongoCollection') + ->disableOriginalConstructor() + ->getMock(); + + $this->mongo->expects($this->once()) + ->method('selectCollection') + ->with($this->options['database'], $this->options['collection']) + ->will($this->returnValue($collection)); + + $that = $this; + + $collection->expects($this->once()) + ->method('remove') + ->will($this->returnCallback(function($criteria) use ($that) { + $that->assertInstanceOf('MongoDate', $criteria[$that->options['time_field']]['$lt']); + $that->assertGreaterThanOrEqual(time() - -1, $criteria[$that->options['time_field']]['$lt']->sec); + })); + + $this->assertTrue($this->storage->gc(-1)); + } +} 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..20cefab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.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\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 + */ +class NativeFileSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testConstruct() + { + $storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler(sys_get_temp_dir())); + + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->assertEquals('files', $storage->getSaveHandler()->getSaveHandlerName()); + $this->assertEquals('files', ini_get('session.save_handler')); + } else { + $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..7880615 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.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\HttpFoundation\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler; + +/** + * Test class for NativeSessionHandler. + * + * @author Drak + * + * @runTestsInSeparateProcesses + */ +class NativeSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testConstruct() + { + $handler = new NativeSessionHandler(); + + // note for PHPUnit optimisers - the use of assertTrue/False + // here is deliberate since the tests do not require the classes to exist - drak + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->assertFalse($handler instanceof \SessionHandler); + $this->assertTrue($handler instanceof NativeSessionHandler); + } else { + $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..45a47ce --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.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\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 + */ +class NullSessionStorageTest 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..1abf384 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.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\HttpFoundation\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; + +class PdoSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + private $pdo; + + protected function setUp() + { + if (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers())) { + $this->markTestSkipped('This test requires SQLite support in your environment'); + } + + $this->pdo = new \PDO("sqlite::memory:"); + $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $sql = "CREATE TABLE sessions (sess_id VARCHAR(255) PRIMARY KEY, sess_data TEXT, sess_time INTEGER)"; + $this->pdo->exec($sql); + } + + public function testIncompleteOptions() + { + $this->setExpectedException('InvalidArgumentException'); + $storage = new PdoSessionHandler($this->pdo, array(), array()); + } + + public function testWrongPdoErrMode() + { + $pdo = new \PDO("sqlite::memory:"); + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); + $pdo->exec("CREATE TABLE sessions (sess_id VARCHAR(255) PRIMARY KEY, sess_data TEXT, sess_time INTEGER)"); + + $this->setExpectedException('InvalidArgumentException'); + $storage = new PdoSessionHandler($pdo, array('db_table' => 'sessions'), array()); + } + + public function testWrongTableOptionsWrite() + { + $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'bad_name'), array()); + $this->setExpectedException('RuntimeException'); + $storage->write('foo', 'bar'); + } + + public function testWrongTableOptionsRead() + { + $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'bad_name'), array()); + $this->setExpectedException('RuntimeException'); + $storage->read('foo', 'bar'); + } + + public function testWriteRead() + { + $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array()); + $storage->write('foo', 'bar'); + $this->assertEquals('bar', $storage->read('foo'), 'written value can be read back correctly'); + } + + public function testMultipleInstances() + { + $storage1 = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array()); + $storage1->write('foo', 'bar'); + + $storage2 = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array()); + $this->assertEquals('bar', $storage2->read('foo'), 'values persist between instances'); + } + + public function testSessionDestroy() + { + $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array()); + $storage->write('foo', 'bar'); + $this->assertEquals(1, count($this->pdo->query('SELECT * FROM sessions')->fetchAll())); + + $storage->destroy('foo'); + + $this->assertEquals(0, count($this->pdo->query('SELECT * FROM sessions')->fetchAll())); + } + + public function testSessionGC() + { + $storage = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array()); + + $storage->write('foo', 'bar'); + $storage->write('baz', 'bar'); + + $this->assertEquals(2, count($this->pdo->query('SELECT * FROM sessions')->fetchAll())); + + $storage->gc(-1); + $this->assertEquals(0, count($this->pdo->query('SELECT * FROM sessions')->fetchAll())); + } +} 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..ef70281 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MetadataBagTest.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\HttpFoundation\Tests\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; + +/** + * Test class for MetadataBag. + */ +class MetadataBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var MetadataBag + */ + protected $bag; + + /** + * @var array + */ + protected $array = array(); + + protected function 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; + } + + public function testInitialize() + { + $p = new \ReflectionProperty('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', 'meta'); + $p->setAccessible(true); + + $bag1 = new MetadataBag(); + $array = array(); + $bag1->initialize($array); + $this->assertGreaterThanOrEqual(time(), $bag1->getCreated()); + $this->assertEquals($bag1->getCreated(), $bag1->getLastUsed()); + + sleep(1); + $bag2 = new MetadataBag(); + $array2 = $p->getValue($bag1); + $bag2->initialize($array2); + $this->assertEquals($bag1->getCreated(), $bag2->getCreated()); + $this->assertEquals($bag1->getLastUsed(), $bag2->getLastUsed()); + $this->assertEquals($bag2->getCreated(), $bag2->getLastUsed()); + + sleep(1); + $bag3 = new MetadataBag(); + $array3 = $p->getValue($bag2); + $bag3->initialize($array3); + $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(); + } +} 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..2a6f6bb --- /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..d42c62a --- /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 FileMockSessionStorage + */ + 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..bf515c7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php @@ -0,0 +1,282 @@ + + * + * 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\Handler\NativeSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + +/** + * Test class for NativeSessionStorage. + * + * @author Drak + * + * These tests require separate processes. + * + * @runTestsInSeparateProcesses + */ +class NativeSessionStorageTest extends \PHPUnit_Framework_TestCase +{ + private $savePath; + + protected function setUp() + { + ini_set('session.save_handler', 'files'); + ini_set('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->assertEquals('', $storage->getId()); + $storage->start(); + $this->assertNotEquals('', $storage->getId()); + } + + 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 testDefaultSessionCacheLimiter() + { + ini_set('session.cache_limiter', 'nocache'); + + $storage = new NativeSessionStorage(); + $this->assertEquals('', ini_get('session.cache_limiter')); + } + + public function testExplicitSessionCacheLimiter() + { + ini_set('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' => 'symfony2.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 testSetSaveHandler53() + { + if (version_compare(phpversion(), '5.4.0', '>=')) { + $this->markTestSkipped('Test skipped, for PHP 5.3 only.'); + } + + ini_set('session.save_handler', 'files'); + $storage = $this->getStorage(); + $storage->setSaveHandler(); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(null); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new NativeSessionHandler()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new SessionHandlerProxy(new SessionHandler())); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new SessionHandler()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new NativeProxy()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler()); + } + + public function testSetSaveHandler54() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->markTestSkipped('Test skipped, for PHP 5.4 only.'); + } + + ini_set('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 SessionHandler())); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new SessionHandler()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + } + + /** + * @expectedException \RuntimeException + */ + public function testStartedOutside53() + { + if (version_compare(phpversion(), '5.4.0', '>=')) { + $this->markTestSkipped('Test skipped, for PHP 5.3 only.'); + } + + $storage = $this->getStorage(); + + $this->assertFalse(isset($_SESSION)); + + session_start(); + $this->assertTrue(isset($_SESSION)); + // 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(); + } + + /** + * @expectedException \RuntimeException + */ + public function testCanStartOutside54() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->markTestSkipped('Test skipped, for PHP 5.4 only.'); + } + + $storage = $this->getStorage(); + + $this->assertFalse(isset($_SESSION)); + $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(); + } +} + +class SessionHandler 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) + { + } +} 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..a066391 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.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\Tests\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler; +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 + */ +class PhpSessionStorageTest extends \PHPUnit_Framework_TestCase +{ + private $savePath; + + protected function setUp() + { + ini_set('session.save_handler', 'files'); + ini_set('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 testPhpSession53() + { + if (version_compare(phpversion(), '5.4.0', '>=')) { + $this->markTestSkipped('Test skipped, for PHP 5.3 only.'); + } + + $storage = $this->getStorage(); + + $this->assertFalse(isset($_SESSION)); + $this->assertFalse($storage->getSaveHandler()->isActive()); + + session_start(); + $this->assertTrue(isset($_SESSION)); + // in PHP 5.3 we cannot reliably tell if a session has started + $this->assertFalse($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 testPhpSession54() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->markTestSkipped('Test skipped, for PHP 5.4 only.'); + } + + $storage = $this->getStorage(); + + $this->assertFalse(isset($_SESSION)); + $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..6b8bba0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/AbstractProxyTest.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\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()); + } + + public function testIsActivePhp53() + { + if (version_compare(phpversion(), '5.4.0', '>=')) { + $this->markTestSkipped('Test skipped, for PHP 5.3 only.'); + } + + $this->assertFalse($this->proxy->isActive()); + } + + /** + * @runInSeparateProcess + */ + public function testIsActivePhp54() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->markTestSkipped('Test skipped, for PHP 5.4 only.'); + } + + $this->assertFalse($this->proxy->isActive()); + session_start(); + $this->assertTrue($this->proxy->isActive()); + } + + public function testSetActivePhp53() + { + if (version_compare(phpversion(), '5.4.0', '>=')) { + $this->markTestSkipped('Test skipped, for PHP 5.3 only.'); + } + + $this->proxy->setActive(true); + $this->assertTrue($this->proxy->isActive()); + $this->proxy->setActive(false); + $this->assertFalse($this->proxy->isActive()); + } + + /** + * @runInSeparateProcess + * @expectedException \LogicException + */ + public function testSetActivePhp54() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->markTestSkipped('Test skipped, for PHP 5.4 only.'); + } + + $this->proxy->setActive(true); + } + + /** + * @runInSeparateProcess + */ + 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()); + } + + /** + * @expectedException \LogicException + */ + public function testNameExceptionPhp53() + { + if (version_compare(phpversion(), '5.4.0', '>=')) { + $this->markTestSkipped('Test skipped, for PHP 5.3 only.'); + } + + $this->proxy->setActive(true); + $this->proxy->setName('foo'); + } + + /** + * @runInSeparateProcess + * @expectedException \LogicException + */ + public function testNameExceptionPhp54() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->markTestSkipped('Test skipped, for PHP 5.4 only.'); + } + + session_start(); + $this->proxy->setName('foo'); + } + + /** + * @runInSeparateProcess + */ + 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()); + } + + /** + * @expectedException \LogicException + */ + public function testIdExceptionPhp53() + { + if (version_compare(phpversion(), '5.4.0', '>=')) { + $this->markTestSkipped('Test skipped, for PHP 5.3 only.'); + } + + $this->proxy->setActive(true); + $this->proxy->setId('foo'); + } + + /** + * @runInSeparateProcess + * @expectedException \LogicException + */ + public function testIdExceptionPhp54() + { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->markTestSkipped('Test skipped, for PHP 5.4 only.'); + } + + 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..74d8419 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.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\Proxy; + +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + +/** + * Tests for SessionHandlerProxy class. + * + * @author Drak + * + * @runTestsInSeparateProcesses + */ +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 testOpen() + { + $this->mock->expects($this->once()) + ->method('open') + ->will($this->returnValue(true)); + + $this->assertFalse($this->proxy->isActive()); + $this->proxy->open('name', 'id'); + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->assertTrue($this->proxy->isActive()); + } else { + $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..0dfe651 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.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\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.'); + $this->assertEquals('no-cache, private', $response->headers->get('Cache-Control')); + } + + 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')); + $this->assertEquals('no-cache, private', $response->headers->get('Cache-Control')); + } + + public function testPrepareWithHeadRequest() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $request = Request::create('/', 'HEAD'); + + $response->prepare($request); + } + + 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 testSetCallbackNonCallable() + { + $response = new StreamedResponse(null); + $response->setCallback(null); + } + + /** + * @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..f77e08e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/composer.json @@ -0,0 +1,32 @@ +{ + "name": "symfony/http-foundation", + "type": "library", + "description": "Symfony HttpFoundation Component", + "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.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\HttpFoundation\\": "" }, + "classmap": [ "Symfony/Component/HttpFoundation/Resources/stubs" ] + }, + "target-dir": "Symfony/Component/HttpFoundation", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..df11f72 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./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..215d1e1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Bundle/Bundle.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\Component\HttpKernel\Bundle; + +use Symfony\Component\DependencyInjection\ContainerAware; +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 + * + * @api + */ +abstract class Bundle extends ContainerAware implements BundleInterface +{ + protected $name; + protected $reflected; + protected $extension; + + /** + * 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 + * + * @api + */ + public function getContainerExtension() + { + if (null === $this->extension) { + $basename = preg_replace('/Bundle$/', '', $this->getName()); + + $class = $this->getNamespace().'\\DependencyInjection\\'.$basename.'Extension'; + if (class_exists($class)) { + $extension = new $class(); + + // check naming convention + $expectedAlias = Container::underscore($basename); + if ($expectedAlias != $extension->getAlias()) { + throw new \LogicException(sprintf( + 'The extension alias for the default extension of a '. + 'bundle must be the underscored version of the '. + 'bundle name ("%s" instead of "%s")', + $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 + * + * @api + */ + public function getNamespace() + { + if (null === $this->reflected) { + $this->reflected = new \ReflectionObject($this); + } + + return $this->reflected->getNamespaceName(); + } + + /** + * Gets the Bundle directory path. + * + * @return string The Bundle absolute path + * + * @api + */ + public function getPath() + { + if (null === $this->reflected) { + $this->reflected = new \ReflectionObject($this); + } + + return dirname($this->reflected->getFileName()); + } + + /** + * Returns the bundle parent name. + * + * @return string The Bundle parent name it overrides or null if no parent + * + * @api + */ + public function getParent() + { + return null; + } + + /** + * Returns the bundle name (the class short name). + * + * @return string The Bundle name + * + * @api + */ + 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; + } + + $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, '/', '\\'); + } + $r = new \ReflectionClass($ns.'\\'.$file->getBasename('.php')); + if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract() && !$r->getConstructor()->getNumberOfRequiredParameters()) { + $application->add($r->newInstance()); + } + } + } +} 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..3203d84 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.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\Bundle; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; + +/** + * BundleInterface. + * + * @author Fabien Potencier + * + * @api + */ +interface BundleInterface extends ContainerAwareInterface +{ + /** + * Boots the Bundle. + * + * @api + */ + public function boot(); + + /** + * Shutdowns the Bundle. + * + * @api + */ + public function shutdown(); + + /** + * Builds the bundle. + * + * It is only ever called once when the cache is empty. + * + * @param ContainerBuilder $container A ContainerBuilder instance + * + * @api + */ + 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 + * + * @api + */ + 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 + * + * @api + */ + public function getParent(); + + /** + * Returns the bundle name (the class short name). + * + * @return string The Bundle name + * + * @api + */ + public function getName(); + + /** + * Gets the Bundle namespace. + * + * @return string The Bundle namespace + * + * @api + */ + 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 + * + * @api + */ + 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..c06dd3f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -0,0 +1,51 @@ +CHANGELOG +========= + +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..7b492d0 --- /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 $clearers + */ + 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..eb26ac5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.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\HttpKernel\CacheWarmer; + +/** + * Aggregates several cache warmers into a single one. + * + * @author Fabien Potencier + */ +class CacheWarmerAggregate implements CacheWarmerInterface +{ + protected $warmers; + protected $optionalsEnabled; + + public function __construct(array $warmers = array()) + { + $this->setWarmers($warmers); + $this->optionalsEnabled = false; + } + + 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 Boolean always true + */ + 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..ed76ce3 --- /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 Boolean 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..bb427b3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Client.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\HttpKernel; + +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +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\HttpKernel\TerminableInterface; + +/** + * Client simulates a browser and makes requests to a Kernel object. + * + * @author Fabien Potencier + * + * @api + */ +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) + { + $this->kernel = $kernel; + + parent::__construct($server, $history, $cookieJar); + + $this->followRedirects = false; + } + + /** + * {@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("'", "\\'", realpath(__DIR__.'/../../..')); + + return <<addPrefix('Symfony', '$symfonyPath'); +\$loader->register(); + +\$kernel = unserialize('$kernel'); +echo serialize(\$kernel->handle(unserialize('$request'))); +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()); + + $httpRequest->files->replace($this->filterFiles($httpRequest->files->all())); + + 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 Symfony\Component\HttpFoundation\File\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 + ); + } + } else { + $filtered[$key] = $value; + } + } + + 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/FileLocator.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Config/FileLocator.php new file mode 100644 index 0000000..47b543c --- /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 ('@' === $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..22d6cd3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerReference.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\Controller; + +/** + * 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 Symfony\Component\HttpKernel\FragmentRenderer + * @see Symfony\Component\HttpKernel\Fragment\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..047ade1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.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\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 + * + * @api + */ +class ControllerResolver implements ControllerResolverInterface +{ + private $logger; + + /** + * Constructor. + * + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + } + + /** + * Returns the Controller instance associated with a Request. + * + * This method looks for a '_controller' request attribute that represents + * the controller name (a string like ClassName::MethodName). + * + * @param Request $request A Request instance + * + * @return mixed|Boolean A PHP callable representing the Controller, + * or false if this resolver is not able to determine the controller + * + * @throws \InvalidArgumentException|\LogicException If the controller can't be found + * + * @api + */ + 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) || (is_object($controller) && method_exists($controller, '__invoke'))) { + return $controller; + } + + if (false === strpos($controller, ':')) { + if (method_exists($controller, '__invoke')) { + return new $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.', $request->getPathInfo())); + } + + return $callable; + } + + /** + * Returns the arguments to pass to the controller. + * + * @param Request $request A Request instance + * @param mixed $controller A PHP callable + * + * @return array + * + * @throws \RuntimeException When value for argument given is not provided + * + * @api + */ + 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 mixed 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(new $class(), $method); + } +} 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..f58f50d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.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\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 + * + * @api + */ +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 mixed|Boolean A PHP callable representing the Controller, + * or false if this resolver is not able to determine the controller + * + * @throws \InvalidArgumentException|\LogicException If the controller can't be found + * + * @api + */ + public function getController(Request $request); + + /** + * Returns the arguments to pass to the controller. + * + * @param Request $request A Request instance + * @param mixed $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 + * + * @api + */ + 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/ConfigDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php new file mode 100644 index 0000000..47529fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.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\HttpKernel\DataCollector; + +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * ConfigDataCollector. + * + * @author Fabien Potencier + */ +class ConfigDataCollector extends DataCollector +{ + private $kernel; + private $name; + private $version; + + /** + * 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, + '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(); + } + } + } + + 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']; + } + + /** + * 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 Boolean true if debug is enabled, false otherwise + */ + public function isDebug() + { + return $this->data['debug']; + } + + /** + * Returns true if the XDebug is enabled. + * + * @return Boolean true if XDebug is enabled, false otherwise + */ + public function hasXDebug() + { + return $this->data['xdebug_enabled']; + } + + /** + * Returns true if EAccelerator is enabled. + * + * @return Boolean true if EAccelerator is enabled, false otherwise + */ + public function hasEAccelerator() + { + return $this->data['eaccel_enabled']; + } + + /** + * Returns true if APC is enabled. + * + * @return Boolean true if APC is enabled, false otherwise + */ + public function hasApc() + { + return $this->data['apc_enabled']; + } + + /** + * Returns true if Zend OPcache is enabled + * + * @return Boolean true if Zend OPcache is enabled, false otherwise + */ + public function hasZendOpcache() + { + return $this->data['zend_opcache_enabled']; + } + + /** + * Returns true if XCache is enabled. + * + * @return Boolean true if XCache is enabled, false otherwise + */ + public function hasXCache() + { + return $this->data['xcache_enabled']; + } + + /** + * Returns true if WinCache is enabled. + * + * @return Boolean true if WinCache is enabled, false otherwise + */ + public function hasWinCache() + { + return $this->data['wincache_enabled']; + } + + /** + * Returns true if any accelerator is enabled. + * + * @return Boolean 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'; + } +} 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..7d9c289 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.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\HttpKernel\DataCollector; + +/** + * DataCollector. + * + * Children of this class must store the collected data in the data property. + * + * @author Fabien Potencier + */ +abstract class DataCollector implements DataCollectorInterface, \Serializable +{ + protected $data; + + 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 (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/DataCollector/DataCollectorInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php new file mode 100644 index 0000000..cf4cdfd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.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\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * DataCollectorInterface. + * + * @author Fabien Potencier + * + * @api + */ +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 + * + * @api + */ + public function collect(Request $request, Response $response, \Exception $exception = null); + + /** + * Returns the name of the collector. + * + * @return string The collector name + * + * @api + */ + public function getName(); +} 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..cd7f787 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.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\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface; + +/** + * EventDataCollector. + * + * @author Fabien Potencier + */ +class EventDataCollector extends DataCollector +{ + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $this->data = array( + 'called_listeners' => array(), + 'not_called_listeners' => array(), + ); + } + + /** + * 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..10a010b --- /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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\FlattenException; + +/** + * 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 Boolean 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 integer The exception code + */ + public function getCode() + { + return $this->data['exception']->getCode(); + } + + /** + * Gets the status code. + * + * @return integer 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/LoggerDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php new file mode 100644 index 0000000..f08720e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.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\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Debug\ErrorHandler; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; + +/** + * LogDataCollector. + * + * @author Fabien Potencier + */ +class LoggerDataCollector extends DataCollector +{ + 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) + { + if (null !== $this->logger) { + $this->data = array( + 'error_count' => $this->logger->countErrors(), + 'logs' => $this->sanitizeLogs($this->logger->getLogs()), + 'deprecation_count' => $this->computeDeprecationCount() + ); + } + } + + /** + * 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 countDeprecations() + { + return isset($this->data['deprecation_count']) ? $this->data['deprecation_count'] : 0; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'logger'; + } + + private function sanitizeLogs($logs) + { + foreach ($logs as $i => $log) { + $logs[$i]['context'] = $this->sanitizeContext($log['context']); + } + + return $logs; + } + + 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 computeDeprecationCount() + { + $count = 0; + foreach ($this->logger->getLogs() as $log) { + if (isset($log['context']['type']) && ErrorHandler::TYPE_DEPRECATION === $log['context']['type']) { + $count++; + } + } + + 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..5540a1b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.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\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * MemoryDataCollector. + * + * @author Fabien Potencier + */ +class MemoryDataCollector extends DataCollector +{ + public function __construct() + { + $this->data = array( + 'memory' => 0, + 'memory_limit' => $this->convertToBytes(strtolower(ini_get('memory_limit'))), + ); + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $this->updateMemoryUsage(); + } + + /** + * Gets the memory. + * + * @return integer The memory + */ + public function getMemory() + { + return $this->data['memory']; + } + + /** + * Gets the PHP memory limit. + * + * @return integer 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; + } + + if (preg_match('#^\+?(0x?)?(.*?)([kmg]?)$#', $memoryLimit, $match)) { + $shifts = array('' => 0, 'k' => 10, 'm' => 20, 'g' => 30); + $bases = array('' => 10, '0' => 8, '0x' => 16); + + return intval($match[2], $bases[$match[1]]) << $shifts[$match[3]]; + } + + return 0; + } +} 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..934c847 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.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\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 = array(); + foreach ($request->attributes->all() as $key => $value) { + if ('_route' == $key && is_object($value)) { + $value = $value->getPath(); + } + + $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') ? $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->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 $re) { + 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(), + ); + } 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 { + $expires = strtotime($expires); + if (false === $expires || -1 == $expires) { + throw new \InvalidArgumentException(sprintf('The "expires" cookie parameter is not valid.', $expires)); + } + } + + $cookie .= '; expires='.substr(\DateTime::createFromFormat('U', $expires, new \DateTimeZone('UTC'))->format('D, d-M-Y H:i:s T'), 0, -5); + } + + 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..80e250a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.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\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +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]); + } + } + } + + 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 Boolean 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..b9a20ad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.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\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * TimeDataCollector. + * + * @author Fabien Potencier + */ +class TimeDataCollector extends DataCollector +{ + protected $kernel; + + public function __construct(KernelInterface $kernel = null) + { + $this->kernel = $kernel; + } + + /** + * {@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( + 'start_time' => $startTime * 1000, + 'events' => array(), + ); + } + + /** + * 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() + { + $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() + { + return $this->data['events']['__section__']->getOrigin() - $this->getStartTime(); + } + + /** + * Gets the request time. + * + * @return integer 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/Debug/ErrorHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Debug/ErrorHandler.php new file mode 100644 index 0000000..2718f89 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Debug/ErrorHandler.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\Debug; + +use Symfony\Component\Debug\ErrorHandler as DebugErrorHandler; + +/** + * ErrorHandler. + * + * @author Fabien Potencier + * + * @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead. + */ +class ErrorHandler extends DebugErrorHandler +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Debug/ExceptionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Debug/ExceptionHandler.php new file mode 100644 index 0000000..581e29c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Debug/ExceptionHandler.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\Debug; + +use Symfony\Component\Debug\ExceptionHandler as DebugExceptionHandler; + +/** + * ExceptionHandler converts an exception to a Response object. + * + * @author Fabien Potencier + * + * @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead. + */ +class ExceptionHandler extends DebugExceptionHandler +{ +} 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..6bfd7a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php @@ -0,0 +1,469 @@ + + * + * 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\Stopwatch\Stopwatch; +use Symfony\Component\HttpKernel\KernelEvents; +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Profiler\Profile; +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface; + +/** + * Collects some data about event listeners. + * + * This event dispatcher delegates the dispatching to another one. + * + * @author Fabien Potencier + */ +class TraceableEventDispatcher implements EventDispatcherInterface, TraceableEventDispatcherInterface +{ + private $logger; + private $called; + private $stopwatch; + private $profiler; + private $dispatcher; + private $wrappedListeners; + private $firstCalledEvent; + private $id; + + /** + * Constructor. + * + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * @param Stopwatch $stopwatch A Stopwatch instance + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null) + { + $this->dispatcher = $dispatcher; + $this->stopwatch = $stopwatch; + $this->logger = $logger; + $this->called = array(); + $this->wrappedListeners = array(); + $this->firstCalledEvent = array(); + } + + /** + * Sets the profiler. + * + * @param Profiler|null $profiler A Profiler instance + */ + public function setProfiler(Profiler $profiler = null) + { + $this->profiler = $profiler; + } + + /** + * {@inheritDoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->dispatcher->addListener($eventName, $listener, $priority); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + return $this->dispatcher->removeListener($eventName, $listener); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + return $this->dispatcher->removeSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + $this->id = spl_object_hash($event); + + $this->preDispatch($eventName, $event); + + $e = $this->stopwatch->start($eventName, 'section'); + + $this->firstCalledEvent[$eventName] = $this->stopwatch->start($eventName.'.loading', 'event_listener_loading'); + + if (!$this->dispatcher->hasListeners($eventName)) { + $this->firstCalledEvent[$eventName]->stop(); + } + + $this->dispatcher->dispatch($eventName, $event); + + // reset the id as another event might have been dispatched during the dispatching of this event + $this->id = spl_object_hash($event); + + unset($this->firstCalledEvent[$eventName]); + + $e->stop(); + + $this->postDispatch($eventName, $event); + + return $event; + } + + /** + * {@inheritDoc} + */ + public function getCalledListeners() + { + return $this->called; + } + + /** + * {@inheritDoc} + */ + public function getNotCalledListeners() + { + $notCalled = array(); + + foreach ($this->getListeners() as $name => $listeners) { + foreach ($listeners as $listener) { + $info = $this->getListenerInfo($listener, $name); + if (!isset($this->called[$name.'.'.$info['pretty']])) { + $notCalled[$name.'.'.$info['pretty']] = $info; + } + } + } + + return $notCalled; + } + + /** + * Proxies all method calls to the original event dispatcher. + * + * @param string $method The method name + * @param array $arguments The method arguments + * + * @return mixed + */ + public function __call($method, $arguments) + { + return call_user_func_array(array($this->dispatcher, $method), $arguments); + } + + /** + * This is a private method and must not be used. + * + * This method is public because it is used in a closure. + * Whenever Symfony will require PHP 5.4, this could be changed + * to a proper private method. + */ + public function logSkippedListeners($eventName, Event $event, $listener) + { + if (null === $this->logger) { + return; + } + + $info = $this->getListenerInfo($listener, $eventName); + + $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName)); + + $skippedListeners = $this->getListeners($eventName); + $skipped = false; + + foreach ($skippedListeners as $skippedListener) { + $skippedListener = $this->unwrapListener($skippedListener); + + if ($skipped) { + $info = $this->getListenerInfo($skippedListener, $eventName); + $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName)); + } + + if ($skippedListener === $listener) { + $skipped = true; + } + } + } + + /** + * This is a private method. + * + * This method is public because it is used in a closure. + * Whenever Symfony will require PHP 5.4, this could be changed + * to a proper private method. + */ + public function preListenerCall($eventName, $listener) + { + // is it the first called listener? + if (isset($this->firstCalledEvent[$eventName])) { + $this->firstCalledEvent[$eventName]->stop(); + + unset($this->firstCalledEvent[$eventName]); + } + + $info = $this->getListenerInfo($listener, $eventName); + + if (null !== $this->logger) { + $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty'])); + } + + $this->called[$eventName.'.'.$info['pretty']] = $info; + + return $this->stopwatch->start(isset($info['class']) ? $info['class'] : $info['type'], 'event_listener'); + } + + /** + * Returns information about the listener + * + * @param object $listener The listener + * @param string $eventName The event name + * + * @return array Informations about the listener + */ + private function getListenerInfo($listener, $eventName) + { + $listener = $this->unwrapListener($listener); + + $info = array( + 'event' => $eventName, + ); + if ($listener instanceof \Closure) { + $info += array( + 'type' => 'Closure', + 'pretty' => 'closure' + ); + } elseif (is_string($listener)) { + try { + $r = new \ReflectionFunction($listener); + $file = $r->getFileName(); + $line = $r->getStartLine(); + } catch (\ReflectionException $e) { + $file = null; + $line = null; + } + $info += array( + 'type' => 'Function', + 'function' => $listener, + 'file' => $file, + 'line' => $line, + 'pretty' => $listener, + ); + } elseif (is_array($listener) || (is_object($listener) && is_callable($listener))) { + if (!is_array($listener)) { + $listener = array($listener, '__invoke'); + } + $class = is_object($listener[0]) ? get_class($listener[0]) : $listener[0]; + try { + $r = new \ReflectionMethod($class, $listener[1]); + $file = $r->getFileName(); + $line = $r->getStartLine(); + } catch (\ReflectionException $e) { + $file = null; + $line = null; + } + $info += array( + 'type' => 'Method', + 'class' => $class, + 'method' => $listener[1], + 'file' => $file, + 'line' => $line, + 'pretty' => $class.'::'.$listener[1], + ); + } + + return $info; + } + + /** + * Updates the stopwatch data in the profile hierarchy. + * + * @param string $token Profile token + * @param Boolean $updateChildren Whether to update the children altogether + */ + private function updateProfiles($token, $updateChildren) + { + if (!$this->profiler || !$profile = $this->profiler->loadProfile($token)) { + return; + } + + $this->saveInfoInProfile($profile, $updateChildren); + } + + /** + * Update the profiles with the timing and events information and saves them. + * + * @param Profile $profile The root profile + * @param Boolean $updateChildren Whether to update the children altogether + */ + private function saveInfoInProfile(Profile $profile, $updateChildren) + { + try { + $collector = $profile->getCollector('memory'); + $collector->updateMemoryUsage(); + } catch (\InvalidArgumentException $e) { + } + + try { + $collector = $profile->getCollector('time'); + $collector->setEvents($this->stopwatch->getSectionEvents($profile->getToken())); + } catch (\InvalidArgumentException $e) { + } + + try { + $collector = $profile->getCollector('events'); + $collector->setCalledListeners($this->getCalledListeners()); + $collector->setNotCalledListeners($this->getNotCalledListeners()); + } catch (\InvalidArgumentException $e) { + } + + $this->profiler->saveProfile($profile); + + if ($updateChildren) { + foreach ($profile->getChildren() as $child) { + $this->saveInfoInProfile($child, true); + } + } + } + + private function preDispatch($eventName, Event $event) + { + // wrap all listeners before they are called + $this->wrappedListeners[$this->id] = new \SplObjectStorage(); + + $listeners = $this->dispatcher->getListeners($eventName); + + foreach ($listeners as $listener) { + $this->dispatcher->removeListener($eventName, $listener); + $wrapped = $this->wrapListener($eventName, $listener); + $this->wrappedListeners[$this->id][$wrapped] = $listener; + $this->dispatcher->addListener($eventName, $wrapped); + } + + 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 builtin 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; + } + } + + private 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); + if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) { + // The profiles can only be updated once they have been created + // that is after the 'kernel.response' event of the main request + $this->updateProfiles($token, true); + } + break; + case KernelEvents::TERMINATE: + $token = $event->getResponse()->headers->get('X-Debug-Token'); + // 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. + try { + $this->stopwatch->stopSection($token); + } catch (\LogicException $e) {} + // The children profiles have been updated by the previous 'kernel.response' + // event. Only the root profile need to be updated with the 'kernel.terminate' + // timing informations. + $this->updateProfiles($token, false); + break; + } + + foreach ($this->wrappedListeners[$this->id] as $wrapped) { + $this->dispatcher->removeListener($eventName, $wrapped); + $this->dispatcher->addListener($eventName, $this->wrappedListeners[$this->id][$wrapped]); + } + + unset($this->wrappedListeners[$this->id]); + } + + private function wrapListener($eventName, $listener) + { + $self = $this; + + return function (Event $event) use ($self, $eventName, $listener) { + $e = $self->preListenerCall($eventName, $listener); + + call_user_func($listener, $event); + + $e->stop(); + + if ($event->isPropagationStopped()) { + $self->logSkippedListeners($eventName, $event, $listener); + } + }; + } + + private function unwrapListener($listener) + { + // get the original listener + if (is_object($listener) && isset($this->wrappedListeners[$this->id][$listener])) { + return $this->wrappedListeners[$this->id][$listener]; + } + + return $listener; + } +} 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..7e694eb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/AddClassesToCachePass.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\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +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..7e036fd --- /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 you + * + * a) 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(array(), $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/ContainerAwareHttpKernel.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php new file mode 100644 index 0000000..18ca8ef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.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\HttpKernel\DependencyInjection; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Scope; + +/** + * Adds a managed request scope. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class ContainerAwareHttpKernel extends HttpKernel +{ + protected $container; + + /** + * Constructor. + * + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * @param ContainerInterface $container A ContainerInterface instance + * @param ControllerResolverInterface $controllerResolver A ControllerResolverInterface instance + */ + public function __construct(EventDispatcherInterface $dispatcher, ContainerInterface $container, ControllerResolverInterface $controllerResolver) + { + parent::__construct($dispatcher, $controllerResolver); + + $this->container = $container; + + // the request scope might have been created before (see FrameworkBundle) + if (!$container->hasScope('request')) { + $container->addScope(new Scope('request')); + } + } + + /** + * {@inheritdoc} + */ + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + $request->headers->set('X-Php-Ob-Level', ob_get_level()); + + $this->container->enterScope('request'); + $this->container->set('request', $request, 'request'); + + try { + $response = parent::handle($request, $type, $catch); + } catch (\Exception $e) { + $this->container->set('request', null, 'request'); + $this->container->leaveScope('request'); + + throw $e; + } + + $this->container->set('request', null, 'request'); + $this->container->leaveScope('request'); + + return $response; + } +} 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/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/DependencyInjection/RegisterListenersPass.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterListenersPass.php new file mode 100644 index 0000000..ee00fbb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterListenersPass.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\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Compiler pass to register tagged services for an event dispatcher. + */ +class RegisterListenersPass implements CompilerPassInterface +{ + /** + * @var string + */ + protected $dispatcherService; + + /** + * @var string + */ + protected $listenerTag; + + /** + * @var string + */ + protected $subscriberTag; + + /** + * Constructor. + * + * @param string $dispatcherService Service name of the event dispatcher in processed container + * @param string $listenerTag Tag name used for listener + * @param string $subscriberTag Tag name used for subscribers + */ + public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber') + { + $this->dispatcherService = $dispatcherService; + $this->listenerTag = $listenerTag; + $this->subscriberTag = $subscriberTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->dispatcherService)) { + return; + } + + $definition = $container->getDefinition($this->dispatcherService); + + foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) { + foreach ($events as $event) { + $priority = isset($event['priority']) ? $event['priority'] : 0; + + if (!isset($event['event'])) { + throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "kernel.event_listener" tags.', $id)); + } + + if (!isset($event['method'])) { + $event['method'] = 'on'.preg_replace_callback(array( + '/(?<=\b)[a-z]/i', + '/[^a-z0-9]/i', + ), function ($matches) { return strtoupper($matches[0]); }, $event['event']); + $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']); + } + + $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority)); + } + } + + foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) { + // We must assume that the class value has been correctly filled, even if the service is created by a factory + $class = $container->getDefinition($id)->getClass(); + + $refClass = new \ReflectionClass($class); + $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface'; + if (!$refClass->implementsInterface($interface)) { + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + + $definition->addMethodCall('addSubscriberService', array($id, $class)); + } + } +} 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..2b4860c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.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\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 + * + * @api + */ +class FilterControllerEvent extends KernelEvent +{ + /** + * The current controller + * @var callable + */ + private $controller; + + public function __construct(HttpKernelInterface $kernel, $controller, Request $request, $requestType) + { + parent::__construct($kernel, $request, $requestType); + + $this->setController($controller); + } + + /** + * Returns the current controller + * + * @return callable + * + * @api + */ + public function getController() + { + return $this->controller; + } + + /** + * Sets a new controller + * + * @param callable $controller + * + * @throws \LogicException + * + * @api + */ + public function setController($controller) + { + // controller must be a callable + if (!is_callable($controller)) { + throw new \LogicException(sprintf('The controller must be a callable (%s given).', $this->varToString($controller))); + } + + $this->controller = $controller; + } + + 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/Event/FilterResponseEvent.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php new file mode 100644 index 0000000..8de76a5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.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; +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 + * + * @api + */ +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 + * + * @api + */ + public function getResponse() + { + return $this->response; + } + + /** + * Sets a new response object + * + * @param Response $response + * + * @api + */ + public function setResponse(Response $response) + { + $this->response = $response; + } +} 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..f71ccae --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.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\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 + * + * @api + */ +class GetResponseEvent extends KernelEvent +{ + /** + * The response object + * @var Response + */ + private $response; + + /** + * Returns the response object + * + * @return Response + * + * @api + */ + public function getResponse() + { + return $this->response; + } + + /** + * Sets a response and stops event propagation + * + * @param Response $response + * + * @api + */ + public function setResponse(Response $response) + { + $this->response = $response; + + $this->stopPropagation(); + } + + /** + * Returns whether a response was set + * + * @return Boolean Whether a response was set + * + * @api + */ + 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..1bc0f98 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.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 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 + * + * @api + */ +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 + * + * @api + */ + public function getControllerResult() + { + return $this->controllerResult; + } + + /** + * Assigns the return value of the controller. + * + * @param mixed The controller return value + * + * @api + */ + 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..3787f62 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.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\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 + * + * @api + */ +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 + * + * @api + */ + 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 + * + * @api + */ + 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..e57eed4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/KernelEvent.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\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 + * + * @api + */ +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 integer + */ + 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 + * + * @api + */ + public function getKernel() + { + return $this->kernel; + } + + /** + * Returns the request the kernel is currently processing + * + * @return Request + * + * @api + */ + public function getRequest() + { + return $this->request; + } + + /** + * Returns the request type the kernel is currently processing + * + * @return integer One of HttpKernelInterface::MASTER_REQUEST and + * HttpKernelInterface::SUB_REQUEST + * + * @api + */ + public function getRequestType() + { + return $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..caa7b9f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/PostResponseEvent.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\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Allows to execute logic after a response was sent + * + * @author Jordi Boggiano + */ +class PostResponseEvent extends Event +{ + /** + * The kernel in which this event was thrown + * @var HttpKernelInterface + */ + private $kernel; + + private $request; + + private $response; + + public function __construct(HttpKernelInterface $kernel, Request $request, Response $response) + { + $this->kernel = $kernel; + $this->request = $request; + $this->response = $response; + } + + /** + * Returns the kernel in which this event was thrown. + * + * @return HttpKernelInterface + */ + public function getKernel() + { + return $this->kernel; + } + + /** + * Returns the request for which this event was thrown. + * + * @return Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * 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/ErrorsLoggerListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ErrorsLoggerListener.php new file mode 100644 index 0000000..13940ab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ErrorsLoggerListener.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\HttpKernel\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Debug\ErrorHandler; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * Injects the logger into the ErrorHandler, so that it can log various errors. + * + * @author Colin Frei + * @author Konstantin Myakshin + */ +class ErrorsLoggerListener implements EventSubscriberInterface +{ + private $channel; + + private $logger; + + public function __construct($channel, LoggerInterface $logger = null) + { + $this->channel = $channel; + $this->logger = $logger; + } + + public function injectLogger() + { + if (null !== $this->logger) { + ErrorHandler::setLogger($this->logger, $this->channel); + } + } + + public static function getSubscribedEvents() + { + return array(KernelEvents::REQUEST => 'injectLogger'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/EsiListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/EsiListener.php new file mode 100644 index 0000000..1176a73 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/EsiListener.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\HttpKernel\EventListener; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * EsiListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for ESI. + * + * @author Fabien Potencier + */ +class EsiListener implements EventSubscriberInterface +{ + private $esi; + + /** + * Constructor. + * + * @param Esi $esi An ESI instance + */ + public function __construct(Esi $esi = null) + { + $this->esi = $esi; + } + + /** + * Filters the Response. + * + * @param FilterResponseEvent $event A FilterResponseEvent instance + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType() || null === $this->esi) { + return; + } + + $this->esi->addSurrogateControl($event->getResponse()); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::RESPONSE => 'onKernelResponse', + ); + } +} 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..4ee5df4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.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\EventListener; + +use Psr\Log\LoggerInterface; +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\FlattenException; +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) + { + static $handling; + + if (true === $handling) { + return false; + } + + $handling = true; + + $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())); + + $attributes = array( + '_controller' => $this->controller, + 'exception' => FlattenException::create($exception), + 'logger' => $this->logger instanceof DebugLoggerInterface ? $this->logger : null, + 'format' => $request->getRequestFormat(), + ); + + $request = $request->duplicate(null, null, $attributes); + $request->setMethod('GET'); + + try { + $response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, true); + } catch (\Exception $e) { + $this->logException($exception, sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $e->getMessage()), false); + + // set handling to false otherwise it wont be able to handle further more + $handling = false; + + // re-throw the exception from within HttpKernel as this is a catch-all + return; + } + + $event->setResponse($response); + + $handling = false; + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::EXCEPTION => array('onKernelException', -128), + ); + } + + /** + * Logs an exception. + * + * @param \Exception $exception The original \Exception instance + * @param string $message The error message to log + * @param Boolean $original False when the handling of the exception thrown another exception + */ + protected function logException(\Exception $exception, $message, $original = true) + { + $isCritical = !$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500; + $context = array('exception' => $exception); + if (null !== $this->logger) { + if ($isCritical) { + $this->logger->critical($message, $context); + } else { + $this->logger->error($message, $context); + } + } elseif (!$original || $isCritical) { + error_log($message); + } + } +} 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..ef3fad3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.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\EventListener; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\IpUtils; +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 the request does not come from a trusted IP, it throws an + * AccessDeniedHttpException exception. + * + * @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 ($this->fragmentPath !== rawurldecode($request->getPathInfo())) { + return; + } + + $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(); + } + + // does the Request come from a trusted IP? + $trustedIps = array_merge($this->getLocalIpAddresses(), $request->getTrustedProxies()); + $remoteAddress = $request->server->get('REMOTE_ADDR'); + if (IpUtils::checkIp($remoteAddress, $trustedIps)) { + return; + } + + // 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(); + } + + protected function getLocalIpAddresses() + { + return array('127.0.0.1', 'fe80::1', '::1'); + } + + 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..0b864c0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.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\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +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; + + public function __construct($defaultLocale = 'en', RequestContextAwareInterface $router = null) + { + $this->defaultLocale = $defaultLocale; + $this->router = $router; + } + + public function setRequest(Request $request = null) + { + if (null === $request) { + return; + } + + if ($locale = $request->attributes->get('_locale')) { + $request->setLocale($locale); + } + + if (null !== $this->router) { + $this->router->getContext()->setParameter('_locale', $request->getLocale()); + } + } + + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + $request->setDefaultLocale($this->defaultLocale); + + $this->setRequest($request); + } + + public static function getSubscribedEvents() + { + return array( + // must be registered after the Router to have access to the _locale + KernelEvents::REQUEST => array(array('onKernelRequest', 16)), + ); + } +} 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..5b2228b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.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\EventListener; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Profiler\Profile; +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpFoundation\RequestMatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * ProfilerListener collects data for the current request by listening to the onKernelResponse event. + * + * @author Fabien Potencier + */ +class ProfilerListener implements EventSubscriberInterface +{ + protected $profiler; + protected $matcher; + protected $onlyException; + protected $onlyMasterRequests; + protected $exception; + protected $children; + protected $requests; + protected $profiles; + + /** + * Constructor. + * + * @param Profiler $profiler A Profiler instance + * @param RequestMatcherInterface $matcher A RequestMatcher instance + * @param Boolean $onlyException true if the profiler only collects data when an exception occurs, false otherwise + * @param Boolean $onlyMasterRequests true if the profiler only collects data when the request is a master request, false otherwise + */ + public function __construct(Profiler $profiler, RequestMatcherInterface $matcher = null, $onlyException = false, $onlyMasterRequests = false) + { + $this->profiler = $profiler; + $this->matcher = $matcher; + $this->onlyException = (Boolean) $onlyException; + $this->onlyMasterRequests = (Boolean) $onlyMasterRequests; + $this->children = new \SplObjectStorage(); + $this->profiles = array(); + } + + /** + * Handles the onKernelException event. + * + * @param GetResponseForExceptionEvent $event A GetResponseForExceptionEvent instance + */ + public function onKernelException(GetResponseForExceptionEvent $event) + { + if ($this->onlyMasterRequests && HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + return; + } + + $this->exception = $event->getException(); + } + + public function onKernelRequest(GetResponseEvent $event) + { + $this->requests[] = $event->getRequest(); + } + + /** + * Handles the onKernelResponse event. + * + * @param FilterResponseEvent $event A FilterResponseEvent instance + */ + public function onKernelResponse(FilterResponseEvent $event) + { + $master = HttpKernelInterface::MASTER_REQUEST === $event->getRequestType(); + 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[] = $profile; + + if (null !== $exception) { + foreach ($this->profiles as $profile) { + $this->profiler->saveProfile($profile); + } + + return; + } + + // keep the profile as the child of its parent + if (!$master) { + array_pop($this->requests); + + $parent = end($this->requests); + + // when simulating requests, we might not have the parent + if ($parent) { + $profiles = isset($this->children[$parent]) ? $this->children[$parent] : array(); + $profiles[] = $profile; + $this->children[$parent] = $profiles; + } + } + + if (isset($this->children[$request])) { + foreach ($this->children[$request] as $child) { + $profile->addChild($child); + } + $this->children[$request] = array(); + } + + if ($master) { + $this->saveProfiles($profile); + } + } + + public static function getSubscribedEvents() + { + return array( + // kernel.request must be registered as early as possible to not break + // when an exception is thrown in any other kernel.request listener + KernelEvents::REQUEST => array('onKernelRequest', 1024), + KernelEvents::RESPONSE => array('onKernelResponse', -100), + KernelEvents::EXCEPTION => 'onKernelException', + ); + } + + /** + * Saves the profile hierarchy. + * + * @param Profile $profile The root profile + */ + private function saveProfiles(Profile $profile) + { + $this->profiler->saveProfile($profile); + foreach ($profile->getChildren() as $profile) { + $this->saveProfiles($profile); + } + } +} 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..669980c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.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\EventListener; + +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; +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 (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + 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..f68716c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/RouterListener.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\HttpKernel\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +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 $request; + + /** + * Constructor. + * + * @param UrlMatcherInterface|RequestMatcherInterface $matcher The Url or Request matcher + * @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, 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->logger = $logger; + } + + /** + * Sets the current Request. + * + * The application should call this method whenever the Request + * object changes (entering a Request scope for instance, but + * also when leaving a Request scope -- especially when they are + * nested). + * + * @param Request|null $request A Request instance + */ + public function setRequest(Request $request = null) + { + if (null !== $request && $this->request !== $request) { + $this->context->fromRequest($request); + } + $this->request = $request; + } + + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + + // initialize the context that is also used by the generator (assuming matcher and generator share the same context instance) + // we call setRequest even if most of the time, it has already been done to keep compatibility + // with frameworks which do not use the Symfony service container + $this->setRequest($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" (parameters: %s)', $parameters['_route'], $this->parametersToString($parameters))); + } + + $request->attributes->add($parameters); + unset($parameters['_route']); + unset($parameters['_controller']); + $request->attributes->set('_route_params', $parameters); + } catch (ResourceNotFoundException $e) { + $message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo()); + + 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(), strtoupper(implode(', ', $e->getAllowedMethods()))); + + throw new MethodNotAllowedHttpException($e->getAllowedMethods(), $message, $e); + } + } + + private function parametersToString(array $parameters) + { + $pieces = array(); + foreach ($parameters as $key => $val) { + $pieces[] = sprintf('"%s": "%s"', $key, (is_string($val) ? $val : json_encode($val))); + } + + return implode(', ', $pieces); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array(array('onKernelRequest', 32)), + ); + } +} 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..88505fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/StreamedResponseListener.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\HttpKernel\EventListener; + +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; +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 (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + 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/Exception/AccessDeniedHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/AccessDeniedHttpException.php new file mode 100644 index 0000000..714102b --- /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 integer $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..3346345 --- /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 integer $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..e416b34 --- /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 integer $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/FatalErrorException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/FatalErrorException.php new file mode 100644 index 0000000..1f1ef1a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/FatalErrorException.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\Exception; + +use Symfony\Component\Debug\Exception\FatalErrorException as DebugFatalErrorException; + +/** + * Fatal Error Exception. + * + * @author Konstanton Myakshin + * + * @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead. + */ +class FatalErrorException extends DebugFatalErrorException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/FlattenException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/FlattenException.php new file mode 100644 index 0000000..0168afc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/FlattenException.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\Exception; + +use Symfony\Component\Debug\Exception\FlattenException as DebugFlattenException; + +/** + * FlattenException wraps a PHP Exception to be able to serialize it. + * + * Basically, this class removes all objects from the trace. + * + * @author Fabien Potencier + * + * @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead. + */ +class FlattenException extends DebugFlattenException +{ +} 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..9fea164 --- /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 integer $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..dd4a9dc --- /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 integer 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..7aca014 --- /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 integer $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..3a81586 --- /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 integer $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..6ac448a --- /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 integer $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..547976c --- /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 integer $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..4126c88 --- /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 integer $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..75ba177 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/PreconditionRequiredHttpException.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; + +/** + * 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 integer $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..09bbb6e --- /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 integer $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..b1232ef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/TooManyRequestsHttpException.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\Exception; + +/** + * TooManyRequestsHttpException. + * + * @author Ben Ramsey + * @see http://tools.ietf.org/html/rfc6585 + */ +class TooManyRequestsHttpException extends HttpException +{ + /** + * Constructor. + * + * @param integer|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 integer $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..2527d62 --- /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 integer $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/UnsupportedMediaTypeHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/UnsupportedMediaTypeHttpException.php new file mode 100644 index 0000000..88bceec --- /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 integer $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/EsiFragmentRenderer.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/EsiFragmentRenderer.php new file mode 100644 index 0000000..68b1a87 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/EsiFragmentRenderer.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\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\HttpCache\Esi; + +/** + * Implements the ESI rendering strategy. + * + * @author Fabien Potencier + */ +class EsiFragmentRenderer extends RoutableFragmentRenderer +{ + private $esi; + private $inlineStrategy; + + /** + * Constructor. + * + * The "fallback" strategy when ESI is not available should always be an + * instance of InlineFragmentRenderer. + * + * @param Esi $esi An Esi instance + * @param InlineFragmentRenderer $inlineStrategy The inline strategy to use when ESI is not supported + */ + public function __construct(Esi $esi, InlineFragmentRenderer $inlineStrategy) + { + $this->esi = $esi; + $this->inlineStrategy = $inlineStrategy; + } + + /** + * {@inheritdoc} + * + * Note that if the current Request has no ESI 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 an esi:include tag + * + * @see Symfony\Component\HttpKernel\HttpCache\ESI + */ + public function render($uri, Request $request, array $options = array()) + { + if (!$this->esi->hasSurrogateEsiCapability($request)) { + return $this->inlineStrategy->render($uri, $request, $options); + } + + if ($uri instanceof ControllerReference) { + $uri = $this->generateFragmentUri($uri, $request); + } + + $alt = isset($options['alt']) ? $options['alt'] : null; + if ($alt instanceof ControllerReference) { + $alt = $this->generateFragmentUri($alt, $request); + } + + $tag = $this->esi->renderIncludeTag($uri, $alt, isset($options['ignore_errors']) ? $options['ignore_errors'] : false, isset($options['comment']) ? $options['comment'] : ''); + + return new Response($tag); + } + + /** + * {@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..af9b9ba --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.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\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\StreamedResponse; +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; + private $request; + + /** + * Constructor. + * + * @param FragmentRendererInterface[] $renderers An array of FragmentRendererInterface instances + * @param Boolean $debug Whether the debug mode is enabled or not + */ + public function __construct(array $renderers = array(), $debug = false) + { + $this->renderers = array(); + 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; + } + + /** + * Sets the current Request. + * + * @param Request $request The current Request + */ + public function setRequest(Request $request = null) + { + $this->request = $request; + } + + /** + * 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 \RuntimeException when the Response is not successful + */ + 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 (null === $this->request) { + throw new \LogicException('Rendering a fragment can only be done when handling a master Request.'); + } + + return $this->deliver($this->renderers[$renderer]->render($uri, $this->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->request->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..a758d2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/FragmentRendererInterface.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\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ControllerReference; + +/** + * Interface implemented by all rendering strategies. + * + * @author Fabien Potencier + * + * @see Symfony\Component\HttpKernel\FragmentRenderer + */ +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..1f059da --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.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\Component\HttpKernel\Fragment; + +if (!defined('ENT_SUBSTITUTE')) { + define('ENT_SUBSTITUTE', 8); +} + +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 Boolean 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.'); + } + + $uri = $this->signer->sign($this->generateFragmentUri($uri, $request)); + } + + // 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) { + foreach($attributes as $attribute => $value) { + $renderedAttributes .= sprintf( + ' %s="%s"', + htmlspecialchars($attribute, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset, false), + htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset, false) + ); + } + } + + return new Response(sprintf('%s', $uri, $renderedAttributes, $content)); + } + + /** + * @param string $template + * + * @return boolean + */ + private function templateExists($template) + { + if ($this->templating instanceof EngineInterface) { + return $this->templating->exists($template); + } + + $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..1cbdeab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.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\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 + */ + 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; + $uri = $this->generateFragmentUri($uri, $request); + } + + $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 + while (ob_get_level() > $level) { + ob_get_clean(); + } + + 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 { + $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..879a0bb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/RoutableFragmentRenderer.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\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 + * + * @return string A fragment URI + */ + protected function generateFragmentUri(ControllerReference $reference, Request $request) + { + if (!isset($reference->attributes['_format'])) { + $reference->attributes['_format'] = $request->getRequestFormat(); + } + + $reference->attributes['_controller'] = $reference->controller; + + $reference->query['_path'] = http_build_query($reference->attributes, '', '&'); + + return $request->getUriForPath($this->fragmentPath.'?'.http_build_query($reference->query, '', '&')); + } +} 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..455b3dc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Esi.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\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 +{ + private $contentTypes; + + /** + * 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; + } + + /** + * Returns a new cache strategy instance. + * + * @return EsiResponseCacheStrategyInterface A EsiResponseCacheStrategyInterface instance + */ + public function createCacheStrategy() + { + return new EsiResponseCacheStrategy(); + } + + /** + * Checks that at least one surrogate has ESI/1.0 capability. + * + * @param Request $request A Request instance + * + * @return Boolean true if one surrogate has ESI/1.0 capability, false otherwise + */ + public function hasSurrogateEsiCapability(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 addSurrogateEsiCapability(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 Boolean true if the Response needs to be parsed, false otherwise + */ + public function needsEsiParsing(Response $response) + { + if (!$control = $response->headers->get('Surrogate-Control')) { + return false; + } + + return (Boolean) preg_match('#content="[^"]*ESI/1.0[^"]*"#', $control); + } + + /** + * Renders an ESI tag. + * + * @param string $uri A URI + * @param string $alt An alternate URI + * @param Boolean $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) + { + $this->request = $request; + $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 = str_replace(array('', ''), $content); + $content = preg_replace_callback('##', array($this, 'handleEsiIncludeTag'), $content); + $content = preg_replace('#]*(?:/|#', '', $content); + $content = preg_replace('#.*?#', '', $content); + + $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 Boolean $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; + } + } + } + + /** + * Handles an ESI include tag (called internally). + * + * @param array $attributes An array containing the attributes. + * + * @return string The response content for the include. + * + * @throws \RuntimeException + */ + private function handleEsiIncludeTag($attributes) + { + $options = array(); + preg_match_all('/(src|onerror|alt)="([^"]*?)"/', $attributes[1], $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.'); + } + + return sprintf('esi->handle($this, \'%s\', \'%s\', %s) ?>'."\n", + $options['src'], + isset($options['alt']) ? $options['alt'] : null, + isset($options['onerror']) && 'continue' == $options['onerror'] ? 'true' : 'false' + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.php new file mode 100644 index 0000000..6384af9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.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; + +/** + * EsiResponseCacheStrategy knows how to compute the Response cache HTTP header + * based on the different ESI response cache headers. + * + * This implementation changes the master response TTL to the smallest TTL received + * or force validation if one of the ESI has validation cache strategy. + * + * @author Fabien Potencier + */ +class EsiResponseCacheStrategy implements EsiResponseCacheStrategyInterface +{ + 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/EsiResponseCacheStrategyInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategyInterface.php new file mode 100644 index 0000000..0fb8a12 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategyInterface.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; + +/** + * EsiResponseCacheStrategyInterface implementations know how to compute the + * Response cache HTTP header based on the different ESI response cache headers. + * + * @author Fabien Potencier + */ +interface EsiResponseCacheStrategyInterface +{ + /** + * 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/HttpCache.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php new file mode 100644 index 0000000..63cde7e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -0,0 +1,688 @@ + + * + * 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; +use Symfony\Component\HttpKernel\HttpCache\Esi; + +/** + * Cache provides HTTP caching. + * + * @author Fabien Potencier + * + * @api + */ +class HttpCache implements HttpKernelInterface, TerminableInterface +{ + private $kernel; + private $store; + private $request; + private $esi; + private $esiCacheStrategy; + private $traces; + + /** + * 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 Esi $esi An Esi instance + * @param array $options An array of options + */ + public function __construct(HttpKernelInterface $kernel, StoreInterface $store, Esi $esi = null, array $options = array()) + { + $this->store = $store; + $this->kernel = $kernel; + + // 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); + $this->esi = $esi; + $this->traces = array(); + } + + /** + * 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 Esi instance + * + * @return Esi An Esi instance + */ + public function getEsi() + { + return $this->esi; + } + + /** + * {@inheritdoc} + * + * @api + */ + 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->esi) { + $this->esiCacheStrategy = $this->esi->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(new \DateTime(null, new \DateTimeZone('UTC'))); + + if (HttpKernelInterface::MASTER_REQUEST === $type && $this->options['debug']) { + $response->headers->set('X-Symfony-Cache', $this->getLog()); + } + + if (null !== $this->esi) { + if (HttpKernelInterface::MASTER_REQUEST === $type) { + $this->esiCacheStrategy->update($response); + } else { + $this->esiCacheStrategy->add($response); + } + } + + $response->prepare($request); + + $response->isNotModified($request); + + return $response; + } + + /** + * {@inheritdoc} + * + * @api + */ + 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 Boolean $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 Boolean $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 Boolean $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); + } + + 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 Boolean $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 Boolean $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 ($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']); + } + + 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 Boolean $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->esi) { + $this->esi->addSurrogateEsiCapability($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'); + + // 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); + + 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 Boolean 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 Boolean 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, $entry); + + // 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 + * + * @return 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->esi && $this->esi->needsEsiParsing($response)) { + $this->esi->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 Boolean 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/Store.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Store.php new file mode 100644 index 0000000..a1cda1f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Store.php @@ -0,0 +1,429 @@ + + * + * 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 + */ + public function __construct($root) + { + $this->root = $root; + if (!is_dir($this->root)) { + mkdir($this->root, 0777, true); + } + $this->keyCache = new \SplObjectStorage(); + $this->locks = array(); + } + + /** + * Cleanups storage. + */ + public function cleanup() + { + // unlock everything + foreach ($this->locks as $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 Boolean|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)) { + 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 Boolean 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) + { + return is_file($this->getPath($this->getCacheKey($request).'.lck')); + } + + /** + * 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 null; + } + + // find a cached entry that matches the request. + $match = null; + foreach ($entries as $entry) { + if ($this->requestsMatch(isset($entry[1]['vary'][0]) ? $entry[1]['vary'][0] : '', $request->headers->all(), $entry[0])) { + $match = $entry; + + break; + } + } + + if (null === $match) { + return null; + } + + 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. + return null; + } + + /** + * 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'.sha1($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 Boolean 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 Boolean 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 Boolean + */ + private function save($key, $data) + { + $path = $this->getPath($key); + if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true)) { + 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); + } + + /** + * 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] = 'md'.sha1($request->getUri()); + } + + /** + * 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..29a54d8 --- /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 Boolean|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 Boolean 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 Boolean true if lock exists, false otherwise + */ + public function isLocked(Request $request); + + /** + * Purges data for the given URL. + * + * @param string $url A URL + * + * @return Boolean 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/HttpKernel.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php new file mode 100644 index 0000000..ae7a44c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -0,0 +1,243 @@ + + * + * 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\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\Response; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * HttpKernel notifies events to convert a Request object to a Response one. + * + * @author Fabien Potencier + * + * @api + */ +class HttpKernel implements HttpKernelInterface, TerminableInterface +{ + protected $dispatcher; + protected $resolver; + + /** + * Constructor + * + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * @param ControllerResolverInterface $resolver A ControllerResolverInterface instance + * + * @api + */ + public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver) + { + $this->dispatcher = $dispatcher; + $this->resolver = $resolver; + } + + /** + * {@inheritdoc} + * + * @api + */ + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + try { + return $this->handleRaw($request, $type); + } catch (\Exception $e) { + if (false === $catch) { + throw $e; + } + + return $this->handleException($e, $request, $type); + } + } + + /** + * {@inheritdoc} + * + * @api + */ + public function terminate(Request $request, Response $response) + { + $this->dispatcher->dispatch(KernelEvents::TERMINATE, new PostResponseEvent($this, $request, $response)); + } + + /** + * Handles a request to convert it to a response. + * + * Exceptions are not caught. + * + * @param Request $request A Request instance + * @param integer $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) + { + // 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". Maybe you forgot to add the matching route in your routing configuration?', $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 integer $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); + + return $event->getResponse(); + } + + /** + * Handles an exception by trying to convert it to a Response. + * + * @param \Exception $e An \Exception instance + * @param Request $request A Request instance + * @param integer $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()) { + 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..f49d37c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernelInterface.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; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * HttpKernelInterface handles a Request to convert it to a Response. + * + * @author Fabien Potencier + * + * @api + */ +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 integer $type The type of the request + * (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) + * @param Boolean $catch Whether to catch exceptions or not + * + * @return Response A Response instance + * + * @throws \Exception When an Exception occurs during processing + * + * @api + */ + 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..644d47b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php @@ -0,0 +1,795 @@ + + * + * 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\ClosureLoader; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +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 + * + * @api + */ +abstract class Kernel implements KernelInterface, TerminableInterface +{ + /** + * @var BundleInterface[] + */ + protected $bundles; + + protected $bundleMap; + protected $container; + protected $rootDir; + protected $environment; + protected $debug; + protected $booted; + protected $name; + protected $startTime; + protected $classes; + protected $loadClassCache; + + const VERSION = '2.3.1'; + const VERSION_ID = '20301'; + const MAJOR_VERSION = '2'; + const MINOR_VERSION = '3'; + const RELEASE_VERSION = '1'; + const EXTRA_VERSION = ''; + + /** + * Constructor. + * + * @param string $environment The environment + * @param Boolean $debug Whether to enable debugging or not + * + * @api + */ + public function __construct($environment, $debug) + { + $this->environment = $environment; + $this->debug = (Boolean) $debug; + $this->booted = false; + $this->rootDir = $this->getRootDir(); + $this->name = $this->getName(); + $this->classes = array(); + $this->bundles = array(); + + if ($this->debug) { + $this->startTime = microtime(true); + } + + $this->init(); + } + + /** + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Move your logic in the constructor instead. + */ + public function init() + { + } + + public function __clone() + { + if ($this->debug) { + $this->startTime = microtime(true); + } + + $this->booted = false; + $this->container = null; + } + + /** + * Boots the current kernel. + * + * @api + */ + 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} + * + * @api + */ + public function terminate(Request $request, Response $response) + { + if (false === $this->booted) { + return; + } + + if ($this->getHttpKernel() instanceof TerminableInterface) { + $this->getHttpKernel()->terminate($request, $response); + } + } + + /** + * {@inheritdoc} + * + * @api + */ + 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} + * + * @api + */ + 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} + * + * @api + */ + public function getBundles() + { + return $this->bundles; + } + + /** + * {@inheritdoc} + * + * @api + */ + public function isClassInActiveBundle($class) + { + foreach ($this->getBundles() as $bundle) { + if (0 === strpos($class, $bundle->getNamespace())) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + * + * @api + */ + 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]; + } + + /** + * 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: + * + * @/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 Boolean $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 + * @throws \RuntimeException if a custom resource is hidden by a resource in a derived bundle + * + * @api + */ + 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} + * + * @api + */ + public function getName() + { + if (null === $this->name) { + $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir)); + } + + return $this->name; + } + + /** + * {@inheritdoc} + * + * @api + */ + public function getEnvironment() + { + return $this->environment; + } + + /** + * {@inheritdoc} + * + * @api + */ + public function isDebug() + { + return $this->debug; + } + + /** + * {@inheritdoc} + * + * @api + */ + public function getRootDir() + { + if (null === $this->rootDir) { + $r = new \ReflectionObject($this); + $this->rootDir = str_replace('\\', '/', dirname($r->getFileName())); + } + + return $this->rootDir; + } + + /** + * {@inheritdoc} + * + * @api + */ + 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} + * + * @api + */ + public function getCacheDir() + { + return $this->rootDir.'/cache/'.$this->environment; + } + + /** + * {@inheritdoc} + * + * @api + */ + public function getLogDir() + { + return $this->rootDir.'/logs'; + } + + /** + * {@inheritdoc} + * + * @api + */ + 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 (count($diff = array_values(array_diff(array_keys($directChildren), array_keys($this->bundles))))) { + 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; + + $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' => $this->rootDir, + 'kernel.environment' => $this->environment, + 'kernel.debug' => $this->debug, + 'kernel.name' => $this->name, + 'kernel.cache_dir' => $this->getCacheDir(), + 'kernel.logs_dir' => $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)) { + 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)); + + 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')) { + $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')) { + $dumper->setProxyDumper(new ProxyDumper()); + } + + $content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass)); + if (!$this->debug) { + $content = self::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 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); + for (reset($tokens); false !== $token = current($tokens); next($tokens)) { + if (is_string($token)) { + $rawChunk .= $token; + } elseif (T_START_HEREDOC === $token[0]) { + $output .= preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $rawChunk).$token[1]; + do { + $token = next($tokens); + $output .= $token[1]; + } while ($token[0] !== T_END_HEREDOC); + $rawChunk = ''; + } elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) { + $rawChunk .= $token[1]; + } + } + + // replace multiple new lines with a single newline + $output .= preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $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..fce48ac --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/KernelEvents.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\HttpKernel; + +/** + * Contains all events thrown in the HttpKernel component + * + * @author Bernhard Schussek + * + * @api + */ +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. + * + * @var string + * + * @api + */ + 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. + * + * @var string + * + * @api + */ + 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. + * + * @var string + * + * @api + */ + 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. + * + * @var string + * + * @api + */ + 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. + * + * @var string + * + * @api + */ + 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. + * + * @var string + */ + const TERMINATE = 'kernel.terminate'; +} 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..dd37b60 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/KernelInterface.php @@ -0,0 +1,208 @@ + + * + * 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\HttpKernelInterface; +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 + * + * @api + */ +interface KernelInterface extends HttpKernelInterface, \Serializable +{ + /** + * Returns an array of bundles to registers. + * + * @return BundleInterface[] An array of bundle instances. + * + * @api + */ + public function registerBundles(); + + /** + * Loads the container configuration + * + * @param LoaderInterface $loader A LoaderInterface instance + * + * @api + */ + public function registerContainerConfiguration(LoaderInterface $loader); + + /** + * Boots the current kernel. + * + * @api + */ + public function boot(); + + /** + * Shutdowns the kernel. + * + * This method is mainly useful when doing functional testing. + * + * @api + */ + public function shutdown(); + + /** + * Gets the registered bundle instances. + * + * @return BundleInterface[] An array of registered bundle instances + * + * @api + */ + public function getBundles(); + + /** + * Checks if a given class name belongs to an active bundle. + * + * @param string $class A class name + * + * @return Boolean true if the class belongs to an active bundle, false otherwise + * + * @api + */ + public function isClassInActiveBundle($class); + + /** + * Returns a bundle and optionally its descendants by its name. + * + * @param string $name Bundle name + * @param Boolean $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 + * + * @api + */ + 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/BundleName/path/without/Resources + * + * @param string $name A resource name to locate + * @param string $dir A directory where to look for the resource first + * @param Boolean $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 + * + * @api + */ + public function locateResource($name, $dir = null, $first = true); + + /** + * Gets the name of the kernel + * + * @return string The kernel name + * + * @api + */ + public function getName(); + + /** + * Gets the environment. + * + * @return string The current environment + * + * @api + */ + public function getEnvironment(); + + /** + * Checks if debug mode is enabled. + * + * @return Boolean true if debug mode is enabled, false otherwise + * + * @api + */ + public function isDebug(); + + /** + * Gets the application root dir. + * + * @return string The application root dir + * + * @api + */ + public function getRootDir(); + + /** + * Gets the current container. + * + * @return ContainerInterface A ContainerInterface instance + * + * @api + */ + public function getContainer(); + + /** + * Gets the request start time (not available if debug is disabled). + * + * @return integer The request start timestamp + * + * @api + */ + public function getStartTime(); + + /** + * Gets the cache directory. + * + * @return string The cache directory + * + * @api + */ + public function getCacheDir(); + + /** + * Gets the log directory. + * + * @return string The log directory + * + * @api + */ + public function getLogDir(); + + /** + * Gets the charset of the application. + * + * @return string The charset + * + * @api + */ + 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..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/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..4442c63 --- /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 integer The number of errors + */ + public function countErrors(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Log/LoggerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Log/LoggerInterface.php new file mode 100644 index 0000000..148c92c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Log/LoggerInterface.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\HttpKernel\Log; + +use Psr\Log\LoggerInterface as PsrLogger; + +/** + * LoggerInterface. + * + * @author Fabien Potencier + * + * @deprecated since 2.2, to be removed in 3.0. Type-hint \Psr\Log\LoggerInterface instead. + * @api + */ +interface LoggerInterface extends PsrLogger +{ + /** + * @api + * @deprecated since 2.2, to be removed in 3.0. Use emergency() which is PSR-3 compatible. + */ + public function emerg($message, array $context = array()); + + /** + * @api + * @deprecated since 2.2, to be removed in 3.0. Use critical() which is PSR-3 compatible. + */ + public function crit($message, array $context = array()); + + /** + * @api + * @deprecated since 2.2, to be removed in 3.0. Use error() which is PSR-3 compatible. + */ + public function err($message, array $context = array()); + + /** + * @api + * @deprecated since 2.2, to be removed in 3.0. Use warning() which is PSR-3 compatible. + */ + public function warn($message, array $context = array()); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Log/NullLogger.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Log/NullLogger.php new file mode 100644 index 0000000..98f932a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Log/NullLogger.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\Log; + +use Psr\Log\NullLogger as PsrNullLogger; + +/** + * NullLogger. + * + * @author Fabien Potencier + * + * @api + */ +class NullLogger extends PsrNullLogger implements LoggerInterface +{ + /** + * @api + * @deprecated since 2.2, to be removed in 3.0. Use emergency() which is PSR-3 compatible. + */ + public function emerg($message, array $context = array()) + { + } + + /** + * @api + * @deprecated since 2.2, to be removed in 3.0. Use critical() which is PSR-3 compatible. + */ + public function crit($message, array $context = array()) + { + } + + /** + * @api + * @deprecated since 2.2, to be removed in 3.0. Use error() which is PSR-3 compatible. + */ + public function err($message, array $context = array()) + { + } + + /** + * @api + * @deprecated since 2.2, to be removed in 3.0. Use warning() which is PSR-3 compatible. + */ + public function warn($message, array $context = array()) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/BaseMemcacheProfilerStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/BaseMemcacheProfilerStorage.php new file mode 100644 index 0000000..69ff973 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/BaseMemcacheProfilerStorage.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\Profiler; + +/** + * Base Memcache storage for profiling information in a Memcache. + * + * @author Andrej Hudec + */ +abstract class BaseMemcacheProfilerStorage implements ProfilerStorageInterface +{ + const TOKEN_PREFIX = 'sf_profiler_'; + + protected $dsn; + protected $lifetime; + + /** + * Constructor. + * + * @param string $dsn A data source name + * @param string $username + * @param string $password + * @param int $lifetime The lifetime to use for the purge + */ + public function __construct($dsn, $username = '', $password = '', $lifetime = 86400) + { + $this->dsn = $dsn; + $this->lifetime = (int) $lifetime; + } + + /** + * {@inheritdoc} + */ + public function find($ip, $url, $limit, $method, $start = null, $end = null) + { + $indexName = $this->getIndexName(); + + $indexContent = $this->getValue($indexName); + if (!$indexContent) { + return array(); + } + + $profileList = explode("\n", $indexContent); + $result = array(); + + foreach ($profileList as $item) { + + if ($limit === 0) { + break; + } + + if ($item=='') { + continue; + } + + list($itemToken, $itemIp, $itemMethod, $itemUrl, $itemTime, $itemParent) = explode("\t", $item, 6); + + $itemTime = (int) $itemTime; + + if ($ip && false === strpos($itemIp, $ip) || $url && false === strpos($itemUrl, $url) || $method && false === strpos($itemMethod, $method)) { + continue; + } + + if (!empty($start) && $itemTime < $start) { + continue; + } + + if (!empty($end) && $itemTime > $end) { + continue; + } + + $result[$itemToken] = array( + 'token' => $itemToken, + 'ip' => $itemIp, + 'method' => $itemMethod, + 'url' => $itemUrl, + 'time' => $itemTime, + 'parent' => $itemParent, + ); + --$limit; + } + + usort($result, function($a, $b) { + if ($a['time'] === $b['time']) { + return 0; + } + + return $a['time'] > $b['time'] ? -1 : 1; + }); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function purge() + { + // delete only items from index + $indexName = $this->getIndexName(); + + $indexContent = $this->getValue($indexName); + + if (!$indexContent) { + return false; + } + + $profileList = explode("\n", $indexContent); + + foreach ($profileList as $item) { + if ($item == '') { + continue; + } + + if (false !== $pos = strpos($item, "\t")) { + $this->delete($this->getItemName(substr($item, 0, $pos))); + } + } + + return $this->delete($indexName); + } + + /** + * {@inheritdoc} + */ + public function read($token) + { + if (empty($token)) { + return false; + } + + $profile = $this->getValue($this->getItemName($token)); + + if (false !== $profile) { + $profile = $this->createProfileFromData($token, $profile); + } + + return $profile; + } + + /** + * {@inheritdoc} + */ + public function write(Profile $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(), + ); + + $profileIndexed = false !== $this->getValue($this->getItemName($profile->getToken())); + + if ($this->setValue($this->getItemName($profile->getToken()), $data, $this->lifetime)) { + + if (!$profileIndexed) { + // Add to index + $indexName = $this->getIndexName(); + + $indexRow = implode("\t", array( + $profile->getToken(), + $profile->getIp(), + $profile->getMethod(), + $profile->getUrl(), + $profile->getTime(), + $profile->getParentToken(), + ))."\n"; + + return $this->appendValue($indexName, $indexRow, $this->lifetime); + } + + return true; + } + + return false; + } + + /** + * Retrieve item from the memcache server + * + * @param string $key + * + * @return mixed + */ + abstract protected function getValue($key); + + /** + * Store an item on the memcache server under the specified key + * + * @param string $key + * @param mixed $value + * @param int $expiration + * + * @return boolean + */ + abstract protected function setValue($key, $value, $expiration = 0); + + /** + * Delete item from the memcache server + * + * @param string $key + * + * @return boolean + */ + abstract protected function delete($key); + + /** + * Append data to an existing item on the memcache server + * @param string $key + * @param string $value + * @param int $expiration + * + * @return boolean + */ + abstract protected function appendValue($key, $value, $expiration = 0); + + private 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) { + continue; + } + + if (!$childProfileData = $this->getValue($this->getItemName($token))) { + continue; + } + + $profile->addChild($this->createProfileFromData($token, $childProfileData, $profile)); + } + + return $profile; + } + + /** + * Get item name + * + * @param string $token + * + * @return string + */ + private function getItemName($token) + { + $name = self::TOKEN_PREFIX.$token; + + if ($this->isItemNameValid($name)) { + return $name; + } + + return false; + } + + /** + * Get name of index + * + * @return string + */ + private function getIndexName() + { + $name = self::TOKEN_PREFIX.'index'; + + if ($this->isItemNameValid($name)) { + return $name; + } + + return false; + } + + private function isItemNameValid($name) + { + $length = strlen($name); + + if ($length > 250) { + throw new \RuntimeException(sprintf('The memcache item key "%s" is too long (%s bytes). Allowed maximum size is 250 bytes.', $name, $length)); + } + + return true; + } + +} 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..9265fc1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.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\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)) { + mkdir($this->folder, 0777, true); + } + } + + /** + * {@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)) { + list($csvToken, $csvIp, $csvMethod, $csvUrl, $csvTime, $csvParent) = str_getcsv($line); + + $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, + ); + } + + 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 null; + } + + return $this->createProfileFromData($token, unserialize(file_get_contents($file))); + } + + /** + * {@inheritdoc} + */ + 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)) { + mkdir($dir, 0777, true); + } + } + + // 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(), + )); + 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 null; + } + + 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/MemcacheProfilerStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/MemcacheProfilerStorage.php new file mode 100644 index 0000000..a78cec0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/MemcacheProfilerStorage.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\HttpKernel\Profiler; + +use Memcache; + +/** + * Memcache Profiler Storage + * + * @author Andrej Hudec + */ +class MemcacheProfilerStorage extends BaseMemcacheProfilerStorage +{ + /** + * @var Memcache + */ + private $memcache; + + /** + * Internal convenience method that returns the instance of the Memcache + * + * @return Memcache + * + * @throws \RuntimeException + */ + protected function getMemcache() + { + if (null === $this->memcache) { + if (!preg_match('#^memcache://(?(?=\[.*\])\[(.*)\]|(.*)):(.*)$#', $this->dsn, $matches)) { + throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Memcache with an invalid dsn "%s". The expected format is "memcache://[host]:port".', $this->dsn)); + } + + $host = $matches[1] ?: $matches[2]; + $port = $matches[3]; + + $memcache = new Memcache; + $memcache->addServer($host, $port); + + $this->memcache = $memcache; + } + + return $this->memcache; + } + + /** + * Set instance of the Memcache + * + * @param Memcache $memcache + */ + public function setMemcache($memcache) + { + $this->memcache = $memcache; + } + + /** + * {@inheritdoc} + */ + protected function getValue($key) + { + return $this->getMemcache()->get($key); + } + + /** + * {@inheritdoc} + */ + protected function setValue($key, $value, $expiration = 0) + { + return $this->getMemcache()->set($key, $value, false, time() + $expiration); + } + + /** + * {@inheritdoc} + */ + protected function delete($key) + { + return $this->getMemcache()->delete($key); + } + + /** + * {@inheritdoc} + */ + protected function appendValue($key, $value, $expiration = 0) + { + $memcache = $this->getMemcache(); + + if (method_exists($memcache, 'append')) { + + //Memcache v3.0 + if (!$result = $memcache->append($key, $value, false, $expiration)) { + return $memcache->set($key, $value, false, $expiration); + } + + return $result; + } + + //simulate append in Memcache <3.0 + $content = $memcache->get($key); + + return $memcache->set($key, $content.$value, false, $expiration); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/MemcachedProfilerStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/MemcachedProfilerStorage.php new file mode 100644 index 0000000..f7f6842 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/MemcachedProfilerStorage.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\HttpKernel\Profiler; + +use Memcached; + +/** + * Memcached Profiler Storage + * + * @author Andrej Hudec + */ +class MemcachedProfilerStorage extends BaseMemcacheProfilerStorage +{ + /** + * @var Memcached + */ + private $memcached; + + /** + * Internal convenience method that returns the instance of the Memcached + * + * @return Memcached + * + * @throws \RuntimeException + */ + protected function getMemcached() + { + if (null === $this->memcached) { + if (!preg_match('#^memcached://(?(?=\[.*\])\[(.*)\]|(.*)):(.*)$#', $this->dsn, $matches)) { + throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Memcached with an invalid dsn "%s". The expected format is "memcached://[host]:port".', $this->dsn)); + } + + $host = $matches[1] ?: $matches[2]; + $port = $matches[3]; + + $memcached = new Memcached; + + //disable compression to allow appending + $memcached->setOption(Memcached::OPT_COMPRESSION, false); + + $memcached->addServer($host, $port); + + $this->memcached = $memcached; + } + + return $this->memcached; + } + + /** + * Set instance of the Memcached + * + * @param Memcached $memcached + */ + public function setMemcached($memcached) + { + $this->memcached = $memcached; + } + + /** + * {@inheritdoc} + */ + protected function getValue($key) + { + return $this->getMemcached()->get($key); + } + + /** + * {@inheritdoc} + */ + protected function setValue($key, $value, $expiration = 0) + { + return $this->getMemcached()->set($key, $value, time() + $expiration); + } + + /** + * {@inheritdoc} + */ + protected function delete($key) + { + return $this->getMemcached()->delete($key); + } + + /** + * {@inheritdoc} + */ + protected function appendValue($key, $value, $expiration = 0) + { + $memcached = $this->getMemcached(); + + if (!$result = $memcached->append($key, $value)) { + return $memcached->set($key, $value, $expiration); + } + + return $result; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/MongoDbProfilerStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/MongoDbProfilerStorage.php new file mode 100644 index 0000000..38a522a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/MongoDbProfilerStorage.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\HttpKernel\Profiler; + +class MongoDbProfilerStorage implements ProfilerStorageInterface +{ + protected $dsn; + protected $lifetime; + private $mongo; + + /** + * Constructor. + * + * @param string $dsn A data source name + * @param string $username Not used + * @param string $password Not used + * @param integer $lifetime The lifetime to use for the purge + */ + public function __construct($dsn, $username = '', $password = '', $lifetime = 86400) + { + $this->dsn = $dsn; + $this->lifetime = (int) $lifetime; + } + + /** + * {@inheritdoc} + */ + public function find($ip, $url, $limit, $method, $start = null, $end = null) + { + $cursor = $this->getMongo()->find($this->buildQuery($ip, $url, $method, $start, $end), array('_id', 'parent', 'ip', 'method', 'url', 'time'))->sort(array('time' => -1))->limit($limit); + + $tokens = array(); + foreach ($cursor as $profile) { + $tokens[] = $this->getData($profile); + } + + return $tokens; + } + + /** + * {@inheritdoc} + */ + public function purge() + { + $this->getMongo()->remove(array()); + } + + /** + * {@inheritdoc} + */ + public function read($token) + { + $profile = $this->getMongo()->findOne(array('_id' => $token, 'data' => array('$exists' => true))); + + if (null !== $profile) { + $profile = $this->createProfileFromData($this->getData($profile)); + } + + return $profile; + } + + /** + * {@inheritdoc} + */ + public function write(Profile $profile) + { + $this->cleanup(); + + $record = array( + '_id' => $profile->getToken(), + 'parent' => $profile->getParentToken(), + 'data' => base64_encode(serialize($profile->getCollectors())), + 'ip' => $profile->getIp(), + 'method' => $profile->getMethod(), + 'url' => $profile->getUrl(), + 'time' => $profile->getTime() + ); + + $result = $this->getMongo()->update(array('_id' => $profile->getToken()), array_filter($record, function ($v) { return !empty($v); }), array('upsert' => true)); + + return (boolean) (isset($result['ok']) ? $result['ok'] : $result); + } + + /** + * Internal convenience method that returns the instance of the MongoDB Collection + * + * @return \MongoCollection + * + * @throws \RuntimeException + */ + protected function getMongo() + { + if ($this->mongo === null) { + if (preg_match('#^(mongodb://.*)/(.*)/(.*)$#', $this->dsn, $matches)) { + $server = $matches[1].(!empty($matches[2]) ? '/'.$matches[2] : ''); + $database = $matches[2]; + $collection = $matches[3]; + + $mongoClass = (version_compare(phpversion('mongo'), '1.3.0', '<')) ? '\Mongo' : '\MongoClient'; + $mongo = new $mongoClass($server); + $this->mongo = $mongo->selectCollection($database, $collection); + } else { + throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use MongoDB with an invalid dsn "%s". The expected format is "mongodb://[user:pass@]host/database/collection"', $this->dsn)); + } + } + + return $this->mongo; + } + + /** + * @param array $data + * + * @return Profile + */ + protected function createProfileFromData(array $data) + { + $profile = $this->getProfile($data); + + if ($data['parent']) { + $parent = $this->getMongo()->findOne(array('_id' => $data['parent'], 'data' => array('$exists' => true))); + if ($parent) { + $profile->setParent($this->getProfile($this->getData($parent))); + } + } + + $profile->setChildren($this->readChildren($data['token'])); + + return $profile; + } + + /** + * @param string $token + * + * @return Profile[] An array of Profile instances + */ + protected function readChildren($token) + { + $profiles = array(); + + $cursor = $this->getMongo()->find(array('parent' => $token, 'data' => array('$exists' => true))); + foreach ($cursor as $d) { + $profiles[] = $this->getProfile($this->getData($d)); + } + + return $profiles; + } + + protected function cleanup() + { + $this->getMongo()->remove(array('time' => array('$lt' => time() - $this->lifetime))); + } + + /** + * @param string $ip + * @param string $url + * @param string $method + * @param int $start + * @param int $end + * + * @return array + */ + private function buildQuery($ip, $url, $method, $start, $end) + { + $query = array(); + + if (!empty($ip)) { + $query['ip'] = $ip; + } + + if (!empty($url)) { + $query['url'] = $url; + } + + if (!empty($method)) { + $query['method'] = $method; + } + + if (!empty($start) || !empty($end)) { + $query['time'] = array(); + } + + if (!empty($start)) { + $query['time']['$gte'] = $start; + } + + if (!empty($end)) { + $query['time']['$lte'] = $end; + } + + return $query; + } + + /** + * @param array $data + * + * @return array + */ + private function getData(array $data) + { + return array( + 'token' => $data['_id'], + 'parent' => isset($data['parent']) ? $data['parent'] : null, + 'ip' => isset($data['ip']) ? $data['ip'] : null, + 'method' => isset($data['method']) ? $data['method'] : null, + 'url' => isset($data['url']) ? $data['url'] : null, + 'time' => isset($data['time']) ? $data['time'] : null, + 'data' => isset($data['data']) ? $data['data'] : null, + ); + } + + /** + * @param array $data + * + * @return Profile + */ + private function getProfile(array $data) + { + $profile = new Profile($data['token']); + $profile->setIp($data['ip']); + $profile->setMethod($data['method']); + $profile->setUrl($data['url']); + $profile->setTime($data['time']); + $profile->setCollectors(unserialize(base64_decode($data['data']))); + + return $profile; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/MysqlProfilerStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/MysqlProfilerStorage.php new file mode 100644 index 0000000..0aee1b5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/MysqlProfilerStorage.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\HttpKernel\Profiler; + +/** + * A ProfilerStorage for Mysql + * + * @author Jan Schumann + */ +class MysqlProfilerStorage extends PdoProfilerStorage +{ + /** + * {@inheritdoc} + */ + protected function initDb() + { + if (null === $this->db) { + if (0 !== strpos($this->dsn, 'mysql')) { + throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Mysql with an invalid dsn "%s". The expected format is "mysql:dbname=database_name;host=host_name".', $this->dsn)); + } + + if (!class_exists('PDO') || !in_array('mysql', \PDO::getAvailableDrivers(), true)) { + throw new \RuntimeException('You need to enable PDO_Mysql extension for the profiler to run properly.'); + } + + $db = new \PDO($this->dsn, $this->username, $this->password); + $db->exec('CREATE TABLE IF NOT EXISTS sf_profiler_data (token VARCHAR(255) PRIMARY KEY, data LONGTEXT, ip VARCHAR(64), method VARCHAR(6), url VARCHAR(255), time INTEGER UNSIGNED, parent VARCHAR(255), created_at INTEGER UNSIGNED, KEY (created_at), KEY (ip), KEY (method), KEY (url), KEY (parent))'); + + $this->db = $db; + } + + return $this->db; + } + + /** + * {@inheritdoc} + */ + protected function buildCriteria($ip, $url, $start, $end, $limit, $method) + { + $criteria = array(); + $args = array(); + + if ($ip = preg_replace('/[^\d\.]/', '', $ip)) { + $criteria[] = 'ip LIKE :ip'; + $args[':ip'] = '%'.$ip.'%'; + } + + if ($url) { + $criteria[] = 'url LIKE :url'; + $args[':url'] = '%'.addcslashes($url, '%_\\').'%'; + } + + if ($method) { + $criteria[] = 'method = :method'; + $args[':method'] = $method; + } + + if (!empty($start)) { + $criteria[] = 'time >= :start'; + $args[':start'] = $start; + } + + if (!empty($end)) { + $criteria[] = 'time <= :end'; + $args[':end'] = $end; + } + + return array($criteria, $args); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/PdoProfilerStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/PdoProfilerStorage.php new file mode 100644 index 0000000..3f9e03d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/PdoProfilerStorage.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; + +/** + * Base PDO storage for profiling information in a PDO database. + * + * @author Fabien Potencier + * @author Jan Schumann + */ +abstract class PdoProfilerStorage implements ProfilerStorageInterface +{ + protected $dsn; + protected $username; + protected $password; + protected $lifetime; + protected $db; + + /** + * Constructor. + * + * @param string $dsn A data source name + * @param string $username The username for the database + * @param string $password The password for the database + * @param integer $lifetime The lifetime to use for the purge + */ + public function __construct($dsn, $username = '', $password = '', $lifetime = 86400) + { + $this->dsn = $dsn; + $this->username = $username; + $this->password = $password; + $this->lifetime = (int) $lifetime; + } + + /** + * {@inheritdoc} + */ + public function find($ip, $url, $limit, $method, $start = null, $end = null) + { + if (null === $start) { + $start = 0; + } + + if (null === $end) { + $end = time(); + } + + list($criteria, $args) = $this->buildCriteria($ip, $url, $start, $end, $limit, $method); + + $criteria = $criteria ? 'WHERE '.implode(' AND ', $criteria) : ''; + + $db = $this->initDb(); + $tokens = $this->fetch($db, 'SELECT token, ip, method, url, time, parent FROM sf_profiler_data '.$criteria.' ORDER BY time DESC LIMIT '.((integer) $limit), $args); + $this->close($db); + + return $tokens; + } + + /** + * {@inheritdoc} + */ + public function read($token) + { + $db = $this->initDb(); + $args = array(':token' => $token); + $data = $this->fetch($db, 'SELECT data, parent, ip, method, url, time FROM sf_profiler_data WHERE token = :token LIMIT 1', $args); + $this->close($db); + if (isset($data[0]['data'])) { + return $this->createProfileFromData($token, $data[0]); + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function write(Profile $profile) + { + $db = $this->initDb(); + $args = array( + ':token' => $profile->getToken(), + ':parent' => $profile->getParentToken(), + ':data' => base64_encode(serialize($profile->getCollectors())), + ':ip' => $profile->getIp(), + ':method' => $profile->getMethod(), + ':url' => $profile->getUrl(), + ':time' => $profile->getTime(), + ':created_at' => time(), + ); + + try { + if ($this->has($profile->getToken())) { + $this->exec($db, 'UPDATE sf_profiler_data SET parent = :parent, data = :data, ip = :ip, method = :method, url = :url, time = :time, created_at = :created_at WHERE token = :token', $args); + } else { + $this->exec($db, 'INSERT INTO sf_profiler_data (token, parent, data, ip, method, url, time, created_at) VALUES (:token, :parent, :data, :ip, :method, :url, :time, :created_at)', $args); + } + $this->cleanup(); + $status = true; + } catch (\Exception $e) { + $status = false; + } + + $this->close($db); + + return $status; + } + + /** + * {@inheritdoc} + */ + public function purge() + { + $db = $this->initDb(); + $this->exec($db, 'DELETE FROM sf_profiler_data'); + $this->close($db); + } + + /** + * Build SQL criteria to fetch records by ip and url + * + * @param string $ip The IP + * @param string $url The URL + * @param string $start The start period to search from + * @param string $end The end period to search to + * @param string $limit The maximum number of tokens to return + * @param string $method The request method + * + * @return array An array with (criteria, args) + */ + abstract protected function buildCriteria($ip, $url, $start, $end, $limit, $method); + + /** + * Initializes the database + * + * @throws \RuntimeException When the requested database driver is not installed + */ + abstract protected function initDb(); + + protected function cleanup() + { + $db = $this->initDb(); + $this->exec($db, 'DELETE FROM sf_profiler_data WHERE created_at < :time', array(':time' => time() - $this->lifetime)); + $this->close($db); + } + + protected function exec($db, $query, array $args = array()) + { + $stmt = $this->prepareStatement($db, $query); + + foreach ($args as $arg => $val) { + $stmt->bindValue($arg, $val, is_int($val) ? \PDO::PARAM_INT : \PDO::PARAM_STR); + } + $success = $stmt->execute(); + if (!$success) { + throw new \RuntimeException(sprintf('Error executing query "%s"', $query)); + } + } + + protected function prepareStatement($db, $query) + { + try { + $stmt = $db->prepare($query); + } catch (\Exception $e) { + $stmt = false; + } + + if (false === $stmt) { + throw new \RuntimeException('The database cannot successfully prepare the statement'); + } + + return $stmt; + } + + protected function fetch($db, $query, array $args = array()) + { + $stmt = $this->prepareStatement($db, $query); + + foreach ($args as $arg => $val) { + $stmt->bindValue($arg, $val, is_int($val) ? \PDO::PARAM_INT : \PDO::PARAM_STR); + } + $stmt->execute(); + $return = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + return $return; + } + + protected function close($db) + { + } + + 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(unserialize(base64_decode($data['data']))); + + if (!$parent && !empty($data['parent'])) { + $parent = $this->read($data['parent']); + } + + if ($parent) { + $profile->setParent($parent); + } + + $profile->setChildren($this->readChildren($token, $profile)); + + return $profile; + } + + /** + * Reads the child profiles for the given token. + * + * @param string $token The parent token + * @param string $parent The parent instance + * + * @return Profile[] An array of Profile instance + */ + protected function readChildren($token, $parent) + { + $db = $this->initDb(); + $data = $this->fetch($db, 'SELECT token, data, ip, method, url, time FROM sf_profiler_data WHERE parent = :token', array(':token' => $token)); + $this->close($db); + + if (!$data) { + return array(); + } + + $profiles = array(); + foreach ($data as $d) { + $profiles[] = $this->createProfileFromData($d['token'], $d, $parent); + } + + return $profiles; + } + + /** + * Returns whether data for the given token already exists in storage. + * + * @param string $token The profile token + * + * @return string + */ + protected function has($token) + { + $db = $this->initDb(); + $tokenExists = $this->fetch($db, 'SELECT 1 FROM sf_profiler_data WHERE token = :token LIMIT 1', array(':token' => $token)); + $this->close($db); + + return !empty($tokenExists); + } +} 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..b3fa551 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/Profile.php @@ -0,0 +1,275 @@ + + * + * 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; + + /** + * @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; + } + + /** + * 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 Boolean + */ + 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..49f3137 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/Profiler.php @@ -0,0 +1,287 @@ + + * + * 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\Profiler\ProfilerStorageInterface; +use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; +use Psr\Log\LoggerInterface; + +/** + * Profiler. + * + * @author Fabien Potencier + */ +class Profiler +{ + /** + * @var ProfilerStorageInterface + */ + private $storage; + + /** + * @var DataCollectorInterface[] + */ + private $collectors = array(); + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var Boolean + */ + 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 Boolean + */ + public function saveProfile(Profile $profile) + { + if (!($ret = $this->storage->write($profile)) && null !== $this->logger) { + $this->logger->warning('Unable to store the profiler information.'); + } + + return $ret; + } + + /** + * Purges all data from the storage. + */ + public function purge() + { + $this->storage->purge(); + } + + /** + * Exports the current profiler data. + * + * @param Profile $profile A Profile instance + * + * @return string The exported data + */ + public function export(Profile $profile) + { + return base64_encode(serialize($profile)); + } + + /** + * Imports data into the profiler storage. + * + * @param string $data A data string as exported by the export() method + * + * @return Profile A Profile instance + */ + public function import($data) + { + $profile = unserialize(base64_decode($data)); + + if ($this->storage->read($profile->getToken())) { + return false; + } + + $this->saveProfile($profile); + + return $profile; + } + + /** + * 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://fr2.php.net/manual/en/datetime.formats.php for the supported date/time formats + */ + public function find($ip, $url, $limit, $method, $start, $end) + { + if ('' != $start && null !== $start) { + $start = new \DateTime($start); + $start = $start->getTimestamp(); + } else { + $start = null; + } + + if ('' != $end && null !== $end) { + $end = new \DateTime($end); + $end = $end->getTimestamp(); + } else { + $end = null; + } + + return $this->storage->find($ip, $url, $limit, $method, $start, $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(sha1(uniqid(mt_rand(), true)), 0, 6)); + $profile->setTime(time()); + $profile->setUrl($request->getUri()); + $profile->setIp($request->getClientIp()); + $profile->setMethod($request->getMethod()); + + $response->headers->set('X-Debug-Token', $profile->getToken()); + + foreach ($this->collectors as $collector) { + $collector->collect($request, $response, $exception); + + // forces collectors to become "read/only" (they loose their object dependencies) + $profile->addCollector(unserialize(serialize($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 Boolean + */ + 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]; + } +} 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..3bb0d68 --- /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 exists 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 Boolean 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/Profiler/RedisProfilerStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/RedisProfilerStorage.php new file mode 100644 index 0000000..d62d6c7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/RedisProfilerStorage.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\HttpKernel\Profiler; + +use Redis; + +/** + * RedisProfilerStorage stores profiling information in Redis. + * + * @author Andrej Hudec + * @author Stephane PY + */ +class RedisProfilerStorage implements ProfilerStorageInterface +{ + const TOKEN_PREFIX = 'sf_profiler_'; + + const REDIS_OPT_SERIALIZER = 1; + const REDIS_OPT_PREFIX = 2; + const REDIS_SERIALIZER_NONE = 0; + const REDIS_SERIALIZER_PHP = 1; + + protected $dsn; + protected $lifetime; + + /** + * @var Redis + */ + private $redis; + + /** + * Constructor. + * + * @param string $dsn A data source name + * @param string $username Not used + * @param string $password Not used + * @param int $lifetime The lifetime to use for the purge + */ + public function __construct($dsn, $username = '', $password = '', $lifetime = 86400) + { + $this->dsn = $dsn; + $this->lifetime = (int) $lifetime; + } + + /** + * {@inheritdoc} + */ + public function find($ip, $url, $limit, $method, $start = null, $end = null) + { + $indexName = $this->getIndexName(); + + if (!$indexContent = $this->getValue($indexName, self::REDIS_SERIALIZER_NONE)) { + return array(); + } + + $profileList = array_reverse(explode("\n", $indexContent)); + $result = array(); + + foreach ($profileList as $item) { + if ($limit === 0) { + break; + } + + if ($item == '') { + continue; + } + + list($itemToken, $itemIp, $itemMethod, $itemUrl, $itemTime, $itemParent) = explode("\t", $item, 6); + + $itemTime = (int) $itemTime; + + if ($ip && false === strpos($itemIp, $ip) || $url && false === strpos($itemUrl, $url) || $method && false === strpos($itemMethod, $method)) { + continue; + } + + if (!empty($start) && $itemTime < $start) { + continue; + } + + if (!empty($end) && $itemTime > $end) { + continue; + } + + $result[] = array( + 'token' => $itemToken, + 'ip' => $itemIp, + 'method' => $itemMethod, + 'url' => $itemUrl, + 'time' => $itemTime, + 'parent' => $itemParent, + ); + --$limit; + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function purge() + { + // delete only items from index + $indexName = $this->getIndexName(); + + $indexContent = $this->getValue($indexName, self::REDIS_SERIALIZER_NONE); + + if (!$indexContent) { + return false; + } + + $profileList = explode("\n", $indexContent); + + $result = array(); + + foreach ($profileList as $item) { + if ($item == '') { + continue; + } + + if (false !== $pos = strpos($item, "\t")) { + $result[] = $this->getItemName(substr($item, 0, $pos)); + } + } + + $result[] = $indexName; + + return $this->delete($result); + } + + /** + * {@inheritdoc} + */ + public function read($token) + { + if (empty($token)) { + return false; + } + + $profile = $this->getValue($this->getItemName($token), self::REDIS_SERIALIZER_PHP); + + if (false !== $profile) { + $profile = $this->createProfileFromData($token, $profile); + } + + return $profile; + } + + /** + * {@inheritdoc} + */ + public function write(Profile $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(), + ); + + $profileIndexed = false !== $this->getValue($this->getItemName($profile->getToken())); + + if ($this->setValue($this->getItemName($profile->getToken()), $data, $this->lifetime, self::REDIS_SERIALIZER_PHP)) { + + if (!$profileIndexed) { + // Add to index + $indexName = $this->getIndexName(); + + $indexRow = implode("\t", array( + $profile->getToken(), + $profile->getIp(), + $profile->getMethod(), + $profile->getUrl(), + $profile->getTime(), + $profile->getParentToken(), + ))."\n"; + + return $this->appendValue($indexName, $indexRow, $this->lifetime); + } + + return true; + } + + return false; + } + + /** + * Internal convenience method that returns the instance of Redis. + * + * @return Redis + * + * @throws \RuntimeException + */ + protected function getRedis() + { + if (null === $this->redis) { + $data = parse_url($this->dsn); + + if (false === $data || !isset($data['scheme']) || $data['scheme'] !== 'redis' || !isset($data['host']) || !isset($data['port'])) { + throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Redis with an invalid dsn "%s". The minimal expected format is "redis://[host]:port".', $this->dsn)); + } + + if (!extension_loaded('redis')) { + throw new \RuntimeException('RedisProfilerStorage requires that the redis extension is loaded.'); + } + + $redis = new Redis; + $redis->connect($data['host'], $data['port']); + + if (isset($data['path'])) { + $redis->select(substr($data['path'], 1)); + } + + if (isset($data['pass'])) { + $redis->auth($data['pass']); + } + + $redis->setOption(self::REDIS_OPT_PREFIX, self::TOKEN_PREFIX); + + $this->redis = $redis; + } + + return $this->redis; + } + + /** + * Set instance of the Redis + * + * @param Redis $redis + */ + public function setRedis($redis) + { + $this->redis = $redis; + } + + private 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) { + continue; + } + + if (!$childProfileData = $this->getValue($this->getItemName($token), self::REDIS_SERIALIZER_PHP)) { + continue; + } + + $profile->addChild($this->createProfileFromData($token, $childProfileData, $profile)); + } + + return $profile; + } + + /** + * Gets the item name. + * + * @param string $token + * + * @return string + */ + private function getItemName($token) + { + $name = $token; + + if ($this->isItemNameValid($name)) { + return $name; + } + + return false; + } + + /** + * Gets the name of the index. + * + * @return string + */ + private function getIndexName() + { + $name = 'index'; + + if ($this->isItemNameValid($name)) { + return $name; + } + + return false; + } + + private function isItemNameValid($name) + { + $length = strlen($name); + + if ($length > 2147483648) { + throw new \RuntimeException(sprintf('The Redis item key "%s" is too long (%s bytes). Allowed maximum size is 2^31 bytes.', $name, $length)); + } + + return true; + } + + /** + * Retrieves an item from the Redis server. + * + * @param string $key + * @param int $serializer + * + * @return mixed + */ + private function getValue($key, $serializer = self::REDIS_SERIALIZER_NONE) + { + $redis = $this->getRedis(); + $redis->setOption(self::REDIS_OPT_SERIALIZER, $serializer); + + return $redis->get($key); + } + + /** + * Stores an item on the Redis server under the specified key. + * + * @param string $key + * @param mixed $value + * @param int $expiration + * @param int $serializer + * + * @return Boolean + */ + private function setValue($key, $value, $expiration = 0, $serializer = self::REDIS_SERIALIZER_NONE) + { + $redis = $this->getRedis(); + $redis->setOption(self::REDIS_OPT_SERIALIZER, $serializer); + + return $redis->setex($key, $expiration, $value); + } + + /** + * Appends data to an existing item on the Redis server. + * + * @param string $key + * @param string $value + * @param int $expiration + * + * @return Boolean + */ + private function appendValue($key, $value, $expiration = 0) + { + $redis = $this->getRedis(); + $redis->setOption(self::REDIS_OPT_SERIALIZER, self::REDIS_SERIALIZER_NONE); + + if ($redis->exists($key)) { + $redis->append($key, $value); + + return $redis->setTimeout($key, $expiration); + } + + return $redis->setex($key, $expiration, $value); + } + + /** + * Removes the specified keys. + * + * @param array $keys + * + * @return Boolean + */ + private function delete(array $keys) + { + return (bool) $this->getRedis()->delete($keys); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/SqliteProfilerStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/SqliteProfilerStorage.php new file mode 100644 index 0000000..0c25bc9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/SqliteProfilerStorage.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\HttpKernel\Profiler; + +/** + * SqliteProfilerStorage stores profiling information in a SQLite database. + * + * @author Fabien Potencier + */ +class SqliteProfilerStorage extends PdoProfilerStorage +{ + /** + * @throws \RuntimeException When neither of SQLite3 or PDO_SQLite extension is enabled + */ + protected function initDb() + { + if (null === $this->db || $this->db instanceof \SQLite3) { + if (0 !== strpos($this->dsn, 'sqlite')) { + throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Sqlite with an invalid dsn "%s". The expected format is "sqlite:/path/to/the/db/file".', $this->dsn)); + } + if (class_exists('SQLite3')) { + $db = new \SQLite3(substr($this->dsn, 7, strlen($this->dsn)), \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE); + if (method_exists($db, 'busyTimeout')) { + // busyTimeout only exists for PHP >= 5.3.3 + $db->busyTimeout(1000); + } + } elseif (class_exists('PDO') && in_array('sqlite', \PDO::getAvailableDrivers(), true)) { + $db = new \PDO($this->dsn); + } else { + throw new \RuntimeException('You need to enable either the SQLite3 or PDO_SQLite extension for the profiler to run properly.'); + } + + $db->exec('PRAGMA temp_store=MEMORY; PRAGMA journal_mode=MEMORY;'); + $db->exec('CREATE TABLE IF NOT EXISTS sf_profiler_data (token STRING, data STRING, ip STRING, method STRING, url STRING, time INTEGER, parent STRING, created_at INTEGER)'); + $db->exec('CREATE INDEX IF NOT EXISTS data_created_at ON sf_profiler_data (created_at)'); + $db->exec('CREATE INDEX IF NOT EXISTS data_ip ON sf_profiler_data (ip)'); + $db->exec('CREATE INDEX IF NOT EXISTS data_method ON sf_profiler_data (method)'); + $db->exec('CREATE INDEX IF NOT EXISTS data_url ON sf_profiler_data (url)'); + $db->exec('CREATE INDEX IF NOT EXISTS data_parent ON sf_profiler_data (parent)'); + $db->exec('CREATE UNIQUE INDEX IF NOT EXISTS data_token ON sf_profiler_data (token)'); + + $this->db = $db; + } + + return $this->db; + } + + protected function exec($db, $query, array $args = array()) + { + if ($db instanceof \SQLite3) { + $stmt = $this->prepareStatement($db, $query); + foreach ($args as $arg => $val) { + $stmt->bindValue($arg, $val, is_int($val) ? \SQLITE3_INTEGER : \SQLITE3_TEXT); + } + + $res = $stmt->execute(); + if (false === $res) { + throw new \RuntimeException(sprintf('Error executing SQLite query "%s"', $query)); + } + $res->finalize(); + } else { + parent::exec($db, $query, $args); + } + } + + protected function fetch($db, $query, array $args = array()) + { + $return = array(); + + if ($db instanceof \SQLite3) { + $stmt = $this->prepareStatement($db, $query, true); + foreach ($args as $arg => $val) { + $stmt->bindValue($arg, $val, is_int($val) ? \SQLITE3_INTEGER : \SQLITE3_TEXT); + } + $res = $stmt->execute(); + while ($row = $res->fetchArray(\SQLITE3_ASSOC)) { + $return[] = $row; + } + $res->finalize(); + $stmt->close(); + } else { + $return = parent::fetch($db, $query, $args); + } + + return $return; + } + + /** + * {@inheritdoc} + */ + protected function buildCriteria($ip, $url, $start, $end, $limit, $method) + { + $criteria = array(); + $args = array(); + + if ($ip = preg_replace('/[^\d\.]/', '', $ip)) { + $criteria[] = 'ip LIKE :ip'; + $args[':ip'] = '%'.$ip.'%'; + } + + if ($url) { + $criteria[] = 'url LIKE :url ESCAPE "\"'; + $args[':url'] = '%'.addcslashes($url, '%_\\').'%'; + } + + if ($method) { + $criteria[] = 'method = :method'; + $args[':method'] = $method; + } + + if (!empty($start)) { + $criteria[] = 'time >= :start'; + $args[':start'] = $start; + } + + if (!empty($end)) { + $criteria[] = 'time <= :end'; + $args[':end'] = $end; + } + + return array($criteria, $args); + } + + protected function close($db) + { + if ($db instanceof \SQLite3) { + $db->close(); + } + } +} 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..e0f3c98 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/README.md @@ -0,0 +1,89 @@ +HttpKernel Component +==================== + +HttpKernel provides the building blocks to create flexible and fast HTTP-based +frameworks. + +``HttpKernelInterface`` is the core interface of the Symfony2 full-stack +framework: + + 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 Symfony2 components. And this will give you many cool features for +free. + +Creating a framework based on the Symfony2 components is really easy. Here is +a very simple, but fully-featured framework based on the Symfony2 components: + + $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 Symfony2 +components. + +Want to add an HTTP reverse proxy and benefit from HTTP caching and Edge Side +Includes? + + $kernel = new HttpKernel($dispatcher, $resolver); + + $kernel = new HttpCache($kernel, new Store(__DIR__.'/cache')); + +Want to functional test this small framework? + + $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? + + $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.phar install --dev + $ 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..fc0e580 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/TerminableInterface.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; + +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 + * + * @api + */ +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 + * + * @api + */ + 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..8affb5f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.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\HttpKernel\Tests\Bundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +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 testRegisterCommands() + { + if (!class_exists('Symfony\Component\Console\Application')) { + $this->markTestSkipped('The "Console" component is not available'); + } + + if (!interface_exists('Symfony\Component\DependencyInjection\ContainerAwareInterface')) { + $this->markTestSkipped('The "DependencyInjection" component is not available'); + } + + if (!class_exists('Symfony\Component\Finder\Finder')) { + $this->markTestSkipped('The "Finder" component is not available'); + } + + $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)); + + } +} 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..c4816ba --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/CacheClearer/ChainCacheClearerTest.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\CacheClearer; + +use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface; +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..24e7fd9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/CacheWarmer/CacheWarmerAggregateTest.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\Tests\CacheWarmer; + +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer; + +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..755d7f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/ClientTest.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\HttpKernel\Tests; + +use Symfony\Component\HttpKernel\Client; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpFoundation\Request; +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 +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\BrowserKit\Client')) { + $this->markTestSkipped('The "BrowserKit" component is not available'); + } + } + + 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() + { + if (!class_exists('Symfony\Component\Process\Process')) { + $this->markTestSkipped('The "Process" component is not available'); + } + + if (!class_exists('Symfony\Component\ClassLoader\ClassLoader')) { + $this->markTestSkipped('The "ClassLoader" component is not available'); + } + + $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), + ); + + 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 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/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..aa401f4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.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\HttpKernel\Tests; + +use Symfony\Component\HttpKernel\Controller\ControllerResolver; +use Symfony\Component\HttpFoundation\Request; + +class ControllerResolverTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testGetController() + { + $logger = new Logger(); + $resolver = new ControllerResolver($logger); + + $request = Request::create('/'); + $this->assertFalse($resolver->getController($request), '->getController() returns false when the request has no _controller attribute'); + $this->assertEquals(array('Unable to look for the controller as the "_controller" parameter is missing'), $logger->getLogs('warning')); + + $request->attributes->set('_controller', 'Symfony\Component\HttpKernel\Tests\ControllerResolverTest::testGetController'); + $controller = $resolver->getController($request); + $this->assertInstanceOf('Symfony\Component\HttpKernel\Tests\ControllerResolverTest', $controller[0], '->getController() returns a PHP callable'); + + $request->attributes->set('_controller', $lambda = function () {}); + $controller = $resolver->getController($request); + $this->assertSame($lambda, $controller); + + $request->attributes->set('_controller', $this); + $controller = $resolver->getController($request); + $this->assertSame($this, $controller); + + $request->attributes->set('_controller', 'Symfony\Component\HttpKernel\Tests\ControllerResolverTest'); + $controller = $resolver->getController($request); + $this->assertInstanceOf('Symfony\Component\HttpKernel\Tests\ControllerResolverTest', $controller); + + $request->attributes->set('_controller', array($this, 'controllerMethod1')); + $controller = $resolver->getController($request); + $this->assertSame(array($this, 'controllerMethod1'), $controller); + + $request->attributes->set('_controller', array('Symfony\Component\HttpKernel\Tests\ControllerResolverTest', 'controllerMethod4')); + $controller = $resolver->getController($request); + $this->assertSame(array('Symfony\Component\HttpKernel\Tests\ControllerResolverTest', 'controllerMethod4'), $controller); + + $request->attributes->set('_controller', 'Symfony\Component\HttpKernel\Tests\some_controller_function'); + $controller = $resolver->getController($request); + $this->assertSame('Symfony\Component\HttpKernel\Tests\some_controller_function', $controller); + + $request->attributes->set('_controller', 'foo'); + try { + $resolver->getController($request); + $this->fail('->getController() throws an \InvalidArgumentException if the _controller attribute is not well-formatted'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->getController() throws an \InvalidArgumentException if the _controller attribute is not well-formatted'); + } + + $request->attributes->set('_controller', 'foo::bar'); + try { + $resolver->getController($request); + $this->fail('->getController() throws an \InvalidArgumentException if the _controller attribute contains a non-existent class'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->getController() throws an \InvalidArgumentException if the _controller attribute contains a non-existent class'); + } + + $request->attributes->set('_controller', 'Symfony\Component\HttpKernel\Tests\ControllerResolverTest::bar'); + try { + $resolver->getController($request); + $this->fail('->getController() throws an \InvalidArgumentException if the _controller attribute contains a non-existent method'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->getController() throws an \InvalidArgumentException if the _controller attribute contains a non-existent method'); + } + } + + public function testGetArguments() + { + $resolver = new ControllerResolver(); + + $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\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'); + + if (version_compare(PHP_VERSION, '5.3.16', '==')) { + $this->markTestSkipped('PHP 5.3.16 has a major bug in the Reflection sub-system'); + } else { + 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\some_controller_function')); + + $request = Request::create('/'); + $request->attributes->set('_controller', 'foobar'); + $mock->getController($request); + } + + public function __invoke($foo, $bar = null) + { + } + + protected function controllerMethod1($foo) + { + } + + protected function controllerMethod2($foo, $bar = null) + { + } + + protected function controllerMethod3($foo, $bar = null, $foobar) + { + } + + protected static function controllerMethod4() + { + } + + protected function controllerMethod5(Request $request) + { + } +} + +function some_controller_function($foo, $foobar) +{ +} 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..192c808 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.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\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 +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testCollect() + { + $kernel = new KernelForTest('test', true); + $c = new ConfigDataCollector(); + $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 init() + { + } + + public function getBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } +} 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..54a8aab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/ExceptionDataCollectorTest.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\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector; +use Symfony\Component\HttpKernel\Exception\FlattenException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class ExceptionDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + 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..ea82d9d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.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\Tests\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector; +use Symfony\Component\HttpKernel\Debug\ErrorHandler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + /** + * @dataProvider getCollectTestData + */ + public function testCollect($nb, $logs, $expectedLogs, $expectedDeprecationCount) + { + $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->collect(new Request(), new Response()); + + $this->assertSame('logger', $c->getName()); + $this->assertSame($nb, $c->countErrors()); + $this->assertSame($expectedLogs ? $expectedLogs : $logs, $c->getLogs()); + $this->assertSame($expectedDeprecationCount, $c->countDeprecations()); + } + + public function getCollectTestData() + { + return array( + array( + 1, + array(array('message' => 'foo', 'context' => array())), + null, + 0 + ), + array( + 1, + array(array('message' => 'foo', 'context' => array('foo' => fopen(__FILE__, 'r')))), + array(array('message' => 'foo', 'context' => array('foo' => 'Resource(stream)'))), + 0 + ), + array( + 1, + array(array('message' => 'foo', 'context' => array('foo' => new \stdClass()))), + array(array('message' => 'foo', 'context' => array('foo' => 'Object(stdClass)'))), + 0 + ), + array( + 1, + array( + array('message' => 'foo', 'context' => array('type' => ErrorHandler::TYPE_DEPRECATION)), + array('message' => 'foo2', 'context' => array('type' => ErrorHandler::TYPE_DEPRECATION)) + ), + null, + 2 + ), + ); + } +} 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..607a580 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/MemoryDataCollectorTest.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\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 +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + 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('-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..8c14604 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php @@ -0,0 +1,208 @@ + + * + * 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 +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + /** + * @dataProvider provider + */ + public function testCollect(Request $request, Response $response) + { + $c = new RequestDataCollector(); + + $c->collect($request, $response); + + $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',$c->getRequestAttributes()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag',$c->getRequestRequest()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag',$c->getRequestQuery()); + $this->assertEquals('html',$c->getFormat()); + $this->assertEquals(array(),$c->getSessionAttributes()); + $this->assertEquals('en',$c->getLocale()); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\HeaderBag',$c->getResponseHeaders()); + $this->assertEquals('OK',$c->getStatusText()); + $this->assertEquals(200,$c->getStatusCode()); + $this->assertEquals('application/json',$c->getContentType()); + } + + /** + * Test various types of controller callables. + * + * @dataProvider provider + */ + public function testControllerInspection(Request $request, Response $response) + { + // make sure we always match the line number + $r1 = new \ReflectionMethod($this, 'testControllerInspection'); + $r2 = new \ReflectionMethod($this, 'staticControllerMethod'); + // 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' + ), + ), + ); + + $c = new RequestDataCollector(); + + foreach ($controllerTests as $controllerTest) { + $this->injectController($c, $controllerTest[1], $request); + $c->collect($request, $response); + $this->assertEquals($controllerTest[2], $c->getController(), sprintf('Testing: %s', $controllerTest[0])); + } + } + + public function provider() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + return array(array(null, null)); + } + + $request = Request::create('http://test.com/foo?bar=baz'); + $request->attributes->set('foo', 'bar'); + + $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 array( + array($request, $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'); + } + +} 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..13abb67 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/TimeDataCollectorTest.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\TimeDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class TimeDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + 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/Debug/TraceableEventDispatcherTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php new file mode 100644 index 0000000..c6efa91 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php @@ -0,0 +1,241 @@ + + * + * 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\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Stopwatch\Stopwatch; + +class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testAddRemoveListener() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $tdispatcher->addListener('foo', $listener = function () { ; }); + $listeners = $dispatcher->getListeners('foo'); + $this->assertCount(1, $listeners); + $this->assertSame($listener, $listeners[0]); + + $tdispatcher->removeListener('foo', $listener); + $this->assertCount(0, $dispatcher->getListeners('foo')); + } + + public function testGetListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $tdispatcher->addListener('foo', $listener = function () { ; }); + $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo')); + } + + public function testHasListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $this->assertFalse($dispatcher->hasListeners('foo')); + $this->assertFalse($tdispatcher->hasListeners('foo')); + + $tdispatcher->addListener('foo', $listener = function () { ; }); + $this->assertTrue($dispatcher->hasListeners('foo')); + $this->assertTrue($tdispatcher->hasListeners('foo')); + } + + public function testAddRemoveSubscriber() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $subscriber = new EventSubscriber(); + + $tdispatcher->addSubscriber($subscriber); + $listeners = $dispatcher->getListeners('foo'); + $this->assertCount(1, $listeners); + $this->assertSame(array($subscriber, 'call'), $listeners[0]); + + $tdispatcher->removeSubscriber($subscriber); + $this->assertCount(0, $dispatcher->getListeners('foo')); + } + + public function testGetCalledListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + $tdispatcher->addListener('foo', $listener = function () { ; }); + + $this->assertEquals(array(), $tdispatcher->getCalledListeners()); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure')), $tdispatcher->getNotCalledListeners()); + + $tdispatcher->dispatch('foo'); + + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure')), $tdispatcher->getCalledListeners()); + $this->assertEquals(array(), $tdispatcher->getNotCalledListeners()); + } + + public function testLogger() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); + $tdispatcher->addListener('foo', $listener1 = function () { ; }); + $tdispatcher->addListener('foo', $listener2 = function () { ; }); + + $logger->expects($this->at(0))->method('debug')->with("Notified event \"foo\" to listener \"closure\"."); + $logger->expects($this->at(1))->method('debug')->with("Notified event \"foo\" to listener \"closure\"."); + + $tdispatcher->dispatch('foo'); + } + + public function testLoggerWithStoppedEvent() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); + $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); }); + $tdispatcher->addListener('foo', $listener2 = function () { ; }); + + $logger->expects($this->at(0))->method('debug')->with("Notified event \"foo\" to listener \"closure\"."); + $logger->expects($this->at(1))->method('debug')->with("Listener \"closure\" stopped propagation of the event \"foo\"."); + $logger->expects($this->at(2))->method('debug')->with("Listener \"closure\" was not called for event \"foo\"."); + + $tdispatcher->dispatch('foo'); + } + + public function testDispatchCallListeners() + { + $called = array(); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + $tdispatcher->addListener('foo', $listener1 = function () use (&$called) { $called[] = 'foo1'; }); + $tdispatcher->addListener('foo', $listener2 = function () use (&$called) { $called[] = 'foo2'; }); + + $tdispatcher->dispatch('foo'); + + $this->assertEquals(array('foo1', 'foo2'), $called); + } + + public function testDispatchNested() + { + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $loop = 1; + $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) { + ++$loop; + if (2 == $loop) { + $dispatcher->dispatch('foo'); + } + }); + + $dispatcher->dispatch('foo'); + } + + 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.request.loading', + 'kernel.controller', + 'kernel.controller.loading', + 'controller', + 'kernel.response', + 'kernel.response.loading', + 'kernel.terminate', + 'kernel.terminate.loading', + ), 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); + } + + 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); + } +} + +class EventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('foo' => 'call'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php new file mode 100644 index 0000000..61372cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.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\HttpKernel\Tests\DependencyInjection; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class ContainerAwareHttpKernelTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\DependencyInjection\Container')) { + $this->markTestSkipped('The "DependencyInjection" component is not available'); + } + + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + /** + * @dataProvider getProviderTypes + */ + public function testHandle($type) + { + $request = new Request(); + $expected = new Response(); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->once()) + ->method('enterScope') + ->with($this->equalTo('request')) + ; + $container + ->expects($this->once()) + ->method('leaveScope') + ->with($this->equalTo('request')) + ; + $container + ->expects($this->at(0)) + ->method('hasScope') + ->with($this->equalTo('request')) + ->will($this->returnValue(false)); + $container + ->expects($this->at(1)) + ->method('addScope') + ->with($this->isInstanceOf('Symfony\Component\DependencyInjection\Scope')); + // enterScope() + $container + ->expects($this->at(3)) + ->method('set') + ->with($this->equalTo('request'), $this->equalTo($request), $this->equalTo('request')) + ; + $container + ->expects($this->at(4)) + ->method('set') + ->with($this->equalTo('request'), $this->equalTo(null), $this->equalTo('request')) + ; + + $dispatcher = new EventDispatcher(); + $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); + $kernel = new ContainerAwareHttpKernel($dispatcher, $container, $resolver); + + $controller = function() use ($expected) { + return $expected; + }; + + $resolver->expects($this->once()) + ->method('getController') + ->with($request) + ->will($this->returnValue($controller)); + $resolver->expects($this->once()) + ->method('getArguments') + ->with($request, $controller) + ->will($this->returnValue(array())); + + $actual = $kernel->handle($request, $type); + + $this->assertSame($expected, $actual, '->handle() returns the response'); + } + + /** + * @dataProvider getProviderTypes + */ + public function testHandleRestoresThePreviousRequestOnException($type) + { + $request = new Request(); + $expected = new \Exception(); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->once()) + ->method('enterScope') + ->with($this->equalTo('request')) + ; + $container + ->expects($this->once()) + ->method('leaveScope') + ->with($this->equalTo('request')) + ; + $container + ->expects($this->at(0)) + ->method('hasScope') + ->with($this->equalTo('request')) + ->will($this->returnValue(true)); + // enterScope() + $container + ->expects($this->at(2)) + ->method('set') + ->with($this->equalTo('request'), $this->equalTo($request), $this->equalTo('request')) + ; + $container + ->expects($this->at(3)) + ->method('set') + ->with($this->equalTo('request'), $this->equalTo(null), $this->equalTo('request')) + ; + + $dispatcher = new EventDispatcher(); + $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); + $kernel = new ContainerAwareHttpKernel($dispatcher, $container, $resolver); + + $controller = function() use ($expected) { + throw $expected; + }; + + $resolver->expects($this->once()) + ->method('getController') + ->with($request) + ->will($this->returnValue($controller)); + $resolver->expects($this->once()) + ->method('getArguments') + ->with($request, $controller) + ->will($this->returnValue(array())); + + try { + $kernel->handle($request, $type); + $this->fail('->handle() suppresses the controller exception'); + } catch (\PHPUnit_Framework_Exception $exception) { + throw $exception; + } catch (\Exception $actual) { + $this->assertSame($expected, $actual, '->handle() throws the controller exception'); + } + } + + public function getProviderTypes() + { + return array( + array(HttpKernelInterface::MASTER_REQUEST), + array(HttpKernelInterface::SUB_REQUEST), + ); + } +} 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..0800758 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.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\Tests; + +use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; + +class MergeExtensionConfigurationPassTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\DependencyInjection\Container')) { + $this->markTestSkipped('The "DependencyInjection" component is not available'); + } + + if (!class_exists('Symfony\Component\Config\FileLocator')) { + $this->markTestSkipped('The "Config" component is not available'); + } + } + + public function testAutoloadMainExtension() + { + $container = $this->getMock('Symfony\\Component\\DependencyInjection\\ContainerBuilder'); + $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/DependencyInjection/RegisterListenersPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterListenersPassTest.php new file mode 100644 index 0000000..d1e825a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterListenersPassTest.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\HttpKernel\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass; + +class RegisterListenersPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * Tests that event subscribers not implementing EventSubscriberInterface + * trigger an exception. + * + * @expectedException \InvalidArgumentException + */ + public function testEventSubscriberWithoutInterface() + { + // one service, not implementing any interface + $services = array( + 'my_event_subscriber' => array(0 => array()), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('stdClass')); + + $builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.event_listener here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->onConsecutiveCalls(array(), $services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + } + + public function testValidEventSubscriber() + { + $services = array( + 'my_event_subscriber' => array(0 => array()), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('Symfony\Component\HttpKernel\Tests\DependencyInjection\SubscriberService')); + + $builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.event_listener here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->onConsecutiveCalls(array(), $services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + } +} + +class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface +{ + public static function getSubscribedEvents() {} +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/EsiListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/EsiListenerTest.php new file mode 100644 index 0000000..2eddc57 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/EsiListenerTest.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\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpKernel\EventListener\EsiListener; +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 EsiListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + } + + public function testFilterDoesNothingForSubRequests() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $response = new Response('foo '); + $listener = new EsiListener(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 EsiListener(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 EsiListener(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/ExceptionListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php new file mode 100644 index 0000000..41b3dec --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.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\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 + */ +class ExceptionListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + 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) + { + // store the current error_log, and disable it temporarily + $errorLog = ini_set('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); + } catch (\Exception $e) { + $this->assertSame('foo', $e->getMessage()); + } + + // restore the old error_log + ini_set('error_log', $errorLog); + } + + /** + * @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); + } catch (\Exception $e) { + $this->assertSame('foo', $e->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) + ); + } +} + +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 \Exception('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..153d8a4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/FragmentListenerTest.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\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 +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + } + + 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')); + } + + /** + * @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 testAccessDeniedWithNonLocalIps() + { + $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); + } + + /** + * @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) + { + 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/LocaleListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php new file mode 100644 index 0000000..e5e4e3a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.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\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpKernel\EventListener\LocaleListener; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; + +class LocaleListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + } + + public function testDefaultLocaleWithoutSession() + { + $listener = new LocaleListener('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('fr'); + $event = $this->getEvent($request); + + $listener->onKernelRequest($event); + $this->assertEquals('es', $request->getLocale()); + } + + public function testLocaleSetForRoutingContext() + { + if (!class_exists('Symfony\Component\Routing\Router')) { + $this->markTestSkipped('The "Routing" component is not available'); + } + + // 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('fr', $router); + $listener->onKernelRequest($this->getEvent($request)); + } + + public function testRequestLocaleIsNotOverridden() + { + $request = Request::create('/'); + $request->setLocale('de'); + $listener = new LocaleListener('fr'); + $event = $this->getEvent($request); + + $listener->onKernelRequest($event); + $this->assertEquals('de', $request->getLocale()); + } + + private function getEvent(Request $request) + { + return new GetResponseEvent($this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'), $request, HttpKernelInterface::MASTER_REQUEST); + } +} diff --git a/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..3f8e852 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ResponseListenerTest.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\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() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $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..3ba56a8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.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\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpKernel\EventListener\RouterListener; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\Routing\RequestContext; + +class RouterListenerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\Routing\Router')) { + $this->markTestSkipped('The "Routing" component is not available'); + } + } + + /** + * @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); + $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()); + } + + 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, 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, 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()); + } +} 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/ExtensionPresentBundle/Command/BarCommand.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php new file mode 100644 index 0000000..b9a5417 --- /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/FooBarBundle.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/FooBarBundle.php new file mode 100644 index 0000000..f940f83 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/FooBarBundle.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\HttpKernel\Tests\Fixtures; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class FooBarBundle extends Bundle +{ + // We need a full namespaced bundle instance to test isClassInActiveBundle +} 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..32c05f4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForOverrideName.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\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..e24daef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.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\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() + { + } + + public function init() + { + } + + public function registerBundleDirs() + { + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + public function initializeBundles() + { + parent::initializeBundles(); + } + + public function isBooted() + { + return $this->booted; + } + + public function setIsBooted($value) + { + $this->booted = (Boolean) $value; + } +} 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..34e68a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.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\Tests\Fragment\FragmentRenderer; + +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpFoundation\Request; + +class EsiFragmentRendererTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testRenderFallbackToInlineStrategyIfNoRequest() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true)); + $strategy->render('/', Request::create('/')); + } + + 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->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()); + $this->assertEquals('', $strategy->render(new ControllerReference('main_controller', array(), array()), $request, array('alt' => new ControllerReference('alt_controller', array(), array())))->getContent()); + } + + 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..cec8ae9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.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\Fragment; + +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class FragmentHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + */ + public function testRenderWhenRendererDoesNotExist() + { + $handler = new FragmentHandler(); + $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(); + $handler->addRenderer($renderer); + $handler->setRequest(Request::create('/')); + + 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..53baf5a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.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\Fragment\Tests\FragmentRenderer; + +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 +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + /** + * @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 testRenderWhithDefault() + { + // 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()); + } +} 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..c7ae7dd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.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\HttpKernel\Fragment\Tests\FragmentRenderer; + +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 +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + 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_controller%3Dmain_controller'); + $subRequest->attributes->replace(array( + 'object' => $object, + '_format' => 'html', + '_controller' => 'main_controller', + )); + $subRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $subRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $kernel + ->expects($this->any()) + ->method('handle') + ->with($subRequest) + ; + + $strategy = new InlineFragmentRenderer($kernel); + + $strategy->render(new ControllerReference('main_controller', array('object' => $object), array()), Request::create('/')); + } + + /** + * @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; + } + + 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"'); + $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $kernel + ->expects($this->any()) + ->method('handle') + ->with($expectedSubRequest) + ; + + $strategy = new InlineFragmentRenderer($kernel); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + $strategy->render('/', $request); + } +} 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..2106f9d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/RoutableFragmentRendererTest.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\Fragment\Tests\FragmentRenderer; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\RoutableFragmentRenderer; + +class RoutableFragmentRendererTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getGenerateFragmentUriData + */ + public function testGenerateFragmentUri($uri, $controller) + { + $this->assertEquals($uri, $this->getRenderer()->doGenerateFragmentUri($controller, Request::create('/'))); + } + + public function getGenerateFragmentUriData() + { + return array( + array('http://localhost/_fragment?_path=_format%3Dhtml%26_controller%3Dcontroller', new ControllerReference('controller', array(), array())), + array('http://localhost/_fragment?_path=_format%3Dxml%26_controller%3Dcontroller', new ControllerReference('controller', array('_format' => 'xml'), array())), + array('http://localhost/_fragment?_path=foo%3Dfoo%26_format%3Djson%26_controller%3Dcontroller', new ControllerReference('controller', array('foo' => 'foo', '_format' => 'json'), array())), + array('http://localhost/_fragment?bar=bar&_path=foo%3Dfoo%26_format%3Dhtml%26_controller%3Dcontroller', new ControllerReference('controller', array('foo' => 'foo'), array('bar' => 'bar'))), + array('http://localhost/_fragment?foo=foo&_path=_format%3Dhtml%26_controller%3Dcontroller', new ControllerReference('controller', array(), array('foo' => 'foo'))), + ); + } + + public function testGenerateFragmentUriWithARequest() + { + $request = Request::create('/'); + $request->attributes->set('_format', 'json'); + $controller = new ControllerReference('controller', array(), array()); + + $this->assertEquals('http://localhost/_fragment?_path=_format%3Djson%26_controller%3Dcontroller', $this->getRenderer()->doGenerateFragmentUri($controller, $request)); + } + + private function getRenderer() + { + return new Renderer(); + } +} + +class Renderer extends RoutableFragmentRenderer +{ + public function render($uri, Request $request, array $options = array()) {} + public function getName() {} + + public function doGenerateFragmentUri(ControllerReference $reference, Request $request) + { + return parent::generateFragmentUri($reference, $request); + } +} 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..7180da1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php @@ -0,0 +1,227 @@ + + * + * 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 +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testHasSurrogateEsiCapability() + { + $esi = new Esi(); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + $this->assertTrue($esi->hasSurrogateEsiCapability($request)); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'foobar'); + $this->assertFalse($esi->hasSurrogateEsiCapability($request)); + + $request = Request::create('/'); + $this->assertFalse($esi->hasSurrogateEsiCapability($request)); + } + + public function testAddSurrogateEsiCapability() + { + $esi = new Esi(); + + $request = Request::create('/'); + $esi->addSurrogateEsiCapability($request); + $this->assertEquals('symfony2="ESI/1.0"', $request->headers->get('Surrogate-Capability')); + + $esi->addSurrogateEsiCapability($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->needsEsiParsing($response)); + + $response = new Response(); + $this->assertFalse($esi->needsEsiParsing($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 testProcess() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $esi->process($request, $response); + + $this->assertEquals('foo esi->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 esi->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent()); + + $response = new Response('foo '); + $esi->process($request, $response); + + $this->assertEquals('foo esi->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent()); + } + + public function testProcessEscapesPhpTags() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response('foo <%= "lala" %>'); + $esi->process($request, $response); + + $this->assertEquals('foo php die("foo"); ?>= "lala" %>', $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..a8064b8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -0,0 +1,1106 @@ + + * + * 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\HttpKernel\HttpCache\StoreInterface; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class HttpCacheTest extends HttpCacheTestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testTerminateDelegatesTerminationOnlyForTerminableInterface() + { + if (!class_exists('Symfony\Component\DependencyInjection\Container')) { + $this->markTestSkipped('The "DependencyInjection" component is not available'); + } + + $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 = new \DateTime(); + + $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 = new \DateTime(); + + $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()); + } + + 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'.sha1('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() + { + $that = $this; + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($that) { + $response->setContent(''); + $response->setStatusCode(200); + $that->assertEquals('HEAD', $request->getMethod()); + }); + + $this->request('HEAD', '/', array('HTTP_EXPECT' => 'something ...')); + $this->assertHttpKernelIsCalled(); + $this->assertEquals('', $this->response->getContent()); + } + + public function testUsesCacheToRespondToHeadRequestsWhenFresh() + { + $that = $this; + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($that) { + $response->headers->set('Cache-Control', 'public, max-age=10'); + $response->setContent('Hello World'); + $response->setStatusCode(200); + $that->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()); + $that = $this; + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($that, $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 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 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 = new \DateTime; + + $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..4377f61 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.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\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 function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $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) + { + 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->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/StoreTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php new file mode 100644 index 0000000..6a3e565 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.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\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() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + + $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('ena94a8fe5ccb19ba61c4c0873d391e987982fbbd3', $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'.sha1('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 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'.sha1('test 3')), $this->store->lookup($req3)->getContent()); + $this->assertEquals($this->getStorePath('en'.sha1('test 2')), $this->store->lookup($req2)->getContent()); + $this->assertEquals($this->getStorePath('en'.sha1('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'.sha1('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'.sha1('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'.sha1('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..cf23d7b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.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\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; + protected $customizer; + protected $catch; + protected $backendRequest; + + public function __construct($body, $status, $headers, \Closure $customizer = null) + { + $this->body = $body; + $this->status = $status; + $this->headers = $headers; + $this->customizer = $customizer; + $this->called = false; + $this->catch = false; + + 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 !== $this->customizer) { + call_user_func($this->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..6dd3d9e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestMultipleHttpKernel.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\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; + protected $statuses; + protected $headers; + protected $catch; + protected $call; + protected $backendRequest; + + public function __construct($responses) + { + $this->bodies = array(); + $this->statuses = array(); + $this->headers = array(); + $this->call = false; + + 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->call = 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..367e3e2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.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\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 +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + /** + * @expectedException RuntimeException + */ + public function testHandleWhenControllerThrowsAnExceptionAndRawIsTrue() + { + $kernel = new HttpKernel(new EventDispatcher(), $this->getResolver(function () { throw new \RuntimeException(); })); + + $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true); + } + + /** + * @expectedException RuntimeException + */ + public function testHandleWhenControllerThrowsAnExceptionAndRawIsFalseAndNoListenerIsRegistered() + { + $kernel = new HttpKernel(new EventDispatcher(), $this->getResolver(function () { throw new \RuntimeException(); })); + + $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, false); + } + + public function testHandleWhenControllerThrowsAnExceptionAndRawIsFalse() + { + $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()); + + $this->assertEquals('500', $response->getStatusCode()); + $this->assertEquals('foo', $response->getContent()); + } + + 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()); + } + + /** + * @expectedException LogicException + */ + public function testHandleWhenTheControllerIsNotACallable() + { + $dispatcher = new EventDispatcher(); + $kernel = new HttpKernel($dispatcher, $this->getResolver('foobar')); + + $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); + } + + 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..b36f909 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -0,0 +1,875 @@ + + * + * 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\Kernel; +use Symfony\Component\HttpKernel\Bundle\Bundle; +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; +use Symfony\Component\HttpKernel\Tests\Fixtures\FooBarBundle; + +class KernelTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\DependencyInjection\Container')) { + $this->markTestSkipped('The "DependencyInjection" component is not available'); + } + } + + 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->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('initializeBundles'); + $kernel->expects($this->once()) + ->method('initializeContainer'); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array())); + + $kernel->boot(); + } + + public function testBootSetsTheContainerToTheBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle') + ->disableOriginalConstructor() + ->getMock(); + $bundle->expects($this->once()) + ->method('setContainer'); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $kernel->boot(); + } + + public function testBootSetsTheBootedFlagToTrue() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array())); + + $kernel->boot(); + + $this->assertTrue($kernel->isBooted()); + } + + public function testClassCacheIsLoaded() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles', 'doLoadClassCache')) + ->getMock(); + $kernel->loadClassCache('name', '.extension'); + $kernel->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue(array())); + $kernel->expects($this->once()) + ->method('doLoadClassCache') + ->with('name', '.extension'); + + $kernel->boot(); + } + + public function testClassCacheIsNotLoadedByDefault() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles', 'doLoadClassCache')) + ->getMock(); + $kernel->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue(array())); + $kernel->expects($this->never()) + ->method('doLoadClassCache'); + + $kernel->boot(); + } + + public function testClassCacheIsNotLoadedWhenKernelIsNotBooted() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles', 'doLoadClassCache')) + ->getMock(); + $kernel->loadClassCache(); + $kernel->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue(array())); + $kernel->expects($this->never()) + ->method('doLoadClassCache'); + } + + public function testBootKernelSeveralTimesOnlyInitializesBundlesOnce() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array())); + + $kernel->boot(); + $kernel->boot(); + } + + public function testShutdownCallsShutdownOnAllBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle') + ->disableOriginalConstructor() + ->getMock(); + $bundle->expects($this->once()) + ->method('shutdown'); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $kernel->shutdown(); + } + + public function testShutdownGivesNullContainerToAllBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle') + ->disableOriginalConstructor() + ->getMock(); + $bundle->expects($this->once()) + ->method('setContainer') + ->with(null); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $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->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getHttpKernel')) + ->getMock(); + + $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->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getHttpKernel', 'boot')) + ->getMock(); + + $kernel->expects($this->once()) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->expects($this->once()) + ->method('boot'); + + // required as this value is initialized + // in the kernel constructor, which we don't call + $kernel->setIsBooted(false); + + $kernel->handle($request, $type, $catch); + } + + public function testStripComments() + { + if (!function_exists('token_get_all')) { + $this->markTestSkipped('The function token_get_all() is not available.'); + + return; + } + $source = <<<'EOF' +assertEquals($expected, $output); + } + + public function testIsClassInActiveBundleFalse() + { + $kernel = $this->getKernelMockForIsClassInActiveBundleTest(); + + $this->assertFalse($kernel->isClassInActiveBundle('Not\In\Active\Bundle')); + } + + public function testIsClassInActiveBundleFalseNoNamespace() + { + $kernel = $this->getKernelMockForIsClassInActiveBundleTest(); + + $this->assertFalse($kernel->isClassInActiveBundle('NotNamespacedClass')); + } + + public function testIsClassInActiveBundleTrue() + { + $kernel = $this->getKernelMockForIsClassInActiveBundleTest(); + + $this->assertTrue($kernel->isClassInActiveBundle(__NAMESPACE__.'\Fixtures\FooBarBundle\SomeClass')); + } + + protected function getKernelMockForIsClassInActiveBundleTest() + { + $bundle = new FooBarBundle(); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + return $kernel; + } + + 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->getKernelForInvalidLocateResource()->locateResource('Foo'); + } + + /** + * @expectedException \RuntimeException + */ + public function testLocateResourceThrowsExceptionWhenNameIsUnsafe() + { + $this->getKernelForInvalidLocateResource()->locateResource('@FooBundle/../bar'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateResourceThrowsExceptionWhenBundleDoesNotExist() + { + $this->getKernelForInvalidLocateResource()->locateResource('@FooBundle/config/routing.xml'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateResourceThrowsExceptionWhenResourceDoesNotExist() + { + $kernel = $this->getKernel(); + $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(); + $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(); + $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(); + $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(); + $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(); + $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(); + $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(); + $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(); + $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(); + $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(); + $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'); + + $kernel = $this->getKernel(); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($parent, $child))) + ; + $kernel->initializeBundles(); + + $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'); + + $kernel = $this->getKernel(); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($grandparent, $parent, $child))) + ; + + $kernel->initializeBundles(); + + $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 + */ + public function testInitializeBundlesThrowsExceptionWhenAParentDoesNotExists() + { + $child = $this->getBundle(null, 'FooBar', 'ChildCBundle'); + + $kernel = $this->getKernel(); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($child))) + ; + $kernel->initializeBundles(); + } + + public function testInitializeBundlesSupportsArbitraryBundleRegistrationOrder() + { + $grandparent = $this->getBundle(null, null, 'GrandParentCCundle'); + $parent = $this->getBundle(null, 'GrandParentCCundle', 'ParentCCundle'); + $child = $this->getBundle(null, 'ParentCCundle', 'ChildCCundle'); + + $kernel = $this->getKernel(); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($parent, $grandparent, $child))) + ; + + $kernel->initializeBundles(); + + $map = $kernel->getBundleMap(); + $this->assertEquals(array($child, $parent, $grandparent), $map['GrandParentCCundle']); + $this->assertEquals(array($child, $parent), $map['ParentCCundle']); + $this->assertEquals(array($child), $map['ChildCCundle']); + } + + /** + * @expectedException \LogicException + */ + public function testInitializeBundlesThrowsExceptionWhenABundleIsDirectlyExtendedByTwoBundles() + { + $parent = $this->getBundle(null, null, 'ParentCBundle'); + $child1 = $this->getBundle(null, 'ParentCBundle', 'ChildC1Bundle'); + $child2 = $this->getBundle(null, 'ParentCBundle', 'ChildC2Bundle'); + + $kernel = $this->getKernel(); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($parent, $child1, $child2))) + ; + $kernel->initializeBundles(); + } + + /** + * @expectedException \LogicException + */ + public function testInitializeBundleThrowsExceptionWhenRegisteringTwoBundlesWithTheSameName() + { + $fooBundle = $this->getBundle(null, null, 'FooBundle', 'DuplicateName'); + $barBundle = $this->getBundle(null, null, 'BarBundle', 'DuplicateName'); + + $kernel = $this->getKernel(); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($fooBundle, $barBundle))) + ; + $kernel->initializeBundles(); + } + + /** + * @expectedException \LogicException + */ + public function testInitializeBundleThrowsExceptionWhenABundleExtendsItself() + { + $circularRef = $this->getBundle(null, 'CircularRefBundle', 'CircularRefBundle'); + + $kernel = $this->getKernel(); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($circularRef))) + ; + $kernel->initializeBundles(); + } + + public function testTerminateReturnsSilentlyIfKernelIsNotBooted() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getHttpKernel')) + ->getMock(); + + $kernel->expects($this->never()) + ->method('getHttpKernel'); + + $kernel->setIsBooted(false); + $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->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getHttpKernel')) + ->getMock(); + + $kernel->expects($this->once()) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->setIsBooted(true); + $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->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getHttpKernel')) + ->getMock(); + + $kernel->expects($this->exactly(2)) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->setIsBooted(true); + $kernel->terminate(Request::create('/'), new Response()); + } + + 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; + } + + protected function getKernel() + { + return $this + ->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->setMethods(array('getBundle', 'registerBundles')) + ->disableOriginalConstructor() + ->getMock() + ; + } + + protected function getKernelForInvalidLocateResource() + { + return $this + ->getMockBuilder('Symfony\Component\HttpKernel\Kernel') + ->disableOriginalConstructor() + ->getMockForAbstractClass() + ; + } +} 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..1be77f2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Logger.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\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); + } + + /** + * @deprecated + */ + public function emerg($message, array $context = array()) + { + trigger_error('Use emergency() which is PSR-3 compatible', E_USER_DEPRECATED); + + $this->log('emergency', $message, $context); + } + + /** + * @deprecated + */ + public function crit($message, array $context = array()) + { + trigger_error('Use crit() which is PSR-3 compatible', E_USER_DEPRECATED); + + $this->log('critical', $message, $context); + } + + /** + * @deprecated + */ + public function err($message, array $context = array()) + { + trigger_error('Use err() which is PSR-3 compatible', E_USER_DEPRECATED); + + $this->log('error', $message, $context); + } + + /** + * @deprecated + */ + public function warn($message, array $context = array()) + { + trigger_error('Use warn() which is PSR-3 compatible', E_USER_DEPRECATED); + + $this->log('warning', $message, $context); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/AbstractProfilerStorageTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/AbstractProfilerStorageTest.php new file mode 100644 index 0000000..4657ff1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/AbstractProfilerStorageTest.php @@ -0,0 +1,253 @@ + + * + * 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\Profile; + +abstract class AbstractProfilerStorageTest extends \PHPUnit_Framework_TestCase +{ + 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->getStorage()->write($profile); + } + $this->assertCount(10, $this->getStorage()->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->getStorage()->write($parentProfile); + $this->getStorage()->write($childProfile); + + // Load them from storage + $parentProfile = $this->getStorage()->read('token_parent'); + $childProfile = $this->getStorage()->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->getStorage()->write($profile); + $this->assertTrue(false !== $this->getStorage()->read('simple_quote'), '->write() accepts single quotes in URL'); + + $profile = new Profile('double_quote'); + $profile->setUrl('http://foo.bar/"'); + $this->getStorage()->write($profile); + $this->assertTrue(false !== $this->getStorage()->read('double_quote'), '->write() accepts double quotes in URL'); + + $profile = new Profile('backslash'); + $profile->setUrl('http://foo.bar/\\'); + $this->getStorage()->write($profile); + $this->assertTrue(false !== $this->getStorage()->read('backslash'), '->write() accepts backslash in URL'); + + $profile = new Profile('comma'); + $profile->setUrl('http://foo.bar/,'); + $this->getStorage()->write($profile); + $this->assertTrue(false !== $this->getStorage()->read('comma'), '->write() accepts comma in URL'); + } + + public function testStoreDuplicateToken() + { + $profile = new Profile('token'); + $profile->setUrl('http://example.com/'); + + $this->assertTrue($this->getStorage()->write($profile), '->write() returns true when the token is unique'); + + $profile->setUrl('http://example.net/'); + + $this->assertTrue($this->getStorage()->write($profile), '->write() returns true when the token is already present in the storage'); + $this->assertEquals('http://example.net/', $this->getStorage()->read('token')->getUrl(), '->write() overwrites the current profile data'); + + $this->assertCount(1, $this->getStorage()->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->getStorage()->write($profile); + + $this->assertCount(1, $this->getStorage()->find('127.0.0.1', '', 10, 'GET'), '->find() retrieve a record by IP'); + $this->assertCount(0, $this->getStorage()->find('127.0.%.1', '', 10, 'GET'), '->find() does not interpret a "%" as a wildcard in the IP'); + $this->assertCount(0, $this->getStorage()->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->getStorage()->write($profile); + + $profile = new Profile('double_quote'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/"'); + $profile->setMethod('GET'); + $this->getStorage()->write($profile); + + $profile = new Profile('backslash'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo\\bar/'); + $profile->setMethod('GET'); + $this->getStorage()->write($profile); + + $profile = new Profile('percent'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/%'); + $profile->setMethod('GET'); + $this->getStorage()->write($profile); + + $profile = new Profile('underscore'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/_'); + $profile->setMethod('GET'); + $this->getStorage()->write($profile); + + $profile = new Profile('semicolon'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/;'); + $profile->setMethod('GET'); + $this->getStorage()->write($profile); + + $this->assertCount(1, $this->getStorage()->find('127.0.0.1', 'http://foo.bar/\'', 10, 'GET'), '->find() accepts single quotes in URLs'); + $this->assertCount(1, $this->getStorage()->find('127.0.0.1', 'http://foo.bar/"', 10, 'GET'), '->find() accepts double quotes in URLs'); + $this->assertCount(1, $this->getStorage()->find('127.0.0.1', 'http://foo\\bar/', 10, 'GET'), '->find() accepts backslash in URLs'); + $this->assertCount(1, $this->getStorage()->find('127.0.0.1', 'http://foo.bar/;', 10, 'GET'), '->find() accepts semicolon in URLs'); + $this->assertCount(1, $this->getStorage()->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->getStorage()->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->getStorage()->write($profile); + } + + $records = $this->getStorage()->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->getStorage()->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->getStorage()->write($profile); + } + $this->assertCount(5, $this->getStorage()->find('', '', 10, 'GET'), '->find() returns all previously added records'); + $this->getStorage()->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->getStorage()->write($profile); + } + } + + $this->assertCount(5, $this->getStorage()->find('', '', 5, 'POST')); + + $this->getStorage()->purge(); + } + + public function testPurge() + { + $profile = new Profile('token1'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://example.com/'); + $profile->setMethod('GET'); + $this->getStorage()->write($profile); + + $this->assertTrue(false !== $this->getStorage()->read('token1')); + $this->assertCount(1, $this->getStorage()->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->getStorage()->write($profile); + + $this->assertTrue(false !== $this->getStorage()->read('token2')); + $this->assertCount(2, $this->getStorage()->find('127.0.0.1', '', 10, 'GET')); + + $this->getStorage()->purge(); + + $this->assertEmpty($this->getStorage()->read('token'), '->purge() removes all data stored by profiler'); + $this->assertCount(0, $this->getStorage()->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->getStorage()->write($profile); + $this->getStorage()->write($profile); + $this->getStorage()->write($profile); + } + $this->assertCount(3, $this->getStorage()->find('127.0.0.1', 'http://example.net/', 3, 'GET'), '->find() method returns incorrect number of entries'); + } + + /** + * @return \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface + */ + abstract protected function getStorage(); +} 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..3c2d04c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/FileProfilerStorageTest.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\Profiler; + +use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage; +use Symfony\Component\HttpKernel\Profiler\Profile; + +class FileProfilerStorageTest extends AbstractProfilerStorageTest +{ + protected static $tmpDir; + protected static $storage; + + protected static function cleanDir() + { + $flags = \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveDirectoryIterator(self::$tmpDir, $flags); + $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST); + + foreach ($iterator as $file) { + if (is_file($file)) { + unlink($file); + } + } + } + + public static function setUpBeforeClass() + { + self::$tmpDir = sys_get_temp_dir().'/sf2_profiler_file_storage'; + if (is_dir(self::$tmpDir)) { + self::cleanDir(); + } + self::$storage = new FileProfilerStorage('file:'.self::$tmpDir); + } + + public static function tearDownAfterClass() + { + self::cleanDir(); + } + + protected function setUp() + { + self::$storage->purge(); + } + + /** + * @return \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface + */ + protected function getStorage() + { + return self::$storage; + } + + 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); + $storage = $this->getStorage(); + + $storage->write($profile); + $storage->write($profile); + $storage->write($profile); + } + + $handle = fopen(self::$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(self::$storage, 'readLineFromFile'); + + $r->setAccessible(true); + + $h = tmpfile(); + + fwrite($h, "line1\n\n\nline2\n"); + fseek($h, 0, SEEK_END); + + $this->assertEquals("line2", $r->invoke(self::$storage, $h)); + $this->assertEquals("line1", $r->invoke(self::$storage, $h)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/MemcacheProfilerStorageTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/MemcacheProfilerStorageTest.php new file mode 100644 index 0000000..f582dff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/MemcacheProfilerStorageTest.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\HttpKernel\Tests\Profiler; + +use Symfony\Component\HttpKernel\Profiler\MemcacheProfilerStorage; +use Symfony\Component\HttpKernel\Tests\Profiler\Mock\MemcacheMock; + +class MemcacheProfilerStorageTest extends AbstractProfilerStorageTest +{ + protected static $storage; + + protected function setUp() + { + $memcacheMock = new MemcacheMock(); + $memcacheMock->addServer('127.0.0.1', 11211); + + self::$storage = new MemcacheProfilerStorage('memcache://127.0.0.1:11211', '', '', 86400); + self::$storage->setMemcache($memcacheMock); + + if (self::$storage) { + self::$storage->purge(); + } + } + + protected function tearDown() + { + if (self::$storage) { + self::$storage->purge(); + self::$storage = false; + } + } + + /** + * @return \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface + */ + protected function getStorage() + { + return self::$storage; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/MemcachedProfilerStorageTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/MemcachedProfilerStorageTest.php new file mode 100644 index 0000000..565ac35 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/MemcachedProfilerStorageTest.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\HttpKernel\Tests\Profiler; + +use Symfony\Component\HttpKernel\Profiler\MemcachedProfilerStorage; +use Symfony\Component\HttpKernel\Tests\Profiler\Mock\MemcachedMock; + +class MemcachedProfilerStorageTest extends AbstractProfilerStorageTest +{ + protected static $storage; + + protected function setUp() + { + $memcachedMock = new MemcachedMock(); + $memcachedMock->addServer('127.0.0.1', 11211); + + self::$storage = new MemcachedProfilerStorage('memcached://127.0.0.1:11211', '', '', 86400); + self::$storage->setMemcached($memcachedMock); + + if (self::$storage) { + self::$storage->purge(); + } + } + + protected function tearDown() + { + if (self::$storage) { + self::$storage->purge(); + self::$storage = false; + } + } + + /** + * @return \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface + */ + protected function getStorage() + { + return self::$storage; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/Mock/MemcacheMock.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/Mock/MemcacheMock.php new file mode 100644 index 0000000..014f549 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/Mock/MemcacheMock.php @@ -0,0 +1,260 @@ + + * + * 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\Mock; + +/** + * MemcacheMock for simulating Memcache extension in tests. + * + * @author Andrej Hudec + */ +class MemcacheMock +{ + private $connected; + private $storage; + + public function __construct() + { + $this->connected = false; + $this->storage = array(); + } + + /** + * Open memcached server connection + * + * @param string $host + * @param integer $port + * @param integer $timeout + * + * @return boolean + */ + public function connect($host, $port = null, $timeout = null) + { + if ('127.0.0.1' == $host && 11211 == $port) { + $this->connected = true; + + return true; + } + + return false; + } + + /** + * Open memcached server persistent connection + * + * @param string $host + * @param integer $port + * @param integer $timeout + * + * @return boolean + */ + public function pconnect($host, $port = null, $timeout = null) + { + if ('127.0.0.1' == $host && 11211 == $port) { + $this->connected = true; + + return true; + } + + return false; + } + + /** + * Add a memcached server to connection pool + * + * @param string $host + * @param integer $port + * @param boolean $persistent + * @param integer $weight + * @param integer $timeout + * @param integer $retry_interval + * @param boolean $status + * @param callable $failure_callback + * @param integer $timeoutms + * + * @return boolean + */ + public function addServer($host, $port = 11211, $persistent = null, $weight = null, $timeout = null, $retry_interval = null, $status = null, $failure_callback = null, $timeoutms = null) + { + if ('127.0.0.1' == $host && 11211 == $port) { + $this->connected = true; + + return true; + } + + return false; + } + + /** + * Add an item to the server only if such key doesn't exist at the server yet. + * + * @param string $key + * @param mixed $var + * @param integer $flag + * @param integer $expire + * + * @return boolean + */ + public function add($key, $var, $flag = null, $expire = null) + { + if (!$this->connected) { + return false; + } + + if (!isset($this->storage[$key])) { + $this->storeData($key, $var); + + return true; + } + + return false; + } + + /** + * Store data at the server. + * + * @param string $key + * @param string $var + * @param integer $flag + * @param integer $expire + * + * @return boolean + */ + public function set($key, $var, $flag = null, $expire = null) + { + if (!$this->connected) { + return false; + } + + $this->storeData($key, $var); + + return true; + } + + /** + * Replace value of the existing item. + * + * @param string $key + * @param mixed $var + * @param integer $flag + * @param integer $expire + * + * @return boolean + */ + public function replace($key, $var, $flag = null, $expire = null) + { + if (!$this->connected) { + return false; + } + + if (isset($this->storage[$key])) { + $this->storeData($key, $var); + + return true; + } + + return false; + } + + /** + * Retrieve item from the server. + * + * @param string|array $key + * @param integer|array $flags + * + * @return mixed + */ + public function get($key, &$flags = null) + { + if (!$this->connected) { + return false; + } + + if (is_array($key)) { + $result = array(); + foreach ($key as $k) { + if (isset($this->storage[$k])) { + $result[] = $this->getData($k); + } + } + + return $result; + } + + return $this->getData($key); + } + + /** + * Delete item from the server + * + * @param string $key + * + * @return boolean + */ + public function delete($key) + { + if (!$this->connected) { + return false; + } + + if (isset($this->storage[$key])) { + unset($this->storage[$key]); + + return true; + } + + return false; + } + + /** + * Flush all existing items at the server + * + * @return boolean + */ + public function flush() + { + if (!$this->connected) { + return false; + } + + $this->storage = array(); + + return true; + } + + /** + * Close memcached server connection + * + * @return boolean + */ + public function close() + { + $this->connected = false; + + return true; + } + + private function getData($key) + { + if (isset($this->storage[$key])) { + return unserialize($this->storage[$key]); + } + + return false; + } + + private function storeData($key, $value) + { + $this->storage[$key] = serialize($value); + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/Mock/MemcachedMock.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/Mock/MemcachedMock.php new file mode 100644 index 0000000..2b17d70 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/Mock/MemcachedMock.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\Tests\Profiler\Mock; + +/** + * MemcachedMock for simulating Memcached extension in tests. + * + * @author Andrej Hudec + */ +class MemcachedMock +{ + private $connected; + private $storage; + + public function __construct() + { + $this->connected = false; + $this->storage = array(); + } + + /** + * Set a Memcached option + * + * @param integer $option + * @param mixed $value + * + * @return boolean + */ + public function setOption($option, $value) + { + return true; + } + + /** + * Add a memcached server to connection pool + * + * @param string $host + * @param integer $port + * @param integer $weight + * + * @return boolean + */ + public function addServer($host, $port = 11211, $weight = 0) + { + if ('127.0.0.1' == $host && 11211 == $port) { + $this->connected = true; + + return true; + } + + return false; + } + + /** + * Add an item to the server only if such key doesn't exist at the server yet. + * + * @param string $key + * @param mixed $value + * @param integer $expiration + * + * @return boolean + */ + public function add($key, $value, $expiration = 0) + { + if (!$this->connected) { + return false; + } + + if (!isset($this->storage[$key])) { + $this->storeData($key, $value); + + return true; + } + + return false; + } + + /** + * Store data at the server. + * + * @param string $key + * @param mixed $value + * @param integer $expiration + * + * @return boolean + */ + public function set($key, $value, $expiration = null) + { + if (!$this->connected) { + return false; + } + + $this->storeData($key, $value); + + return true; + } + + /** + * Replace value of the existing item. + * + * @param string $key + * @param mixed $value + * @param integer $expiration + * + * @return boolean + */ + public function replace($key, $value, $expiration = null) + { + if (!$this->connected) { + return false; + } + + if (isset($this->storage[$key])) { + $this->storeData($key, $value); + + return true; + } + + return false; + } + + /** + * Retrieve item from the server. + * + * @param string $key + * @param callable $cache_cb + * @param float $cas_token + * + * @return boolean + */ + public function get($key, $cache_cb = null, &$cas_token = null) + { + if (!$this->connected) { + return false; + } + + return $this->getData($key); + } + + /** + * Append data to an existing item + * + * @param string $key + * @param string $value + * + * @return boolean + */ + public function append($key, $value) + { + if (!$this->connected) { + return false; + } + + if (isset($this->storage[$key])) { + $this->storeData($key, $this->getData($key).$value); + + return true; + } + + return false; + } + + /** + * Delete item from the server + * + * @param string $key + * + * @return boolean + */ + public function delete($key) + { + if (!$this->connected) { + return false; + } + + if (isset($this->storage[$key])) { + unset($this->storage[$key]); + + return true; + } + + return false; + } + + /** + * Flush all existing items at the server + * + * @return boolean + */ + public function flush() + { + if (!$this->connected) { + return false; + } + + $this->storage = array(); + + return true; + } + + private function getData($key) + { + if (isset($this->storage[$key])) { + return unserialize($this->storage[$key]); + } + + return false; + } + + private function storeData($key, $value) + { + $this->storage[$key] = serialize($value); + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/Mock/RedisMock.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/Mock/RedisMock.php new file mode 100644 index 0000000..ca2980e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/Mock/RedisMock.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\Component\HttpKernel\Tests\Profiler\Mock; + +/** + * RedisMock for simulating Redis extension in tests. + * + * @author Andrej Hudec + */ +class RedisMock +{ + + private $connected; + private $storage; + + public function __construct() + { + $this->connected = false; + $this->storage = array(); + } + + /** + * Add a server to connection pool + * + * @param string $host + * @param integer $port + * @param float $timeout + * + * @return boolean + */ + public function connect($host, $port = 6379, $timeout = 0) + { + if ('127.0.0.1' == $host && 6379 == $port) { + $this->connected = true; + + return true; + } + + return false; + } + + /** + * Set client option. + * + * @param integer $name + * @param integer $value + * + * @return boolean + */ + public function setOption($name, $value) + { + if (!$this->connected) { + return false; + } + + return true; + } + + /** + * Verify if the specified key exists. + * + * @param string $key + * + * @return boolean + */ + public function exists($key) + { + if (!$this->connected) { + return false; + } + + return isset($this->storage[$key]); + } + + /** + * Store data at the server with expiration time. + * + * @param string $key + * @param integer $ttl + * @param mixed $value + * + * @return boolean + */ + public function setex($key, $ttl, $value) + { + if (!$this->connected) { + return false; + } + + $this->storeData($key, $value); + + return true; + } + + /** + * Sets an expiration time on an item. + * + * @param string $key + * @param integer $ttl + * + * @return boolean + */ + public function setTimeout($key, $ttl) + { + if (!$this->connected) { + return false; + } + + if (isset($this->storage[$key])) { + return true; + } + + return false; + } + + /** + * Retrieve item from the server. + * + * @param string $key + * + * @return boolean + */ + public function get($key) + { + if (!$this->connected) { + return false; + } + + return $this->getData($key); + } + + /** + * Append data to an existing item + * + * @param string $key + * @param string $value + * + * @return integer Size of the value after the append. + */ + public function append($key, $value) + { + if (!$this->connected) { + return false; + } + + if (isset($this->storage[$key])) { + $this->storeData($key, $this->getData($key).$value); + + return strlen($this->storage[$key]); + } + + return false; + } + + /** + * Remove specified keys. + * + * @param string|array $key + * + * @return integer + */ + public function delete($key) + { + if (!$this->connected) { + return false; + } + + if (is_array($key)) { + $result = 0; + foreach ($key as $k) { + if (isset($this->storage[$k])) { + unset($this->storage[$k]); + ++$result; + } + } + + return $result; + } + + if (isset($this->storage[$key])) { + unset($this->storage[$key]); + + return 1; + } + + return 0; + } + + /** + * Flush all existing items from all databases at the server. + * + * @return boolean + */ + public function flushAll() + { + if (!$this->connected) { + return false; + } + + $this->storage = array(); + + return true; + } + + /** + * Close Redis server connection + * + * @return boolean + */ + public function close() + { + $this->connected = false; + + return true; + } + + private function getData($key) + { + if (isset($this->storage[$key])) { + return unserialize($this->storage[$key]); + } + + return false; + } + + private function storeData($key, $value) + { + $this->storage[$key] = serialize($value); + + return true; + } + + public function select($dbnum) + { + if (!$this->connected) { + return false; + } + + if (0 > $dbnum) { + return false; + } + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/MongoDbProfilerStorageTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/MongoDbProfilerStorageTest.php new file mode 100644 index 0000000..b63b84c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/MongoDbProfilerStorageTest.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\HttpKernel\Tests\Profiler; + +use Symfony\Component\HttpKernel\Profiler\MongoDbProfilerStorage; +use Symfony\Component\HttpKernel\Profiler\Profile; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class DummyMongoDbProfilerStorage extends MongoDbProfilerStorage +{ + public function getMongo() + { + return parent::getMongo(); + } +} + +class MongoDbProfilerStorageTestDataCollector extends DataCollector +{ + public function setData($data) + { + $this->data = $data; + } + + public function getData() + { + return $this->data; + } + + public function collect(Request $request, Response $response, \Exception $exception = null) + { + } + + public function getName() + { + return 'test_data_collector'; + } +} + +class MongoDbProfilerStorageTest extends AbstractProfilerStorageTest +{ + protected static $storage; + + public static function setUpBeforeClass() + { + if (extension_loaded('mongo')) { + self::$storage = new DummyMongoDbProfilerStorage('mongodb://localhost/symfony_tests/profiler_data', '', '', 86400); + try { + self::$storage->getMongo(); + } catch (\MongoConnectionException $e) { + self::$storage = null; + } + } + } + + public static function tearDownAfterClass() + { + if (self::$storage) { + self::$storage->purge(); + self::$storage = null; + } + } + + public function testCleanup() + { + $dt = new \DateTime('-2 day'); + for ($i = 0; $i < 3; $i++) { + $dt->modify('-1 day'); + $profile = new Profile('time_'.$i); + $profile->setTime($dt->getTimestamp()); + $profile->setMethod('GET'); + self::$storage->write($profile); + } + $records = self::$storage->find('', '', 3, 'GET'); + $this->assertCount(1, $records, '->find() returns only one record'); + $this->assertEquals($records[0]['token'], 'time_2', '->find() returns the latest added record'); + self::$storage->purge(); + } + + public function testUtf8() + { + $profile = new Profile('utf8_test_profile'); + + $data = 'HЁʃʃϿ, ϢorЃd!'; + $nonUtf8Data = mb_convert_encoding($data, 'UCS-2'); + + $collector = new MongoDbProfilerStorageTestDataCollector(); + $collector->setData($nonUtf8Data); + + $profile->setCollectors(array($collector)); + + self::$storage->write($profile); + + $readProfile = self::$storage->read('utf8_test_profile'); + $collectors = $readProfile->getCollectors(); + + $this->assertCount(1, $collectors); + $this->assertArrayHasKey('test_data_collector', $collectors); + $this->assertEquals($nonUtf8Data, $collectors['test_data_collector']->getData(), 'Non-UTF8 data is properly encoded/decoded'); + } + + /** + * @return \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface + */ + protected function getStorage() + { + return self::$storage; + } + + protected function setUp() + { + if (self::$storage) { + self::$storage->purge(); + } else { + $this->markTestSkipped('MongoDbProfilerStorageTest requires the mongo PHP extension and a MongoDB server on localhost'); + } + } +} 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..2a41531 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.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\Tests\Profiler; + +use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector; +use Symfony\Component\HttpKernel\Profiler\SqliteProfilerStorage; +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class ProfilerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + $this->markTestSkipped('The "HttpFoundation" component is not available'); + } + } + + public function testCollect() + { + if (!class_exists('SQLite3') && (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers()))) { + $this->markTestSkipped('This test requires SQLite support in your environment'); + } + + $request = new Request(); + $request->query->set('foo', 'bar'); + $response = new Response(); + $collector = new RequestDataCollector(); + + $tmp = tempnam(sys_get_temp_dir(), 'sf2_profiler'); + if (file_exists($tmp)) { + @unlink($tmp); + } + $storage = new SqliteProfilerStorage('sqlite:'.$tmp); + $storage->purge(); + + $profiler = new Profiler($storage); + $profiler->add($collector); + $profile = $profiler->collect($request, $response); + + $profile = $profiler->loadProfile($profile->getToken()); + $this->assertEquals(array('foo' => 'bar'), $profiler->get('request')->getRequestQuery()->all()); + + @unlink($tmp); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/RedisProfilerStorageTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/RedisProfilerStorageTest.php new file mode 100644 index 0000000..91354ae --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/RedisProfilerStorageTest.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\HttpKernel\Tests\Profiler; + +use Symfony\Component\HttpKernel\Profiler\RedisProfilerStorage; +use Symfony\Component\HttpKernel\Tests\Profiler\Mock\RedisMock; + +class RedisProfilerStorageTest extends AbstractProfilerStorageTest +{ + protected static $storage; + + protected function setUp() + { + $redisMock = new RedisMock(); + $redisMock->connect('127.0.0.1', 6379); + + self::$storage = new RedisProfilerStorage('redis://127.0.0.1:6379', '', '', 86400); + self::$storage->setRedis($redisMock); + + if (self::$storage) { + self::$storage->purge(); + } + } + + protected function tearDown() + { + if (self::$storage) { + self::$storage->purge(); + self::$storage = false; + } + } + + /** + * @return \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface + */ + protected function getStorage() + { + return self::$storage; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/SqliteProfilerStorageTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/SqliteProfilerStorageTest.php new file mode 100644 index 0000000..43546c1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/SqliteProfilerStorageTest.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\HttpKernel\Tests\Profiler; + +use Symfony\Component\HttpKernel\Profiler\SqliteProfilerStorage; + +class SqliteProfilerStorageTest extends AbstractProfilerStorageTest +{ + protected static $dbFile; + protected static $storage; + + public static function setUpBeforeClass() + { + self::$dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_storage'); + if (file_exists(self::$dbFile)) { + @unlink(self::$dbFile); + } + self::$storage = new SqliteProfilerStorage('sqlite:'.self::$dbFile); + } + + public static function tearDownAfterClass() + { + @unlink(self::$dbFile); + } + + protected function setUp() + { + if (!class_exists('SQLite3') && (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers()))) { + $this->markTestSkipped('This test requires SQLite support in your environment'); + } + self::$storage->purge(); + } + + /** + * @return \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface + */ + protected function getStorage() + { + return self::$storage; + } +} 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..8ffc2bf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.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; + +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'))); + } +} 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..665e99e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/UriSigner.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\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) + { + 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 Boolean True if the URI is signed correctly, false otherwise + */ + public function check($uri) + { + if (!preg_match('/^(.*)(?:\?|&)_hash=(.+?)$/', $uri, $matches)) { + return false; + } + + return $this->computeHash($matches[1]) === $matches[2]; + } + + private function computeHash($uri) + { + return urlencode(base64_encode(hash_hmac('sha1', $uri, $this->secret, true))); + } +} 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..6cccbc7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/composer.json @@ -0,0 +1,54 @@ +{ + "name": "symfony/http-kernel", + "type": "library", + "description": "Symfony HttpKernel Component", + "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.3", + "symfony/event-dispatcher": "~2.1", + "symfony/http-foundation": "~2.2", + "symfony/debug": "~2.3", + "psr/log": "~1.0" + }, + "require-dev": { + "symfony/browser-kit": "2.2.*", + "symfony/class-loader": "~2.1", + "symfony/config": "~2.0", + "symfony/console": "2.2.*", + "symfony/dependency-injection": "~2.0", + "symfony/finder": "~2.0", + "symfony/process": "~2.0", + "symfony/routing": "~2.2", + "symfony/stopwatch": "~2.2" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/class-loader": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/finder": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\HttpKernel\\": "" } + }, + "target-dir": "Symfony/Component/HttpKernel", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-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..f8490c3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + 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..315c28a --- /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.phar install --dev + +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 Symfony2. 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..8c0ffc3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Collator/Collator.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\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 + */ +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". + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + */ + public function __construct($locale) + { + if ('en' != $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". + * + * @return Collator + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + */ + public static function create($locale) + { + return new self($locale); + } + + /** + * Sort array maintaining index association + * + * @param array &$array Input array + * @param integer $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 Boolean 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 Boolean|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 Boolean|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 Boolean|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 Boolean 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 Boolean 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 Boolean 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 Boolean 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/DateFormatter/DateFormat/AmPmTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.php new file mode 100644 index 0000000..1a9601d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for AM/PM markers format + * + * @author Igor Wiedler + */ +class AmPmTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + return $dateTime->format('A'); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 'AM|PM'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'marker' => $matched + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php new file mode 100644 index 0000000..ee53a4e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for day of week format + * + * @author Igor Wiedler + */ +class DayOfWeekTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $dayOfWeek = $dateTime->format('l'); + switch ($length) { + case 4: + return $dayOfWeek; + case 5: + return $dayOfWeek[0]; + default: + return substr($dayOfWeek, 0, 3); + } + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + switch ($length) { + case 4: + return 'Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday'; + case 5: + return '[MTWFS]'; + default: + return 'Mon|Tue|Wed|Thu|Fri|Sat|Sun'; + } + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php new file mode 100644 index 0000000..2c33888 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for day of year format + * + * @author Igor Wiedler + */ +class DayOfYearTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $dayOfYear = $dateTime->format('z') + 1; + + return $this->padLeft($dayOfYear, $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return '\d{'.$length.'}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php new file mode 100644 index 0000000..19d30e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for day format + * + * @author Igor Wiedler + */ +class DayTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + return $this->padLeft($dateTime->format('j'), $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'day' => (int) $matched, + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php new file mode 100644 index 0000000..b89db36 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.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\Intl\DateFormatter\DateFormat; + +use Symfony\Component\Intl\Exception\NotImplementedException; +use Symfony\Component\Intl\Globals\IntlGlobals; +use Symfony\Component\Intl\DateFormatter\DateFormat\MonthTransformer; + +/** + * Parser and formatter for date formats + * + * @author Igor Wiedler + */ +class FullTransformer +{ + private $quoteMatch = "'(?:[^']+|'')*'"; + private $implementedChars = 'MLydQqhDEaHkKmsz'; + private $notImplementedChars = 'GYuwWFgecSAZvVW'; + private $regExp; + + /** + * @var Transformer[] + */ + private $transformers; + + private $pattern; + private $timezone; + + /** + * Constructor + * + * @param string $pattern The pattern to be used to format and/or parse values + * @param string $timezone The timezone to perform the date/time calculations + */ + public function __construct($pattern, $timezone) + { + $this->pattern = $pattern; + $this->timezone = $timezone; + + $implementedCharsMatch = $this->buildCharsMatch($this->implementedChars); + $notImplementedCharsMatch = $this->buildCharsMatch($this->notImplementedChars); + $this->regExp = "/($this->quoteMatch|$implementedCharsMatch|$notImplementedCharsMatch)/"; + + $this->transformers = array( + 'M' => new MonthTransformer(), + 'L' => new MonthTransformer(), + 'y' => new YearTransformer(), + 'd' => new DayTransformer(), + 'q' => new QuarterTransformer(), + 'Q' => new QuarterTransformer(), + 'h' => new Hour1201Transformer(), + 'D' => new DayOfYearTransformer(), + 'E' => new DayOfWeekTransformer(), + 'a' => new AmPmTransformer(), + 'H' => new Hour2400Transformer(), + 'K' => new Hour1200Transformer(), + 'k' => new Hour2401Transformer(), + 'm' => new MinuteTransformer(), + 's' => new SecondTransformer(), + 'z' => new TimeZoneTransformer(), + ); + } + + /** + * Return the array of Transformer objects + * + * @return Transformer[] Associative array of Transformer objects (format char => Transformer) + */ + public function getTransformers() + { + return $this->transformers; + } + + /** + * Format a DateTime using ICU dateformat pattern + * + * @param \DateTime $dateTime A DateTime object to be used to generate the formatted value + * + * @return string The formatted value + */ + public function format(\DateTime $dateTime) + { + $that = $this; + + $formatted = preg_replace_callback($this->regExp, function($matches) use ($that, $dateTime) { + return $that->formatReplace($matches[0], $dateTime); + }, $this->pattern); + + return $formatted; + } + + /** + * Return the formatted ICU value for the matched date characters + * + * @param string $dateChars The date characters to be replaced with a formatted ICU value + * @param DateTime $dateTime A DateTime object to be used to generate the formatted value + * + * @return string The formatted value + * + * @throws NotImplementedException When it encounters a not implemented date character + */ + public function formatReplace($dateChars, $dateTime) + { + $length = strlen($dateChars); + + if ($this->isQuoteMatch($dateChars)) { + return $this->replaceQuoteMatch($dateChars); + } + + if (isset($this->transformers[$dateChars[0]])) { + $transformer = $this->transformers[$dateChars[0]]; + + return $transformer->format($dateTime, $length); + } + + // handle unimplemented characters + if (false !== strpos($this->notImplementedChars, $dateChars[0])) { + throw new NotImplementedException(sprintf("Unimplemented date character '%s' in format '%s'", $dateChars[0], $this->pattern)); + } + } + + /** + * Parse a pattern based string to a timestamp value + * + * @param \DateTime $dateTime A configured DateTime object to use to perform the date calculation + * @param string $value String to convert to a time value + * + * @return int The corresponding Unix timestamp + * + * @throws \InvalidArgumentException When the value can not be matched with pattern + */ + public function parse(\DateTime $dateTime, $value) + { + $reverseMatchingRegExp = $this->getReverseMatchingRegExp($this->pattern); + $reverseMatchingRegExp = '/^'.$reverseMatchingRegExp.'$/'; + + $options = array(); + + if (preg_match($reverseMatchingRegExp, $value, $matches)) { + $matches = $this->normalizeArray($matches); + + foreach ($this->transformers as $char => $transformer) { + if (isset($matches[$char])) { + $length = strlen($matches[$char]['pattern']); + $options = array_merge($options, $transformer->extractDateOptions($matches[$char]['value'], $length)); + } + } + + // reset error code and message + IntlGlobals::setError(IntlGlobals::U_ZERO_ERROR); + + return $this->calculateUnixTimestamp($dateTime, $options); + } + + // behave like the intl extension + IntlGlobals::setError(IntlGlobals::U_PARSE_ERROR, 'Date parsing failed'); + + return false; + } + + /** + * Retrieve a regular expression to match with a formatted value. + * + * @param string $pattern The pattern to create the reverse matching regular expression + * + * @return string The reverse matching regular expression with named captures being formed by the + * transformer index in the $transformer array + */ + public function getReverseMatchingRegExp($pattern) + { + $that = $this; + + $escapedPattern = preg_quote($pattern, '/'); + + // ICU 4.8 recognizes slash ("/") in a value to be parsed as a dash ("-") and vice-versa + // when parsing a date/time value + $escapedPattern = preg_replace('/\\\[\-|\/]/', '[\/\-]', $escapedPattern); + + $reverseMatchingRegExp = preg_replace_callback($this->regExp, function($matches) use ($that) { + $length = strlen($matches[0]); + $transformerIndex = $matches[0][0]; + + $dateChars = $matches[0]; + if ($that->isQuoteMatch($dateChars)) { + return $that->replaceQuoteMatch($dateChars); + } + + $transformers = $that->getTransformers(); + if (isset($transformers[$transformerIndex])) { + $transformer = $transformers[$transformerIndex]; + $captureName = str_repeat($transformerIndex, $length); + + return "(?P<$captureName>".$transformer->getReverseMatchingRegExp($length).')'; + } + }, $escapedPattern); + + return $reverseMatchingRegExp; + } + + /** + * Check if the first char of a string is a single quote + * + * @param string $quoteMatch The string to check + * + * @return Boolean true if matches, false otherwise + */ + public function isQuoteMatch($quoteMatch) + { + return ("'" === $quoteMatch[0]); + } + + /** + * Replaces single quotes at the start or end of a string with two single quotes + * + * @param string $quoteMatch The string to replace the quotes + * + * @return string A string with the single quotes replaced + */ + public function replaceQuoteMatch($quoteMatch) + { + if (preg_match("/^'+$/", $quoteMatch)) { + return str_replace("''", "'", $quoteMatch); + } + + return str_replace("''", "'", substr($quoteMatch, 1, -1)); + } + + /** + * Builds a chars match regular expression + * + * @param string $specialChars A string of chars to build the regular expression + * + * @return string The chars match regular expression + */ + protected function buildCharsMatch($specialChars) + { + $specialCharsArray = str_split($specialChars); + + $specialCharsMatch = implode('|', array_map(function($char) { + return $char.'+'; + }, $specialCharsArray)); + + return $specialCharsMatch; + } + + /** + * Normalize a preg_replace match array, removing the numeric keys and returning an associative array + * with the value and pattern values for the matched Transformer + * + * @param array $data + * + * @return array + */ + protected function normalizeArray(array $data) + { + $ret = array(); + + foreach ($data as $key => $value) { + if (!is_string($key)) { + continue; + } + + $ret[$key[0]] = array( + 'value' => $value, + 'pattern' => $key + ); + } + + return $ret; + } + + /** + * Calculates the Unix timestamp based on the matched values by the reverse matching regular + * expression of parse() + * + * @param \DateTime $dateTime The DateTime object to be used to calculate the timestamp + * @param array $options An array with the matched values to be used to calculate the timestamp + * + * @return Boolean|int The calculated timestamp or false if matched date is invalid + */ + protected function calculateUnixTimestamp(\DateTime $dateTime, array $options) + { + $options = $this->getDefaultValueForOptions($options); + + $year = $options['year']; + $month = $options['month']; + $day = $options['day']; + $hour = $options['hour']; + $hourInstance = $options['hourInstance']; + $minute = $options['minute']; + $second = $options['second']; + $marker = $options['marker']; + $timezone = $options['timezone']; + + // If month is false, return immediately (intl behavior) + if (false === $month) { + IntlGlobals::setError(IntlGlobals::U_PARSE_ERROR, 'Date parsing failed'); + + return false; + } + + // Normalize hour + if ($hourInstance instanceof HourTransformer) { + $hour = $hourInstance->normalizeHour($hour, $marker); + } + + // Set the timezone if different from the default one + if (null !== $timezone && $timezone !== $this->timezone) { + $dateTime->setTimezone(new \DateTimeZone($timezone)); + } + + // Normalize yy year + preg_match_all($this->regExp, $this->pattern, $matches); + if (in_array('yy', $matches[0])) { + $dateTime->setTimestamp(time()); + $year = $year > $dateTime->format('y') + 20 ? 1900 + $year : 2000 + $year; + } + + $dateTime->setDate($year, $month, $day); + $dateTime->setTime($hour, $minute, $second); + + return $dateTime->getTimestamp(); + } + + /** + * Add sensible default values for missing items in the extracted date/time options array. The values + * are base in the beginning of the Unix era + * + * @param array $options + * + * @return array + */ + private function getDefaultValueForOptions(array $options) + { + return array( + 'year' => isset($options['year']) ? $options['year'] : 1970, + 'month' => isset($options['month']) ? $options['month'] : 1, + 'day' => isset($options['day']) ? $options['day'] : 1, + 'hour' => isset($options['hour']) ? $options['hour'] : 0, + 'hourInstance' => isset($options['hourInstance']) ? $options['hourInstance'] : null, + 'minute' => isset($options['minute']) ? $options['minute'] : 0, + 'second' => isset($options['second']) ? $options['second'] : 0, + 'marker' => isset($options['marker']) ? $options['marker'] : null, + 'timezone' => isset($options['timezone']) ? $options['timezone'] : null, + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php new file mode 100644 index 0000000..8c8f5ef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.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\DateFormatter\DateFormat; + +/** + * Parser and formatter for 12 hour format (0-11) + * + * @author Igor Wiedler + */ +class Hour1200Transformer extends HourTransformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $hourOfDay = $dateTime->format('g'); + $hourOfDay = '12' == $hourOfDay ? '0' : $hourOfDay; + + return $this->padLeft($hourOfDay, $length); + } + + /** + * {@inheritDoc} + */ + public function normalizeHour($hour, $marker = null) + { + if ('PM' === $marker) { + $hour += 12; + } + + return $hour; + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return '\d{1,2}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'hour' => (int) $matched, + 'hourInstance' => $this + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php new file mode 100644 index 0000000..a8c4370 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.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\DateFormatter\DateFormat; + +/** + * Parser and formatter for 12 hour format (1-12) + * + * @author Igor Wiedler + */ +class Hour1201Transformer extends HourTransformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + return $this->padLeft($dateTime->format('g'), $length); + } + + /** + * {@inheritDoc} + */ + public function normalizeHour($hour, $marker = null) + { + if ('PM' !== $marker && 12 === $hour) { + $hour = 0; + } elseif ('PM' === $marker && 12 !== $hour) { + // If PM and hour is not 12 (1-12), sum 12 hour + $hour += 12; + } + + return $hour; + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return '\d{1,2}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'hour' => (int) $matched, + 'hourInstance' => $this + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php new file mode 100644 index 0000000..8f22da1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for 24 hour format (0-23) + * + * @author Igor Wiedler + */ +class Hour2400Transformer extends HourTransformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + return $this->padLeft($dateTime->format('G'), $length); + } + + /** + * {@inheritDoc} + */ + public function normalizeHour($hour, $marker = null) + { + if ('AM' == $marker) { + $hour = 0; + } elseif ('PM' == $marker) { + $hour = 12; + } + + return $hour; + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return '\d{1,2}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'hour' => (int) $matched, + 'hourInstance' => $this + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php new file mode 100644 index 0000000..b0f486b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for 24 hour format (1-24) + * + * @author Igor Wiedler + */ +class Hour2401Transformer extends HourTransformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $hourOfDay = $dateTime->format('G'); + $hourOfDay = ('0' == $hourOfDay) ? '24' : $hourOfDay; + + return $this->padLeft($hourOfDay, $length); + } + + /** + * {@inheritDoc} + */ + public function normalizeHour($hour, $marker = null) + { + if ((null === $marker && 24 === $hour) || 'AM' == $marker) { + $hour = 0; + } elseif ('PM' == $marker) { + $hour = 12; + } + + return $hour; + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return '\d{1,2}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'hour' => (int) $matched, + 'hourInstance' => $this + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php new file mode 100644 index 0000000..51097d9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.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\Intl\DateFormatter\DateFormat; + +/** + * Base class for hour transformers + * + * @author Eriksen Costa + */ +abstract class HourTransformer extends Transformer +{ + /** + * Returns a normalized hour value suitable for the hour transformer type + * + * @param int $hour The hour value + * @param string $marker An optional AM/PM marker + * + * @return int The normalized hour value + */ + abstract public function normalizeHour($hour, $marker = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php new file mode 100644 index 0000000..b48de29 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for minute format + * + * @author Igor Wiedler + */ +class MinuteTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $minuteOfHour = (int) $dateTime->format('i'); + + return $this->padLeft($minuteOfHour, $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'minute' => (int) $matched, + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php new file mode 100644 index 0000000..30c15af --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for month format + * + * @author Igor Wiedler + */ +class MonthTransformer extends Transformer +{ + /** + * @var array + */ + protected static $months = array( + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ); + + /** + * Short months names (first 3 letters) + * @var array + */ + protected static $shortMonths = array(); + + /** + * Flipped $months array, $name => $index + * @var array + */ + protected static $flippedMonths = array(); + + /** + * Flipped $shortMonths array, $name => $index + * @var array + */ + protected static $flippedShortMonths = array(); + + /** + * Constructor + */ + public function __construct() + { + if (0 === count(self::$shortMonths)) { + self::$shortMonths = array_map(function($month) { + return substr($month, 0, 3); + }, self::$months); + + self::$flippedMonths = array_flip(self::$months); + self::$flippedShortMonths = array_flip(self::$shortMonths); + } + } + + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $matchLengthMap = array( + 1 => 'n', + 2 => 'm', + 3 => 'M', + 4 => 'F', + ); + + if (isset($matchLengthMap[$length])) { + return $dateTime->format($matchLengthMap[$length]); + } + + if (5 === $length) { + return substr($dateTime->format('M'), 0, 1); + } + + return $this->padLeft($dateTime->format('m'), $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + switch ($length) { + case 1: + $regExp = '\d{1,2}'; + break; + case 3: + $regExp = implode('|', self::$shortMonths); + break; + case 4: + $regExp = implode('|', self::$months); + break; + case 5: + $regExp = '[JFMASOND]'; + break; + default: + $regExp = '\d{'.$length.'}'; + break; + } + + return $regExp; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + if (!is_numeric($matched)) { + if (3 === $length) { + $matched = self::$flippedShortMonths[$matched] + 1; + } elseif (4 === $length) { + $matched = self::$flippedMonths[$matched] + 1; + } elseif (5 === $length) { + // IntlDateFormatter::parse() always returns false for MMMMM or LLLLL + $matched = false; + } + } else { + $matched = (int) $matched; + } + + return array( + 'month' => $matched, + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php new file mode 100644 index 0000000..8e83dc7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for quarter format + * + * @author Igor Wiedler + */ +class QuarterTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $month = (int) $dateTime->format('n'); + $quarter = (int) floor(($month - 1) / 3) + 1; + switch ($length) { + case 1: + case 2: + return $this->padLeft($quarter, $length); + case 3: + return 'Q'.$quarter; + default: + $map = array(1 => '1st quarter', 2 => '2nd quarter', 3 => '3rd quarter', 4 => '4th quarter'); + + return $map[$quarter]; + } + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + switch ($length) { + case 1: + case 2: + return '\d{'.$length.'}'; + case 3: + return 'Q\d'; + default: + return '(?:1st|2nd|3rd|4th) quarter'; + } + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php new file mode 100644 index 0000000..ccbcdb4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for the second format + * + * @author Igor Wiedler + */ +class SecondTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + $secondOfMinute = (int) $dateTime->format('s'); + + return $this->padLeft($secondOfMinute, $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'second' => (int) $matched, + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/TimeZoneTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/TimeZoneTransformer.php new file mode 100644 index 0000000..7d74bd3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/TimeZoneTransformer.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\Intl\DateFormatter\DateFormat; + +use Symfony\Component\Intl\Exception\NotImplementedException; + +/** + * Parser and formatter for time zone format + * + * @author Igor Wiedler + */ +class TimeZoneTransformer extends Transformer +{ + /** + * {@inheritDoc} + * + * @throws NotImplementedException When time zone is different than UTC or GMT (Etc/GMT) + */ + public function format(\DateTime $dateTime, $length) + { + $timeZone = substr($dateTime->getTimezone()->getName(), 0, 3); + + if (!in_array($timeZone, array('Etc', 'UTC'))) { + throw new NotImplementedException('Time zone different than GMT or UTC is not supported as a formatting output.'); + } + + // From ICU >= 4.8, the zero offset is not more used, example: GMT instead of GMT+00:00 + $format = (0 !== (int) $dateTime->format('O')) ? '\G\M\TP' : '\G\M\T'; + + return $dateTime->format($format); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 'GMT[+-]\d{2}:?\d{2}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'timezone' => self::getEtcTimeZoneId($matched) + ); + } + + /** + * Get an Etc/GMT timezone identifier for the specified timezone + * + * The PHP documentation for timezones states to not use the 'Other' time zones because them exists + * "for backwards compatibility". However all Etc/GMT time zones are in the tz database 'etcetera' file, + * which indicates they are not deprecated (neither are old names). + * + * Only GMT, Etc/Universal, Etc/Zulu, Etc/Greenwich, Etc/GMT-0, Etc/GMT+0 and Etc/GMT0 are old names and + * are linked to Etc/GMT or Etc/UTC. + * + * @param string $formattedTimeZone A GMT timezone string (GMT-03:00, e.g.) + * + * @return string A timezone identifier + * + * @see http://php.net/manual/en/timezones.others.php + * @see http://www.twinsun.com/tz/tz-link.htm + * + * @throws NotImplementedException When the GMT time zone have minutes offset different than zero + * @throws \InvalidArgumentException When the value can not be matched with pattern + */ + public static function getEtcTimeZoneId($formattedTimeZone) + { + if (preg_match('/GMT(?P[+-])(?P\d{2}):?(?P\d{2})/', $formattedTimeZone, $matches)) { + $hours = (int) $matches['hours']; + $minutes = (int) $matches['minutes']; + $signal = $matches['signal'] == '-' ? '+' : '-'; + + if (0 < $minutes) { + throw new NotImplementedException(sprintf( + 'It is not possible to use a GMT time zone with minutes offset different than zero (0). GMT time zone tried: %s.', + $formattedTimeZone + )); + } + + return 'Etc/GMT'.($hours !== 0 ? $signal.$hours : ''); + } + + throw new \InvalidArgumentException('The GMT time zone \'%s\' does not match with the supported formats GMT[+-]HH:MM or GMT[+-]HHMM.'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php new file mode 100644 index 0000000..0e67f8a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for date formats + * + * @author Igor Wiedler + */ +abstract class Transformer +{ + /** + * Format a value using a configured DateTime as date/time source + * + * + * @param \DateTime $dateTime A DateTime object to be used to generate the formatted value + * @param int $length The formatted value string length + * + * @return string The formatted value + */ + abstract public function format(\DateTime $dateTime, $length); + + /** + * Returns a reverse matching regular expression of a string generated by format() + * + * @param int $length The length of the value to be reverse matched + * + * @return string The reverse matching regular expression + */ + abstract public function getReverseMatchingRegExp($length); + + /** + * Extract date options from a matched value returned by the processing of the reverse matching + * regular expression + * + * @param string $matched The matched value + * @param int $length The length of the Transformer pattern string + * + * @return array An associative array + */ + abstract public function extractDateOptions($matched, $length); + + /** + * Pad a string with zeros to the left + * + * @param string $value The string to be padded + * @param int $length The length to pad + * + * @return string The padded string + */ + protected function padLeft($value, $length) + { + return str_pad($value, $length, '0', STR_PAD_LEFT); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php new file mode 100644 index 0000000..c3bd699 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.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\Intl\DateFormatter\DateFormat; + +/** + * Parser and formatter for year format + * + * @author Igor Wiedler + */ +class YearTransformer extends Transformer +{ + /** + * {@inheritDoc} + */ + public function format(\DateTime $dateTime, $length) + { + if (2 === $length) { + return $dateTime->format('y'); + } + + return $this->padLeft($dateTime->format('Y'), $length); + } + + /** + * {@inheritDoc} + */ + public function getReverseMatchingRegExp($length) + { + return 2 === $length ? '\d{2}' : '\d{4}'; + } + + /** + * {@inheritDoc} + */ + public function extractDateOptions($matched, $length) + { + return array( + 'year' => (int) $matched, + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php new file mode 100644 index 0000000..33a499a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php @@ -0,0 +1,631 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\DateFormatter; + +use Symfony\Component\Intl\Globals\IntlGlobals; +use Symfony\Component\Intl\DateFormatter\DateFormat\FullTransformer; +use Symfony\Component\Intl\Exception\MethodNotImplementedException; +use Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException; +use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException; +use Symfony\Component\Intl\Locale\Locale; + +/** + * Replacement for PHP's native {@link \IntlDateFormatter} class. + * + * The only methods currently supported in this class are: + * + * - {@link __construct} + * - {@link create} + * - {@link format} + * - {@link getCalendar} + * - {@link getDateType} + * - {@link getErrorCode} + * - {@link getErrorMessage} + * - {@link getLocale} + * - {@link getPattern} + * - {@link getTimeType} + * - {@link getTimeZoneId} + * - {@link isLenient} + * - {@link parse} + * - {@link setLenient} + * - {@link setPattern} + * - {@link setTimeZoneId} + * - {@link setTimeZone} + * + * @author Igor Wiedler + * @author Bernhard Schussek + */ +class IntlDateFormatter +{ + /** + * The error code from the last operation + * + * @var integer + */ + protected $errorCode = IntlGlobals::U_ZERO_ERROR; + + /** + * The error message from the last operation + * + * @var string + */ + protected $errorMessage = 'U_ZERO_ERROR'; + + /* date/time format types */ + const NONE = -1; + const FULL = 0; + const LONG = 1; + const MEDIUM = 2; + const SHORT = 3; + + /* calendar formats */ + const TRADITIONAL = 0; + const GREGORIAN = 1; + + /** + * Patterns used to format the date when no pattern is provided + * + * @var array + */ + private $defaultDateFormats = array( + self::NONE => '', + self::FULL => 'EEEE, LLLL d, y', + self::LONG => 'LLLL d, y', + self::MEDIUM => 'LLL d, y', + self::SHORT => 'M/d/yy', + ); + + /** + * Patterns used to format the time when no pattern is provided + * + * @var array + */ + private $defaultTimeFormats = array( + self::FULL => 'h:mm:ss a zzzz', + self::LONG => 'h:mm:ss a z', + self::MEDIUM => 'h:mm:ss a', + self::SHORT => 'h:mm a', + ); + + /** + * @var int + */ + private $datetype; + + /** + * @var int + */ + private $timetype; + + /** + * @var string + */ + private $pattern; + + /** + * @var \DateTimeZone + */ + private $dateTimeZone; + + /** + * @var Boolean + */ + private $unitializedTimeZoneId = false; + + /** + * @var string + */ + private $timeZoneId; + + /** + * Constructor + * + * @param string $locale The locale code. The only currently supported locale is "en". + * @param int $datetype Type of date formatting, one of the format type constants + * @param int $timetype Type of time formatting, one of the format type constants + * @param string $timezone Timezone identifier + * @param int $calendar Calendar to use for formatting or parsing. The only currently + * supported value is IntlDateFormatter::GREGORIAN. + * @param string $pattern Optional pattern to use when formatting + * + * @see http://www.php.net/manual/en/intldateformatter.create.php + * @see http://userguide.icu-project.org/formatparse/datetime + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + * @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed + */ + public function __construct($locale, $datetype, $timetype, $timezone = null, $calendar = self::GREGORIAN, $pattern = null) + { + if ('en' !== $locale) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); + } + + if (self::GREGORIAN !== $calendar) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'calendar', $calendar, 'Only the GREGORIAN calendar is supported'); + } + + $this->datetype = $datetype; + $this->timetype = $timetype; + + $this->setPattern($pattern); + $this->setTimeZoneId($timezone); + } + + /** + * Static constructor + * + * @param string $locale The locale code. The only currently supported locale is "en". + * @param int $datetype Type of date formatting, one of the format type constants + * @param int $timetype Type of time formatting, one of the format type constants + * @param string $timezone Timezone identifier + * @param int $calendar Calendar to use for formatting or parsing; default is Gregorian. + * One of the calendar constants. + * @param string $pattern Optional pattern to use when formatting + * + * @return IntlDateFormatter + * + * @see http://www.php.net/manual/en/intldateformatter.create.php + * @see http://userguide.icu-project.org/formatparse/datetime + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + * @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed + */ + public static function create($locale, $datetype, $timetype, $timezone = null, $calendar = self::GREGORIAN, $pattern = null) + { + return new self($locale, $datetype, $timetype, $timezone, $calendar, $pattern); + } + + /** + * Format the date/time value (timestamp) as a string + * + * @param integer|\DateTime $timestamp The timestamp to format. \DateTime objects + * are supported as of PHP 5.3.4. + * + * @return string|Boolean The formatted value or false if formatting failed. + * + * @see http://www.php.net/manual/en/intldateformatter.format.php + * + * @throws MethodArgumentValueNotImplementedException If one of the formatting characters is not implemented + */ + public function format($timestamp) + { + // intl allows timestamps to be passed as arrays - we don't + if (is_array($timestamp)) { + $message = version_compare(PHP_VERSION, '5.3.4', '>=') ? + 'Only integer unix timestamps and DateTime objects are supported' : + 'Only integer unix timestamps are supported'; + + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'timestamp', $timestamp, $message); + } + + // behave like the intl extension + $argumentError = null; + if (version_compare(PHP_VERSION, '5.3.4', '<') && !is_int($timestamp)) { + $argumentError = 'datefmt_format: takes either an array or an integer timestamp value '; + } elseif (version_compare(PHP_VERSION, '5.3.4', '>=') && !is_int($timestamp) && !$timestamp instanceof \DateTime) { + $argumentError = 'datefmt_format: takes either an array or an integer timestamp value or a DateTime object'; + if (version_compare(PHP_VERSION, '5.5.0-dev', '>=') && !is_int($timestamp)) { + $argumentError = sprintf('datefmt_format: string \'%s\' is not numeric, which would be required for it to be a valid date', $timestamp); + } + } + + if (null !== $argumentError) { + IntlGlobals::setError(IntlGlobals::U_ILLEGAL_ARGUMENT_ERROR, $argumentError); + $this->errorCode = IntlGlobals::getErrorCode(); + $this->errorMessage = IntlGlobals::getErrorMessage(); + + return false; + } + + // As of PHP 5.3.4, IntlDateFormatter::format() accepts DateTime instances + if (version_compare(PHP_VERSION, '5.3.4', '>=') && $timestamp instanceof \DateTime) { + $timestamp = $timestamp->getTimestamp(); + } + + $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId()); + $formatted = $transformer->format($this->createDateTime($timestamp)); + + // behave like the intl extension + IntlGlobals::setError(IntlGlobals::U_ZERO_ERROR); + $this->errorCode = IntlGlobals::getErrorCode(); + $this->errorMessage = IntlGlobals::getErrorMessage(); + + return $formatted; + } + + /** + * Not supported. Formats an object + * + * @param object $object + * @param mixed $format + * @param string $locale + * + * @return string The formatted value + * + * @see http://www.php.net/manual/en/intldateformatter.formatobject.php + * + * @throws MethodNotImplementedException + */ + public function formatObject($object, $format = null, $locale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Returns the formatter's calendar + * + * @return int The calendar being used by the formatter. Currently always returns + * IntlDateFormatter::GREGORIAN. + * + * @see http://www.php.net/manual/en/intldateformatter.getcalendar.php + */ + public function getCalendar() + { + return self::GREGORIAN; + } + + /** + * Not supported. Returns the formatter's calendar object + * + * @return object The calendar's object being used by the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.getcalendarobject.php + * + * @throws MethodNotImplementedException + */ + public function getCalendarObject() + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Returns the formatter's datetype + * + * @return int The current value of the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.getdatetype.php + */ + public function getDateType() + { + return $this->datetype; + } + + /** + * Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value + * + * @return int The error code from last formatter call + * + * @see http://www.php.net/manual/en/intldateformatter.geterrorcode.php + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value + * + * @return string The error message from last formatter call + * + * @see http://www.php.net/manual/en/intldateformatter.geterrormessage.php + */ + public function getErrorMessage() + { + return $this->errorMessage; + } + + /** + * Returns the formatter'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 formatter. Currently always + * returns "en". + * + * @see http://www.php.net/manual/en/intldateformatter.getlocale.php + */ + public function getLocale($type = Locale::ACTUAL_LOCALE) + { + return 'en'; + } + + /** + * Returns the formatter's pattern + * + * @return string The pattern string used by the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.getpattern.php + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * Returns the formatter's time type + * + * @return string The time type used by the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.gettimetype.php + */ + public function getTimeType() + { + return $this->timetype; + } + + /** + * Returns the formatter's timezone identifier + * + * @return string The timezone identifier used by the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.gettimezoneid.php + */ + public function getTimeZoneId() + { + if (!$this->unitializedTimeZoneId) { + return $this->timeZoneId; + } + + // In PHP 5.5 default timezone depends on `date_default_timezone_get()` method + if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) { + return date_default_timezone_get(); + } + + return null; + } + + /** + * Not supported. Returns the formatter's timezone + * + * @return mixed The timezone used by the formatter + * + * @see http://www.php.net/manual/en/intldateformatter.gettimezone.php + * + * @throws MethodNotImplementedException + */ + public function getTimeZone() + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Returns whether the formatter is lenient + * + * @return Boolean Currently always returns false. + * + * @see http://www.php.net/manual/en/intldateformatter.islenient.php + * + * @throws MethodNotImplementedException + */ + public function isLenient() + { + return false; + } + + /** + * Not supported. Parse string to a field-based time value + * + * @param string $value String to convert to a time value + * @param int $position Position at which to start the parsing in $value (zero-based). + * If no error occurs before $value is consumed, $parse_pos will + * contain -1 otherwise it will contain the position at which parsing + * ended. If $parse_pos > strlen($value), the parse fails immediately. + * + * @return string Localtime compatible array of integers: contains 24 hour clock value in tm_hour field + * + * @see http://www.php.net/manual/en/intldateformatter.localtime.php + * + * @throws MethodNotImplementedException + */ + public function localtime($value, &$position = 0) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Parse string to a timestamp value + * + * @param string $value String to convert to a time value + * @param int $position Not supported. Position at which to start the parsing in $value (zero-based). + * If no error occurs before $value is consumed, $parse_pos will + * contain -1 otherwise it will contain the position at which parsing + * ended. If $parse_pos > strlen($value), the parse fails immediately. + * + * @return string Parsed value as a timestamp + * + * @see http://www.php.net/manual/en/intldateformatter.parse.php + * + * @throws MethodArgumentNotImplementedException When $position different than null, behavior not implemented + */ + public function parse($value, &$position = null) + { + // We don't calculate the position when parsing the value + if (null !== $position) { + throw new MethodArgumentNotImplementedException(__METHOD__, 'position'); + } + + $dateTime = $this->createDateTime(0); + $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId()); + + $timestamp = $transformer->parse($dateTime, $value); + + // behave like the intl extension. FullTransformer::parse() set the proper error + $this->errorCode = IntlGlobals::getErrorCode(); + $this->errorMessage = IntlGlobals::getErrorMessage(); + + return $timestamp; + } + + /** + * Not supported. Set the formatter's calendar + * + * @param string $calendar The calendar to use. Default is IntlDateFormatter::GREGORIAN. + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/intldateformatter.setcalendar.php + * + * @throws MethodNotImplementedException + */ + public function setCalendar($calendar) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Set the leniency of the parser + * + * Define if the parser is strict or lenient in interpreting inputs that do not match the pattern + * exactly. Enabling lenient parsing allows the parser to accept otherwise flawed date or time + * patterns, parsing as much as possible to obtain a value. Extra space, unrecognized tokens, or + * invalid values ("February 30th") are not accepted. + * + * @param Boolean $lenient Sets whether the parser is lenient or not. Currently + * only false (strict) is supported. + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/intldateformatter.setlenient.php + * + * @throws MethodArgumentValueNotImplementedException When $lenient is true + */ + public function setLenient($lenient) + { + if ($lenient) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'lenient', $lenient, 'Only the strict parser is supported'); + } + + return true; + } + + /** + * Set the formatter's pattern + * + * @param string $pattern A pattern string in conformance with the ICU IntlDateFormatter documentation + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/intldateformatter.setpattern.php + * @see http://userguide.icu-project.org/formatparse/datetime + */ + public function setPattern($pattern) + { + if (null === $pattern) { + $pattern = $this->getDefaultPattern(); + } + + $this->pattern = $pattern; + + return true; + } + + /** + * Set the formatter's timezone identifier + * + * @param string $timeZoneId The time zone ID string of the time zone to use. + * If NULL or the empty string, the default time zone for the + * runtime is used. + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/intldateformatter.settimezoneid.php + */ + public function setTimeZoneId($timeZoneId) + { + if (null === $timeZoneId) { + // In PHP 5.5 if $timeZoneId is null it fallbacks to `date_default_timezone_get()` method + if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) { + $timeZoneId = date_default_timezone_get(); + } else { + // TODO: changes were made to ext/intl in PHP 5.4.4 release that need to be investigated since it will + // use ini's date.timezone when the time zone is not provided. As a not well tested workaround, uses UTC. + // See the first two items of the commit message for more information: + // https://github.com/php/php-src/commit/eb346ef0f419b90739aadfb6cc7b7436c5b521d9 + $timeZoneId = getenv('TZ') ?: 'UTC'; + } + + $this->unitializedTimeZoneId = true; + } + + // Backup original passed time zone + $timeZone = $timeZoneId; + + // Get an Etc/GMT time zone that is accepted for \DateTimeZone + if ('GMT' !== $timeZoneId && 0 === strpos($timeZoneId, 'GMT')) { + try { + $timeZoneId = DateFormat\TimeZoneTransformer::getEtcTimeZoneId($timeZoneId); + } catch (\InvalidArgumentException $e) { + // Does nothing, will fallback to UTC + } + } + + try { + $this->dateTimeZone = new \DateTimeZone($timeZoneId); + } catch (\Exception $e) { + $this->dateTimeZone = new \DateTimeZone('UTC'); + } + + $this->timeZoneId = $timeZone; + + return true; + } + + /** + * This method was added in PHP 5.5 as replacement for `setTimeZoneId()` + * + * @param mixed $timeZone + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/intldateformatter.settimezone.php + */ + public function setTimeZone($timeZone) + { + return $this->setTimeZoneId($timeZone); + } + + /** + * Create and returns a DateTime object with the specified timestamp and with the + * current time zone + * + * @param int $timestamp + * + * @return \DateTime + */ + protected function createDateTime($timestamp) + { + $dateTime = new \DateTime(); + $dateTime->setTimestamp($timestamp); + $dateTime->setTimezone($this->dateTimeZone); + + return $dateTime; + } + + /** + * Returns a pattern string based in the datetype and timetype values + * + * @return string + */ + protected function getDefaultPattern() + { + $patternParts = array(); + if (self::NONE !== $this->datetype) { + $patternParts[] = $this->defaultDateFormats[$this->datetype]; + } + if (self::NONE !== $this->timetype) { + $patternParts[] = $this->defaultTimeFormats[$this->timetype]; + } + $pattern = implode(' ', $patternParts); + + return $pattern; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/BadMethodCallException.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/BadMethodCallException.php new file mode 100644 index 0000000..ca79729 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/BadMethodCallException.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\Intl\Exception; + +/** + * Base BadMethodCallException for the Intl component. + * + * @author Bernhard Schussek + */ +class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/ExceptionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/ExceptionInterface.php new file mode 100644 index 0000000..4fc076c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/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\Intl\Exception; + +/** + * Base ExceptionInterface for the Intl component. + * + * @author Bernhard Schussek + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/InvalidArgumentException.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..10f69ee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/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\Intl\Exception; + +/** + * InvalidArgumentException for the Intl component. + * + * @author Bernhard Schussek + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php new file mode 100644 index 0000000..570609d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.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\Intl\Exception; + +use Symfony\Component\Intl\Exception\NotImplementedException; + +/** + * @author Eriksen Costa + */ +class MethodArgumentNotImplementedException extends NotImplementedException +{ + /** + * Constructor + * + * @param string $methodName The method name that raised the exception + * @param string $argName The argument name that is not implemented + */ + public function __construct($methodName, $argName) + { + $message = sprintf('The %s() method\'s argument $%s behavior is not implemented.', $methodName, $argName); + parent::__construct($message); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php new file mode 100644 index 0000000..76e3f63 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.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\Intl\Exception; + +use Symfony\Component\Intl\Exception\NotImplementedException; + +/** + * @author Eriksen Costa + */ +class MethodArgumentValueNotImplementedException extends NotImplementedException +{ + /** + * Constructor + * + * @param string $methodName The method name that raised the exception + * @param string $argName The argument name + * @param string $argValue The argument value that is not implemented + * @param string $additionalMessage An optional additional message to append to the exception message + */ + public function __construct($methodName, $argName, $argValue, $additionalMessage = '') + { + $message = sprintf( + 'The %s() method\'s argument $%s value %s behavior is not implemented.%s', + $methodName, + $argName, + var_export($argValue, true), + $additionalMessage !== '' ? ' '.$additionalMessage.'. ' : '' + ); + + parent::__construct($message); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/MethodNotImplementedException.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/MethodNotImplementedException.php new file mode 100644 index 0000000..d8a0e90 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/MethodNotImplementedException.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\Intl\Exception; + +/** + * @author Eriksen Costa + */ +class MethodNotImplementedException extends NotImplementedException +{ + /** + * Constructor + * + * @param string $methodName The name of the method + */ + public function __construct($methodName) + { + parent::__construct(sprintf('The %s() is not implemented.', $methodName)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/NotImplementedException.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/NotImplementedException.php new file mode 100644 index 0000000..1f3ba46 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/NotImplementedException.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\Intl\Exception; + +/** + * Base exception class for not implemented behaviors of the intl extension in the Locale component. + * + * @author Eriksen Costa + */ +class NotImplementedException extends RuntimeException +{ + const INTL_INSTALL_MESSAGE = 'Please install the "intl" extension for full localization capabilities.'; + + /** + * Constructor + * + * @param string $message The exception message. A note to install the intl extension is appended to this string + */ + public function __construct($message) + { + parent::__construct($message.' '.self::INTL_INSTALL_MESSAGE); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/OutOfBoundsException.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/OutOfBoundsException.php new file mode 100644 index 0000000..2727141 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/OutOfBoundsException.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\Intl\Exception; + +/** + * Base OutOfBoundsException for the Intl component. + * + * @author Bernhard Schussek + */ +class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/RuntimeException.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Exception/RuntimeException.php new file mode 100644 index 0000000..9893714 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/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\Intl\Exception; + +/** + * RuntimeException for the Intl component. + * + * @author Bernhard Schussek + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Globals/IntlGlobals.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Globals/IntlGlobals.php new file mode 100644 index 0000000..6da0001 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Globals/IntlGlobals.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\Intl\Globals; + +/** + * Provides fake static versions of the global functions in the intl extension + * + * @author Bernhard Schussek + */ +abstract class IntlGlobals +{ + /** + * Indicates that no error occurred + * + * @var integer + */ + const U_ZERO_ERROR = 0; + + /** + * Indicates that an invalid argument was passed + * + * @var integer + */ + const U_ILLEGAL_ARGUMENT_ERROR = 1; + + /** + * Indicates that the parse() operation failed + * + * @var integer + */ + const U_PARSE_ERROR = 9; + + /** + * All known error codes + * + * @var array + */ + private static $errorCodes = array( + self::U_ZERO_ERROR => 'U_ZERO_ERROR', + self::U_ILLEGAL_ARGUMENT_ERROR => 'U_ILLEGAL_ARGUMENT_ERROR', + self::U_PARSE_ERROR => 'U_PARSE_ERROR', + ); + + /** + * The error code of the last operation + * + * @var integer + */ + private static $errorCode = self::U_ZERO_ERROR; + + /** + * The error code of the last operation + * + * @var integer + */ + private static $errorMessage = 'U_ZERO_ERROR'; + + /** + * Returns whether the error code indicates a failure + * + * @param integer $errorCode The error code returned by IntlGlobals::getErrorCode() + * + * @return Boolean + */ + public static function isFailure($errorCode) + { + return isset(self::$errorCodes[$errorCode]) + && $errorCode > self::U_ZERO_ERROR; + } + + /** + * Returns the error code of the last operation + * + * Returns IntlGlobals::U_ZERO_ERROR if no error occurred. + * + * @return integer + */ + public static function getErrorCode() + { + return self::$errorCode; + } + + /** + * Returns the error message of the last operation + * + * Returns "U_ZERO_ERROR" if no error occurred. + * + * @return string + */ + public static function getErrorMessage() + { + return self::$errorMessage; + } + + /** + * Returns the symbolic name for a given error code + * + * @param integer $code The error code returned by IntlGlobals::getErrorCode() + * + * @return string + */ + public static function getErrorName($code) + { + if (isset(self::$errorCodes[$code])) { + return self::$errorCodes[$code]; + } + + return '[BOGUS UErrorCode]'; + } + + /** + * Sets the current error + * + * @param integer $code One of the error constants in this class + * @param string $message The ICU class error message + * + * @throws \InvalidArgumentException If the code is not one of the error constants in this class + */ + public static function setError($code, $message = '') + { + if (!isset(self::$errorCodes[$code])) { + throw new \InvalidArgumentException(sprintf('No such error code: "%s"', $code)); + } + + self::$errorMessage = $message ? sprintf('%s: %s', $message, self::$errorCodes[$code]) : self::$errorCodes[$code]; + self::$errorCode = $code; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Intl.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Intl.php new file mode 100644 index 0000000..71ffca6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Intl.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl; + +use Symfony\Component\Icu\IcuCurrencyBundle; +use Symfony\Component\Icu\IcuData; +use Symfony\Component\Icu\IcuLanguageBundle; +use Symfony\Component\Icu\IcuLocaleBundle; +use Symfony\Component\Icu\IcuRegionBundle; +use Symfony\Component\Intl\Exception\InvalidArgumentException; +use Symfony\Component\Intl\ResourceBundle\Reader\BinaryBundleReader; +use Symfony\Component\Intl\ResourceBundle\Reader\BufferedBundleReader; +use Symfony\Component\Intl\ResourceBundle\Reader\PhpBundleReader; +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReader; +use Symfony\Component\Intl\ResourceBundle\Stub\StubCurrencyBundle; +use Symfony\Component\Intl\ResourceBundle\Stub\StubLanguageBundle; +use Symfony\Component\Intl\ResourceBundle\Stub\StubLocaleBundle; +use Symfony\Component\Intl\ResourceBundle\Stub\StubRegionBundle; + +/** + * Gives access to internationalization data. + * + * @author Bernhard Schussek + */ +class Intl +{ + /** + * The number of resource bundles to buffer. Loading the same resource + * bundle for n locales takes up n spots in the buffer. + */ + const BUFFER_SIZE = 10; + + /** + * @var ResourceBundle\CurrencyBundleInterface + */ + private static $currencyBundle; + + /** + * @var ResourceBundle\LanguageBundleInterface + */ + private static $languageBundle; + + /** + * @var ResourceBundle\LocaleBundleInterface + */ + private static $localeBundle; + + /** + * @var ResourceBundle\RegionBundleInterface + */ + private static $regionBundle; + + /** + * @var string|Boolean|null + */ + private static $icuVersion = false; + + /** + * @var string + */ + private static $icuDataVersion = false; + + /** + * @var ResourceBundle\Reader\StructuredBundleReaderInterface + */ + private static $bundleReader; + + /** + * Returns whether the intl extension is installed. + * + * @return Boolean Returns true if the intl extension is installed, false otherwise. + */ + public static function isExtensionLoaded() + { + return class_exists('\ResourceBundle'); + } + + /** + * Returns the bundle containing currency information. + * + * @return ResourceBundle\CurrencyBundleInterface The currency resource bundle. + */ + public static function getCurrencyBundle() + { + if (null === self::$currencyBundle) { + self::$currencyBundle = new IcuCurrencyBundle(self::getBundleReader()); + } + + return self::$currencyBundle; + } + + /** + * Returns the bundle containing language information. + * + * @return ResourceBundle\LanguageBundleInterface The language resource bundle. + */ + public static function getLanguageBundle() + { + if (null === self::$languageBundle) { + self::$languageBundle = new IcuLanguageBundle(self::getBundleReader()); + } + + return self::$languageBundle; + } + + /** + * Returns the bundle containing locale information. + * + * @return ResourceBundle\LocaleBundleInterface The locale resource bundle. + */ + public static function getLocaleBundle() + { + if (null === self::$localeBundle) { + self::$localeBundle = new IcuLocaleBundle(self::getBundleReader()); + } + + return self::$localeBundle; + } + + /** + * Returns the bundle containing region information. + * + * @return ResourceBundle\RegionBundleInterface The region resource bundle. + */ + public static function getRegionBundle() + { + if (null === self::$regionBundle) { + self::$regionBundle = new IcuRegionBundle(self::getBundleReader()); + } + + return self::$regionBundle; + } + + /** + * Returns the version of the installed ICU library. + * + * @return null|string The ICU version or NULL if it could not be determined. + */ + public static function getIcuVersion() + { + if (false === self::$icuVersion) { + if (!self::isExtensionLoaded()) { + self::$icuVersion = self::getIcuStubVersion(); + } elseif (defined('INTL_ICU_VERSION')) { + self::$icuVersion = INTL_ICU_VERSION; + } else { + try { + $reflector = new \ReflectionExtension('intl'); + ob_start(); + $reflector->info(); + $output = strip_tags(ob_get_clean()); + preg_match('/^ICU version (?:=>)?(.*)$/m', $output, $matches); + + self::$icuVersion = trim($matches[1]); + } catch (\ReflectionException $e) { + self::$icuVersion = null; + } + } + } + + return self::$icuVersion; + } + + /** + * Returns the version of the installed ICU data. + * + * @return string The version of the installed ICU data. + */ + public static function getIcuDataVersion() + { + if (false === self::$icuDataVersion) { + self::$icuDataVersion = IcuData::getVersion(); + } + + return self::$icuDataVersion; + } + + /** + * Returns the ICU version that the stub classes mimic. + * + * @return string The ICU version of the stub classes. + */ + public static function getIcuStubVersion() + { + return '50.1.2'; + } + + /** + * Returns a resource bundle reader for .php resource bundle files. + * + * @return ResourceBundle\Reader\StructuredBundleReaderInterface The resource reader. + */ + private static function getBundleReader() + { + if (null === self::$bundleReader) { + self::$bundleReader = new StructuredBundleReader(new BufferedBundleReader( + IcuData::getBundleReader(), + self::BUFFER_SIZE + )); + } + + return self::$bundleReader; + } + + /** + * This class must not be instantiated. + */ + private function __construct() {} +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/Intl/LICENSE new file mode 100644 index 0000000..88a57f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/symfony/symfony/src/Symfony/Component/Intl/Locale/Locale.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Locale/Locale.php new file mode 100644 index 0000000..1217f97 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Locale/Locale.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\Intl\Locale; + +use Symfony\Component\Intl\Exception\NotImplementedException; +use Symfony\Component\Intl\Exception\MethodNotImplementedException; + +/** + * Replacement for PHP's native {@link \Locale} class. + * + * The only method supported in this class is {@link getDefault}. This method + * will always return "en". All other methods will throw an exception when used. + * + * @author Eriksen Costa + * @author Bernhard Schussek + */ +class Locale +{ + const DEFAULT_LOCALE = null; + + /* Locale method constants */ + const ACTUAL_LOCALE = 0; + const VALID_LOCALE = 1; + + /* Language tags constants */ + const LANG_TAG = 'language'; + const EXTLANG_TAG = 'extlang'; + const SCRIPT_TAG = 'script'; + const REGION_TAG = 'region'; + const VARIANT_TAG = 'variant'; + const GRANDFATHERED_LANG_TAG = 'grandfathered'; + const PRIVATE_TAG = 'private'; + + /** + * Not supported. Returns the best available locale based on HTTP "Accept-Language" header according to RFC 2616 + * + * @param string $header The string containing the "Accept-Language" header value + * + * @return string The corresponding locale code + * + * @see http://www.php.net/manual/en/locale.acceptfromhttp.php + * + * @throws MethodNotImplementedException + */ + public static function acceptFromHttp($header) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns a correctly ordered and delimited locale code + * + * @param array $subtags A keyed array where the keys identify the particular locale code subtag + * + * @return string The corresponding locale code + * + * @see http://www.php.net/manual/en/locale.composelocale.php + * + * @throws MethodNotImplementedException + */ + public static function composeLocale(array $subtags) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Checks if a language tag filter matches with locale + * + * @param string $langtag The language tag to check + * @param string $locale The language range to check against + * @param Boolean $canonicalize + * + * @return string The corresponding locale code + * + * @see http://www.php.net/manual/en/locale.filtermatches.php + * + * @throws MethodNotImplementedException + */ + public static function filterMatches($langtag, $locale, $canonicalize = false) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the variants for the input locale + * + * @param string $locale The locale to extract the variants from + * + * @return array The locale variants + * + * @see http://www.php.net/manual/en/locale.getallvariants.php + * + * @throws MethodNotImplementedException + */ + public static function getAllVariants($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Returns the default locale + * + * @return string The default locale code. Always returns 'en' + * + * @see http://www.php.net/manual/en/locale.getdefault.php + */ + public static function getDefault() + { + return 'en'; + } + + /** + * Not supported. Returns the localized display name for the locale language + * + * @param string $locale The locale code to return the display language from + * @param string $inLocale Optional format locale code to use to display the language name + * + * @return string The localized language display name + * + * @see http://www.php.net/manual/en/locale.getdisplaylanguage.php + * + * @throws MethodNotImplementedException + */ + public static function getDisplayLanguage($locale, $inLocale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the localized display name for the locale + * + * @param string $locale The locale code to return the display locale name from + * @param string $inLocale Optional format locale code to use to display the locale name + * + * @return string The localized locale display name + * + * @see http://www.php.net/manual/en/locale.getdisplayname.php + * + * @throws MethodNotImplementedException + */ + public static function getDisplayName($locale, $inLocale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the localized display name for the locale region + * + * @param string $locale The locale code to return the display region from + * @param string $inLocale Optional format locale code to use to display the region name + * + * @return string The localized region display name + * + * @see http://www.php.net/manual/en/locale.getdisplayregion.php + * + * @throws MethodNotImplementedException + */ + public static function getDisplayRegion($locale, $inLocale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the localized display name for the locale script + * + * @param string $locale The locale code to return the display script from + * @param string $inLocale Optional format locale code to use to display the script name + * + * @return string The localized script display name + * + * @see http://www.php.net/manual/en/locale.getdisplayscript.php + * + * @throws MethodNotImplementedException + */ + public static function getDisplayScript($locale, $inLocale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the localized display name for the locale variant + * + * @param string $locale The locale code to return the display variant from + * @param string $inLocale Optional format locale code to use to display the variant name + * + * @return string The localized variant display name + * + * @see http://www.php.net/manual/en/locale.getdisplayvariant.php + * + * @throws MethodNotImplementedException + */ + public static function getDisplayVariant($locale, $inLocale = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the keywords for the locale + * + * @param string $locale The locale code to extract the keywords from + * + * @return array Associative array with the extracted variants + * + * @see http://www.php.net/manual/en/locale.getkeywords.php + * + * @throws MethodNotImplementedException + */ + public static function getKeywords($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the primary language for the locale + * + * @param string $locale The locale code to extract the language code from + * + * @return string|null The extracted language code or null in case of error + * + * @see http://www.php.net/manual/en/locale.getprimarylanguage.php + * + * @throws MethodNotImplementedException + */ + public static function getPrimaryLanguage($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the region for the locale + * + * @param string $locale The locale code to extract the region code from + * + * @return string|null The extracted region code or null if not present + * + * @see http://www.php.net/manual/en/locale.getregion.php + * + * @throws MethodNotImplementedException + */ + public static function getRegion($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the script for the locale + * + * @param string $locale The locale code to extract the script code from + * + * @return string|null The extracted script code or null if not present + * + * @see http://www.php.net/manual/en/locale.getscript.php + * + * @throws MethodNotImplementedException + */ + public static function getScript($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns the closest language tag for the locale + * + * @param array $langtag A list of the language tags to compare to locale + * @param string $locale The locale to use as the language range when matching + * @param Boolean $canonicalize If true, the arguments will be converted to canonical form before matching + * @param string $default The locale to use if no match is found + * + * @see http://www.php.net/manual/en/locale.lookup.php + * + * @throws MethodNotImplementedException + */ + public static function lookup(array $langtag, $locale, $canonicalize = false, $default = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns an associative array of locale identifier subtags + * + * @param string $locale The locale code to extract the subtag array from + * + * @return array Associative array with the extracted subtags + * + * @see http://www.php.net/manual/en/locale.parselocale.php + * + * @throws MethodNotImplementedException + */ + public static function parseLocale($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Sets the default runtime locale + * + * @param string $locale The locale code + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/locale.parselocale.php + * + * @throws MethodNotImplementedException + */ + public static function setDefault($locale) + { + throw new MethodNotImplementedException(__METHOD__); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php new file mode 100644 index 0000000..0e5652e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php @@ -0,0 +1,891 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\NumberFormatter; + +use Symfony\Component\Intl\Exception\NotImplementedException; +use Symfony\Component\Intl\Exception\MethodNotImplementedException; +use Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException; +use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException; +use Symfony\Component\Intl\Globals\IntlGlobals; +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\Locale\Locale; + +/** + * Replacement for PHP's native {@link \NumberFormatter} class. + * + * The only methods currently supported in this class are: + * + * - {@link __construct} + * - {@link create} + * - {@link formatCurrency} + * - {@link format} + * - {@link getAttribute} + * - {@link getErrorCode} + * - {@link getErrorMessage} + * - {@link getLocale} + * - {@link parse} + * - {@link setAttribute} + * + * @author Eriksen Costa + * @author Bernhard Schussek + */ +class NumberFormatter +{ + /* Format style constants */ + const PATTERN_DECIMAL = 0; + const DECIMAL = 1; + const CURRENCY = 2; + const PERCENT = 3; + const SCIENTIFIC = 4; + const SPELLOUT = 5; + const ORDINAL = 6; + const DURATION = 7; + const PATTERN_RULEBASED = 9; + const IGNORE = 0; + const DEFAULT_STYLE = 1; + + /* Format type constants */ + const TYPE_DEFAULT = 0; + const TYPE_INT32 = 1; + const TYPE_INT64 = 2; + const TYPE_DOUBLE = 3; + const TYPE_CURRENCY = 4; + + /* Numeric attribute constants */ + const PARSE_INT_ONLY = 0; + const GROUPING_USED = 1; + const DECIMAL_ALWAYS_SHOWN = 2; + const MAX_INTEGER_DIGITS = 3; + const MIN_INTEGER_DIGITS = 4; + const INTEGER_DIGITS = 5; + const MAX_FRACTION_DIGITS = 6; + const MIN_FRACTION_DIGITS = 7; + const FRACTION_DIGITS = 8; + const MULTIPLIER = 9; + const GROUPING_SIZE = 10; + const ROUNDING_MODE = 11; + const ROUNDING_INCREMENT = 12; + const FORMAT_WIDTH = 13; + const PADDING_POSITION = 14; + const SECONDARY_GROUPING_SIZE = 15; + const SIGNIFICANT_DIGITS_USED = 16; + const MIN_SIGNIFICANT_DIGITS = 17; + const MAX_SIGNIFICANT_DIGITS = 18; + const LENIENT_PARSE = 19; + + /* Text attribute constants */ + const POSITIVE_PREFIX = 0; + const POSITIVE_SUFFIX = 1; + const NEGATIVE_PREFIX = 2; + const NEGATIVE_SUFFIX = 3; + const PADDING_CHARACTER = 4; + const CURRENCY_CODE = 5; + const DEFAULT_RULESET = 6; + const PUBLIC_RULESETS = 7; + + /* Format symbol constants */ + const DECIMAL_SEPARATOR_SYMBOL = 0; + const GROUPING_SEPARATOR_SYMBOL = 1; + const PATTERN_SEPARATOR_SYMBOL = 2; + const PERCENT_SYMBOL = 3; + const ZERO_DIGIT_SYMBOL = 4; + const DIGIT_SYMBOL = 5; + const MINUS_SIGN_SYMBOL = 6; + const PLUS_SIGN_SYMBOL = 7; + const CURRENCY_SYMBOL = 8; + const INTL_CURRENCY_SYMBOL = 9; + const MONETARY_SEPARATOR_SYMBOL = 10; + const EXPONENTIAL_SYMBOL = 11; + const PERMILL_SYMBOL = 12; + const PAD_ESCAPE_SYMBOL = 13; + const INFINITY_SYMBOL = 14; + const NAN_SYMBOL = 15; + const SIGNIFICANT_DIGIT_SYMBOL = 16; + const MONETARY_GROUPING_SEPARATOR_SYMBOL = 17; + + /* Rounding mode values used by NumberFormatter::setAttribute() with NumberFormatter::ROUNDING_MODE attribute */ + const ROUND_CEILING = 0; + const ROUND_FLOOR = 1; + const ROUND_DOWN = 2; + const ROUND_UP = 3; + const ROUND_HALFEVEN = 4; + const ROUND_HALFDOWN = 5; + const ROUND_HALFUP = 6; + + /* Pad position values used by NumberFormatter::setAttribute() with NumberFormatter::PADDING_POSITION attribute */ + const PAD_BEFORE_PREFIX = 0; + const PAD_AFTER_PREFIX = 1; + const PAD_BEFORE_SUFFIX = 2; + const PAD_AFTER_SUFFIX = 3; + + /** + * The error code from the last operation + * + * @var integer + */ + protected $errorCode = IntlGlobals::U_ZERO_ERROR; + + /** + * The error message from the last operation + * + * @var string + */ + protected $errorMessage = 'U_ZERO_ERROR'; + + /** + * @var int + */ + private $style; + + /** + * Default values for the en locale + * + * @var array + */ + private $attributes = array( + self::FRACTION_DIGITS => 0, + self::GROUPING_USED => 1, + self::ROUNDING_MODE => self::ROUND_HALFEVEN + ); + + /** + * Holds the initialized attributes code + * + * @var array + */ + private $initializedAttributes = array(); + + /** + * The supported styles to the constructor $styles argument + * + * @var array + */ + private static $supportedStyles = array( + 'CURRENCY' => self::CURRENCY, + 'DECIMAL' => self::DECIMAL + ); + + /** + * Supported attributes to the setAttribute() $attr argument + * + * @var array + */ + private static $supportedAttributes = array( + 'FRACTION_DIGITS' => self::FRACTION_DIGITS, + 'GROUPING_USED' => self::GROUPING_USED, + 'ROUNDING_MODE' => self::ROUNDING_MODE + ); + + /** + * The available rounding modes for setAttribute() usage with + * NumberFormatter::ROUNDING_MODE. NumberFormatter::ROUND_DOWN + * and NumberFormatter::ROUND_UP does not have a PHP only equivalent + * + * @var array + */ + private static $roundingModes = array( + 'ROUND_HALFEVEN' => self::ROUND_HALFEVEN, + 'ROUND_HALFDOWN' => self::ROUND_HALFDOWN, + 'ROUND_HALFUP' => self::ROUND_HALFUP + ); + + /** + * The mapping between NumberFormatter rounding modes to the available + * modes in PHP's round() function. + * + * @see http://www.php.net/manual/en/function.round.php + * + * @var array + */ + private static $phpRoundingMap = array( + self::ROUND_HALFDOWN => \PHP_ROUND_HALF_DOWN, + self::ROUND_HALFEVEN => \PHP_ROUND_HALF_EVEN, + self::ROUND_HALFUP => \PHP_ROUND_HALF_UP + ); + + /** + * The maximum values of the integer type in 32 bit platforms. + * + * @var array + */ + private static $int32Range = array( + 'positive' => 2147483647, + 'negative' => -2147483648 + ); + + /** + * The maximum values of the integer type in 64 bit platforms. + * + * @var array + */ + private static $int64Range = array( + 'positive' => 9223372036854775807, + 'negative' => -9223372036854775808 + ); + + /** + * Constructor. + * + * @param string $locale The locale code. The only currently supported locale is "en". + * @param int $style Style of the formatting, one of the format style constants. + * The only supported styles are NumberFormatter::DECIMAL + * and NumberFormatter::CURRENCY. + * @param string $pattern Not supported. A pattern string in case $style is NumberFormat::PATTERN_DECIMAL or + * NumberFormat::PATTERN_RULEBASED. It must conform to the syntax + * described in the ICU DecimalFormat or ICU RuleBasedNumberFormat documentation + * + * @see http://www.php.net/manual/en/numberformatter.create.php + * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details + * @see http://www.icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#_details + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + * @throws MethodArgumentValueNotImplementedException When the $style is not supported + * @throws MethodArgumentNotImplementedException When the pattern value is different than null + */ + public function __construct($locale = 'en', $style = null, $pattern = null) + { + if ('en' != $locale) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); + } + + if (!in_array($style, self::$supportedStyles)) { + $message = sprintf('The available styles are: %s.', implode(', ', array_keys(self::$supportedStyles))); + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'style', $style, $message); + } + + if (null !== $pattern) { + throw new MethodArgumentNotImplementedException(__METHOD__, 'pattern'); + } + + $this->style = $style; + } + + /** + * Static constructor. + * + * @param string $locale The locale code. The only supported locale is "en". + * @param int $style Style of the formatting, one of the format style constants. + * The only currently supported styles are NumberFormatter::DECIMAL + * and NumberFormatter::CURRENCY. + * @param string $pattern Not supported. A pattern string in case $style is NumberFormat::PATTERN_DECIMAL or + * NumberFormat::PATTERN_RULEBASED. It must conform to the syntax + * described in the ICU DecimalFormat or ICU RuleBasedNumberFormat documentation + * + * @return NumberFormatter + * + * @see http://www.php.net/manual/en/numberformatter.create.php + * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details + * @see http://www.icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#_details + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed + * @throws MethodArgumentValueNotImplementedException When the $style is not supported + * @throws MethodArgumentNotImplementedException When the pattern value is different than null + */ + public static function create($locale = 'en', $style = null, $pattern = null) + { + return new self($locale, $style, $pattern); + } + + /** + * Format a currency value + * + * @param float $value The numeric currency value + * @param string $currency The 3-letter ISO 4217 currency code indicating the currency to use + * + * @return string The formatted currency value + * + * @see http://www.php.net/manual/en/numberformatter.formatcurrency.php + * @see http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/currency_codes/currency_codes_list-1.htm + */ + public function formatCurrency($value, $currency) + { + if ($this->style == self::DECIMAL) { + return $this->format($value); + } + + $symbol = Intl::getCurrencyBundle()->getCurrencySymbol($currency, 'en'); + $fractionDigits = Intl::getCurrencyBundle()->getFractionDigits($currency); + + $value = $this->roundCurrency($value, $currency); + + $negative = false; + if (0 > $value) { + $negative = true; + $value *= -1; + } + + $value = $this->formatNumber($value, $fractionDigits); + + $ret = $symbol.$value; + + return $negative ? '('.$ret.')' : $ret; + } + + /** + * Format a number + * + * @param number $value The value to format + * @param int $type Type of the formatting, one of the format type constants. + * Only type NumberFormatter::TYPE_DEFAULT is currently supported. + * + * @return Boolean|string The formatted value or false on error + * + * @see http://www.php.net/manual/en/numberformatter.format.php + * + * @throws NotImplementedException If the method is called with the class $style 'CURRENCY' + * @throws MethodArgumentValueNotImplementedException If the $type is different than TYPE_DEFAULT + */ + public function format($value, $type = self::TYPE_DEFAULT) + { + // The original NumberFormatter does not support this format type + if ($type == self::TYPE_CURRENCY) { + trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING); + + return false; + } + + if ($this->style == self::CURRENCY) { + throw new NotImplementedException(sprintf( + '%s() method does not support the formatting of currencies (instance with CURRENCY style). %s', + __METHOD__, NotImplementedException::INTL_INSTALL_MESSAGE + )); + } + + // Only the default type is supported. + if ($type != self::TYPE_DEFAULT) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'type', $type, 'Only TYPE_DEFAULT is supported'); + } + + $fractionDigits = $this->getAttribute(self::FRACTION_DIGITS); + + $value = $this->round($value, $fractionDigits); + $value = $this->formatNumber($value, $fractionDigits); + + // behave like the intl extension + $this->resetError(); + + return $value; + } + + /** + * Returns an attribute value + * + * @param int $attr An attribute specifier, one of the numeric attribute constants + * + * @return Boolean|int The attribute value on success or false on error + * + * @see http://www.php.net/manual/en/numberformatter.getattribute.php + */ + public function getAttribute($attr) + { + return isset($this->attributes[$attr]) ? $this->attributes[$attr] : null; + } + + /** + * Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value + * + * @return int The error code from last formatter call + * + * @see http://www.php.net/manual/en/numberformatter.geterrorcode.php + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value + * + * @return string The error message from last formatter call + * + * @see http://www.php.net/manual/en/numberformatter.geterrormessage.php + */ + public function getErrorMessage() + { + return $this->errorMessage; + } + + /** + * Returns the formatter's locale + * + * The parameter $type is currently ignored. + * + * @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 formatter. Currently always + * returns "en". + * + * @see http://www.php.net/manual/en/numberformatter.getlocale.php + */ + public function getLocale($type = Locale::ACTUAL_LOCALE) + { + return 'en'; + } + + /** + * Not supported. Returns the formatter's pattern + * + * @return Boolean|string The pattern string used by the formatter or false on error + * + * @see http://www.php.net/manual/en/numberformatter.getpattern.php + * + * @throws MethodNotImplementedException + */ + public function getPattern() + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns a formatter symbol value + * + * @param int $attr A symbol specifier, one of the format symbol constants + * + * @return Boolean|string The symbol value or false on error + * + * @see http://www.php.net/manual/en/numberformatter.getsymbol.php + * + * @throws MethodNotImplementedException + */ + public function getSymbol($attr) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Returns a formatter text attribute value + * + * @param int $attr An attribute specifier, one of the text attribute constants + * + * @return Boolean|string The attribute value or false on error + * + * @see http://www.php.net/manual/en/numberformatter.gettextattribute.php + * + * @throws MethodNotImplementedException + */ + public function getTextAttribute($attr) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Parse a currency number + * + * @param string $value The value to parse + * @param string $currency Parameter to receive the currency name (reference) + * @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended + * + * @return Boolean|string The parsed numeric value of false on error + * + * @see http://www.php.net/manual/en/numberformatter.parsecurrency.php + * + * @throws MethodNotImplementedException + */ + public function parseCurrency($value, &$currency, &$position = null) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Parse a number + * + * @param string $value The value to parse + * @param int $type Type of the formatting, one of the format type constants. + * The only currently supported types are NumberFormatter::TYPE_DOUBLE, + * NumberFormatter::TYPE_INT32 and NumberFormatter::TYPE_INT64. + * @param int $position Not supported. Offset to begin the parsing on return this value will hold the offset at which the parsing ended + * + * @return Boolean|string The parsed value of false on error + * + * @see http://www.php.net/manual/en/numberformatter.parse.php + * + * @throws MethodArgumentNotImplementedException When $position different than null, behavior not implemented + */ + public function parse($value, $type = self::TYPE_DOUBLE, &$position = null) + { + if ($type == self::TYPE_DEFAULT || $type == self::TYPE_CURRENCY) { + trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING); + + return false; + } + + // We don't calculate the position when parsing the value + if (null !== $position) { + throw new MethodArgumentNotImplementedException(__METHOD__, 'position'); + } + + preg_match('/^([^0-9\-]{0,})(.*)/', $value, $matches); + + // Any string before the numeric value causes error in the parsing + if (isset($matches[1]) && !empty($matches[1])) { + IntlGlobals::setError(IntlGlobals::U_PARSE_ERROR, 'Number parsing failed'); + $this->errorCode = IntlGlobals::getErrorCode(); + $this->errorMessage = IntlGlobals::getErrorMessage(); + + return false; + } + + // Remove everything that is not number or dot (.) + $value = preg_replace('/[^0-9\.\-]/', '', $value); + $value = $this->convertValueDataType($value, $type); + + // behave like the intl extension + $this->resetError(); + + return $value; + } + + /** + * Set an attribute + * + * @param int $attr An attribute specifier, one of the numeric attribute constants. + * The only currently supported attributes are NumberFormatter::FRACTION_DIGITS, + * NumberFormatter::GROUPING_USED and NumberFormatter::ROUNDING_MODE. + * @param int $value The attribute value. The only currently supported rounding modes are + * NumberFormatter::ROUND_HALFEVEN, NumberFormatter::ROUND_HALFDOWN and + * NumberFormatter::ROUND_HALFUP. + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/numberformatter.setattribute.php + * + * @throws MethodArgumentValueNotImplementedException When the $attr is not supported + * @throws MethodArgumentValueNotImplementedException When the $value is not supported + */ + public function setAttribute($attr, $value) + { + if (!in_array($attr, self::$supportedAttributes)) { + $message = sprintf( + 'The available attributes are: %s', + implode(', ', array_keys(self::$supportedAttributes)) + ); + + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'attr', $value, $message); + } + + if (self::$supportedAttributes['ROUNDING_MODE'] == $attr && $this->isInvalidRoundingMode($value)) { + $message = sprintf( + 'The supported values for ROUNDING_MODE are: %s', + implode(', ', array_keys(self::$roundingModes)) + ); + + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'attr', $value, $message); + } + + if (self::$supportedAttributes['GROUPING_USED'] == $attr) { + $value = $this->normalizeGroupingUsedValue($value); + } + + if (self::$supportedAttributes['FRACTION_DIGITS'] == $attr) { + $value = $this->normalizeFractionDigitsValue($value); + } + + $this->attributes[$attr] = $value; + $this->initializedAttributes[$attr] = true; + + return true; + } + + /** + * Not supported. Set the formatter's pattern + * + * @param string $pattern A pattern string in conformance with the ICU DecimalFormat documentation + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/numberformatter.setpattern.php + * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details + * + * @throws MethodNotImplementedException + */ + public function setPattern($pattern) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Set the formatter's symbol + * + * @param int $attr A symbol specifier, one of the format symbol constants + * @param string $value The value for the symbol + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/numberformatter.setsymbol.php + * + * @throws MethodNotImplementedException + */ + public function setSymbol($attr, $value) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Set a text attribute + * + * @param int $attr An attribute specifier, one of the text attribute constants + * @param int $value The attribute value + * + * @return Boolean true on success or false on failure + * + * @see http://www.php.net/manual/en/numberformatter.settextattribute.php + * + * @throws MethodNotImplementedException + */ + public function setTextAttribute($attr, $value) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Set the error to the default U_ZERO_ERROR + */ + protected function resetError() + { + IntlGlobals::setError(IntlGlobals::U_ZERO_ERROR); + $this->errorCode = IntlGlobals::getErrorCode(); + $this->errorMessage = IntlGlobals::getErrorMessage(); + } + + /** + * Rounds a currency value, applying increment rounding if applicable + * + * When a currency have a rounding increment, an extra round is made after the first one. The rounding factor is + * determined in the ICU data and is explained as of: + * + * "the rounding increment is given in units of 10^(-fraction_digits)" + * + * The only actual rounding data as of this writing, is CHF. + * + * @param float $value The numeric currency value + * @param string $currency The 3-letter ISO 4217 currency code indicating the currency to use + * + * @return string The rounded numeric currency value + * + * @see http://en.wikipedia.org/wiki/Swedish_rounding + * @see http://www.docjar.com/html/api/com/ibm/icu/util/Currency.java.html#1007 + */ + private function roundCurrency($value, $currency) + { + $fractionDigits = Intl::getCurrencyBundle()->getFractionDigits($currency); + $roundingIncrement = Intl::getCurrencyBundle()->getRoundingIncrement($currency); + + // Round with the formatter rounding mode + $value = $this->round($value, $fractionDigits); + + // Swiss rounding + if (0 < $roundingIncrement && 0 < $fractionDigits) { + $roundingFactor = $roundingIncrement / pow(10, $fractionDigits); + $value = round($value / $roundingFactor) * $roundingFactor; + } + + return $value; + } + + /** + * Rounds a value. + * + * @param integer|float $value The value to round + * @param int $precision The number of decimal digits to round to + * + * @return integer|float The rounded value + */ + private function round($value, $precision) + { + $precision = $this->getUnitializedPrecision($value, $precision); + + $roundingMode = self::$phpRoundingMap[$this->getAttribute(self::ROUNDING_MODE)]; + $value = round($value, $precision, $roundingMode); + + return $value; + } + + /** + * Formats a number. + * + * @param integer|float $value The numeric value to format + * @param int $precision The number of decimal digits to use + * + * @return string The formatted number + */ + private function formatNumber($value, $precision) + { + $precision = $this->getUnitializedPrecision($value, $precision); + + return number_format($value, $precision, '.', $this->getAttribute(self::GROUPING_USED) ? ',' : ''); + } + + /** + * Returns the precision value if the DECIMAL style is being used and the FRACTION_DIGITS attribute is unitialized. + * + * @param integer|float $value The value to get the precision from if the FRACTION_DIGITS attribute is unitialized + * @param int $precision The precision value to returns if the FRACTION_DIGITS attribute is initialized + * + * @return int The precision value + */ + private function getUnitializedPrecision($value, $precision) + { + if ($this->style == self::CURRENCY) { + return $precision; + } + + if (!$this->isInitializedAttribute(self::FRACTION_DIGITS)) { + preg_match('/.*\.(.*)/', (string) $value, $digits); + if (isset($digits[1])) { + $precision = strlen($digits[1]); + } + } + + return $precision; + } + + /** + * Check if the attribute is initialized (value set by client code). + * + * @param string $attr The attribute name + * + * @return Boolean true if the value was set by client, false otherwise + */ + private function isInitializedAttribute($attr) + { + return isset($this->initializedAttributes[$attr]); + } + + /** + * Returns the numeric value using the $type to convert to the right data type. + * + * @param mixed $value The value to be converted + * @param int $type The type to convert. Can be TYPE_DOUBLE (float) or TYPE_INT32 (int) + * + * @return integer|float The converted value + */ + private function convertValueDataType($value, $type) + { + if ($type == self::TYPE_DOUBLE) { + $value = (float) $value; + } elseif ($type == self::TYPE_INT32) { + $value = $this->getInt32Value($value); + } elseif ($type == self::TYPE_INT64) { + $value = $this->getInt64Value($value); + } + + return $value; + } + + /** + * Convert the value data type to int or returns false if the value is out of the integer value range. + * + * @param mixed $value The value to be converted + * + * @return int The converted value + */ + private function getInt32Value($value) + { + if ($value > self::$int32Range['positive'] || $value < self::$int32Range['negative']) { + return false; + } + + return (int) $value; + } + + /** + * Convert the value data type to int or returns false if the value is out of the integer value range. + * + * @param mixed $value The value to be converted + * + * @return int|float The converted value + * + * @see https://bugs.php.net/bug.php?id=59597 Bug #59597 + */ + private function getInt64Value($value) + { + if ($value > self::$int64Range['positive'] || $value < self::$int64Range['negative']) { + return false; + } + + if (PHP_INT_SIZE !== 8 && ($value > self::$int32Range['positive'] || $value <= self::$int32Range['negative'])) { + // Bug #59597 was fixed on PHP 5.3.14 and 5.4.4 + // The negative PHP_INT_MAX was being converted to float + if ( + $value == self::$int32Range['negative'] && + ( + (version_compare(PHP_VERSION, '5.4.0', '<') && version_compare(PHP_VERSION, '5.3.14', '>=')) || + version_compare(PHP_VERSION, '5.4.4', '>=') + ) + ) { + return (int) $value; + } + + return (float) $value; + } + + if (PHP_INT_SIZE === 8) { + // Bug #59597 was fixed on PHP 5.3.14 and 5.4.4 + // A 32 bit integer was being generated instead of a 64 bit integer + if ( + ($value > self::$int32Range['positive'] || $value < self::$int32Range['negative']) && + ( + (version_compare(PHP_VERSION, '5.3.14', '<')) || + (version_compare(PHP_VERSION, '5.4.0', '>=') && version_compare(PHP_VERSION, '5.4.4', '<')) + ) + ) { + $value = (-2147483648 - ($value % -2147483648)) * ($value / abs($value)); + } + } + + return (int) $value; + } + + /** + * Check if the rounding mode is invalid. + * + * @param int $value The rounding mode value to check + * + * @return Boolean true if the rounding mode is invalid, false otherwise + */ + private function isInvalidRoundingMode($value) + { + if (in_array($value, self::$roundingModes, true)) { + return false; + } + + return true; + } + + /** + * Returns the normalized value for the GROUPING_USED attribute. Any value that can be converted to int will be + * cast to Boolean and then to int again. This way, negative values are converted to 1 and string values to 0. + * + * @param mixed $value The value to be normalized + * + * @return int The normalized value for the attribute (0 or 1) + */ + private function normalizeGroupingUsedValue($value) + { + return (int) (Boolean) (int) $value; + } + + /** + * Returns the normalized value for the FRACTION_DIGITS attribute. The value is converted to int and if negative, + * the returned value will be 0. + * + * @param mixed $value The value to be normalized + * + * @return int The normalized value for the attribute + */ + private function normalizeFractionDigitsValue($value) + { + $value = (int) $value; + + return (0 > $value) ? 0 : $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/README.md b/vendor/symfony/symfony/src/Symfony/Component/Intl/README.md new file mode 100644 index 0000000..ef4ba50 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/README.md @@ -0,0 +1,25 @@ +Intl Component +============= + +A PHP replacement layer for the C intl extension that includes additional data +from the ICU library. + +The replacement layer is limited to the locale "en". If you want to use other +locales, you should [install the intl extension] [1] instead. + +Documentation +------------- + +The documentation for the component can be found [online] [2]. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Intl/ + $ composer.phar install --dev + $ phpunit + +[0]: http://www.php.net/manual/en/intl.setup.php +[1]: http://symfony.com/doc/2.3/components/intl.html diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php new file mode 100644 index 0000000..d1d523c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/AbstractBundle.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\Intl\ResourceBundle; + +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface; + +/** + * Base class for {@link ResourceBundleInterface} implementations. + * + * @author Bernhard Schussek + */ +abstract class AbstractBundle implements ResourceBundleInterface +{ + /** + * @var string + */ + private $path; + + /** + * @var StructuredBundleReaderInterface + */ + private $reader; + + /** + * Creates a bundle at the given path using the given reader for reading + * bundle entries. + * + * @param string $path The path to the bundle. + * @param StructuredBundleReaderInterface $reader The reader for reading + * the bundle. + */ + public function __construct($path, StructuredBundleReaderInterface $reader) + { + $this->path = $path; + $this->reader = $reader; + } + + /** + * {@inheritdoc} + */ + public function getLocales() + { + return $this->reader->getLocales($this->path); + } + + /** + * Proxy method for {@link StructuredBundleReaderInterface#read}. + */ + protected function read($locale) + { + return $this->reader->read($this->path, $locale); + } + + /** + * Proxy method for {@link StructuredBundleReaderInterface#readEntry}. + */ + protected function readEntry($locale, array $indices, $mergeFallback = false) + { + return $this->reader->readEntry($this->path, $locale, $indices, $mergeFallback); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php new file mode 100644 index 0000000..174aa17 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.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\Intl\ResourceBundle\Compiler; + +use Symfony\Component\Intl\Exception\RuntimeException; + +/** + * Compiles .txt resource bundles to binary .res files. + * + * @author Bernhard Schussek + */ +class BundleCompiler 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/ResourceBundle/Compiler/BundleCompilerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.php new file mode 100644 index 0000000..6184ea3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.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\Intl\ResourceBundle\Compiler; + +/** + * Compiles a resource bundle. + * + * @author Bernhard Schussek + */ +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/ResourceBundle/CurrencyBundle.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php new file mode 100644 index 0000000..6f2a0ed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.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\Intl\ResourceBundle; + +/** + * Default implementation of {@link CurrencyBundleInterface}. + * + * @author Bernhard Schussek + */ +class CurrencyBundle extends AbstractBundle implements CurrencyBundleInterface +{ + const INDEX_NAME = 0; + + const INDEX_SYMBOL = 1; + + const INDEX_FRACTION_DIGITS = 2; + + const INDEX_ROUNDING_INCREMENT = 3; + + /** + * {@inheritdoc} + */ + public function getCurrencySymbol($currency, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + return $this->readEntry($locale, array('Currencies', $currency, static::INDEX_SYMBOL)); + } + + /** + * {@inheritdoc} + */ + public function getCurrencyName($currency, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + return $this->readEntry($locale, array('Currencies', $currency, static::INDEX_NAME)); + } + + /** + * {@inheritdoc} + */ + public function getCurrencyNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($currencies = $this->readEntry($locale, array('Currencies')))) { + return array(); + } + + if ($currencies instanceof \Traversable) { + $currencies = iterator_to_array($currencies); + } + + $index = static::INDEX_NAME; + + array_walk($currencies, function (&$value) use ($index) { + $value = $value[$index]; + }); + + return $currencies; + } + + /** + * {@inheritdoc} + */ + public function getFractionDigits($currency) + { + return $this->readEntry('en', array('Currencies', $currency, static::INDEX_FRACTION_DIGITS)); + } + + /** + * {@inheritdoc} + */ + public function getRoundingIncrement($currency) + { + return $this->readEntry('en', array('Currencies', $currency, static::INDEX_ROUNDING_INCREMENT)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundleInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundleInterface.php new file mode 100644 index 0000000..1a88e93 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundleInterface.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\Intl\ResourceBundle; + +/** + * Gives access to currency-related ICU data. + * + * @author Bernhard Schussek + */ +interface CurrencyBundleInterface extends ResourceBundleInterface +{ + /** + * Returns the symbol used for a currency. + * + * @param string $currency A currency code (e.g. "EUR"). + * @param string $locale Optional. The locale to return the result in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The currency symbol or NULL if not found. + */ + public function getCurrencySymbol($currency, $locale = null); + + /** + * Returns the name of a currency. + * + * @param string $currency A currency code (e.g. "EUR"). + * @param string $locale Optional. The locale to return the name in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The name of the currency or NULL if not found. + */ + public function getCurrencyName($currency, $locale = null); + + /** + * Returns the names of all known currencies. + * + * @param string $locale Optional. The locale to return the names in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string[] A list of currency names indexed by currency codes. + */ + public function getCurrencyNames($locale = null); + + /** + * Returns the number of digits after the comma of a currency. + * + * @param string $currency A currency code (e.g. "EUR"). + * + * @return integer|null The number of digits after the comma or NULL if not found. + */ + public function getFractionDigits($currency); + + /** + * Returns the rounding increment of a currency. + * + * The rounding increment indicates to which number a currency is rounded. + * For example, 1230 rounded to the nearest 50 is 1250. 1.234 rounded to the + * nearest 0.65 is 1.3. + * + * @param string $currency A currency code (e.g. "EUR"). + * + * @return float|integer|null The rounding increment or NULL if not found. + */ + public function getRoundingIncrement($currency); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php new file mode 100644 index 0000000..c449f9c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LanguageBundle.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\Intl\ResourceBundle; + +/** + * Default implementation of {@link LanguageBundleInterface}. + * + * @author Bernhard Schussek + */ +class LanguageBundle extends AbstractBundle implements LanguageBundleInterface +{ + /** + * {@inheritdoc} + */ + public function getLanguageName($lang, $region = null, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($languages = $this->readEntry($locale, array('Languages')))) { + return null; + } + + // Some languages are translated together with their region, + // i.e. "en_GB" is translated as "British English" + if (null !== $region && isset($languages[$lang.'_'.$region])) { + return $languages[$lang.'_'.$region]; + } + + return $languages[$lang]; + } + + /** + * {@inheritdoc} + */ + public function getLanguageNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($languages = $this->readEntry($locale, array('Languages')))) { + return array(); + } + + if ($languages instanceof \Traversable) { + $languages = iterator_to_array($languages); + } + + return $languages; + } + + /** + * {@inheritdoc} + */ + public function getScriptName($script, $lang = null, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + $data = $this->read($locale); + + // Some languages are translated together with their script, + // e.g. "zh_Hans" is translated as "Simplified Chinese" + if (null !== $lang && isset($data['Languages'][$lang.'_'.$script])) { + $langName = $data['Languages'][$lang.'_'.$script]; + + // If the script is appended in braces, extract it, e.g. "zh_Hans" + // is translated as "Chinesisch (vereinfacht)" in locale "de" + if (strpos($langName, '(') !== false) { + list($langName, $scriptName) = preg_split('/[\s()]/', $langName, null, PREG_SPLIT_NO_EMPTY); + + return $scriptName; + } + } + + // "af" (Afrikaans) has no "Scripts" block + if (!isset($data['Scripts'][$script])) { + return null; + } + + return $data['Scripts'][$script]; + } + + /** + * {@inheritdoc} + */ + public function getScriptNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($scripts = $this->readEntry($locale, array('Scripts')))) { + return array(); + } + + if ($scripts instanceof \Traversable) { + $scripts = iterator_to_array($scripts); + } + + return $scripts; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LanguageBundleInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LanguageBundleInterface.php new file mode 100644 index 0000000..de50bda --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LanguageBundleInterface.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\Intl\ResourceBundle; + +/** + * Gives access to language-related ICU data. + * + * @author Bernhard Schussek + */ +interface LanguageBundleInterface extends ResourceBundleInterface +{ + /** + * Returns the name of a language. + * + * @param string $lang A language code (e.g. "en"). + * @param string|null $region Optional. A region code (e.g. "US"). + * @param string $locale Optional. The locale to return the name in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The name of the language or NULL if not found. + */ + public function getLanguageName($lang, $region = null, $locale = null); + + /** + * Returns the names of all known languages. + * + * @param string $locale Optional. The locale to return the names in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string[] A list of language names indexed by language codes. + */ + public function getLanguageNames($locale = null); + + /** + * Returns the name of a script. + * + * @param string $script A script code (e.g. "Hans"). + * @param string $lang Optional. A language code (e.g. "zh"). + * @param string $locale Optional. The locale to return the name in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The name of the script or NULL if not found. + */ + public function getScriptName($script, $lang = null, $locale = null); + + /** + * Returns the names of all known scripts. + * + * @param string $locale Optional. The locale to return the names in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string[] A list of script names indexed by script codes. + */ + public function getScriptNames($locale = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php new file mode 100644 index 0000000..6f6cdfc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LocaleBundle.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\Intl\ResourceBundle; + +/** + * Default implementation of {@link LocaleBundleInterface}. + * + * @author Bernhard Schussek + */ +class LocaleBundle extends AbstractBundle implements LocaleBundleInterface +{ + /** + * {@inheritdoc} + */ + public function getLocaleName($ofLocale, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + return $this->readEntry($locale, array('Locales', $ofLocale)); + } + + /** + * {@inheritdoc} + */ + public function getLocaleNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($locales = $this->readEntry($locale, array('Locales')))) { + return array(); + } + + if ($locales instanceof \Traversable) { + $locales = iterator_to_array($locales); + } + + return $locales; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LocaleBundleInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LocaleBundleInterface.php new file mode 100644 index 0000000..daf5be6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/LocaleBundleInterface.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\Intl\ResourceBundle; + +/** + * Gives access to locale-related ICU data. + * + * @author Bernhard Schussek + */ +interface LocaleBundleInterface extends ResourceBundleInterface +{ + /** + * Returns the name of a locale. + * + * @param string $ofLocale The locale to return the name of (e.g. "de_AT"). + * @param string $locale Optional. The locale to return the name in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The name of the locale or NULL if not found. + */ + public function getLocaleName($ofLocale, $locale = null); + + /** + * Returns the names of all known locales. + * + * @param string $locale Optional. The locale to return the names in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string[] A list of locale names indexed by locale codes. + */ + public function getLocaleNames($locale = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.php new file mode 100644 index 0000000..c30693a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.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\ResourceBundle\Reader; + +/** + * Base class for {@link BundleReaderInterface} implementations. + * + * @author Bernhard Schussek + */ +abstract class AbstractBundleReader implements BundleReaderInterface +{ + /** + * {@inheritdoc} + */ + public function getLocales($path) + { + $extension = '.' . $this->getFileExtension(); + $locales = glob($path . '/*' . $extension); + + // Remove file extension and sort + array_walk($locales, function (&$locale) use ($extension) { $locale = basename($locale, $extension); }); + sort($locales); + + return $locales; + } + + /** + * Returns the extension of locale files in this bundle. + * + * @return string The file extension (without leading dot). + */ + abstract protected function getFileExtension(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php new file mode 100644 index 0000000..56cef80 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.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\ResourceBundle\Reader; + +use Symfony\Component\Intl\Exception\RuntimeException; +use Symfony\Component\Intl\ResourceBundle\Util\ArrayAccessibleResourceBundle; + +/** + * Reads binary .res resource bundles. + * + * @author Bernhard Schussek + */ +class BinaryBundleReader extends AbstractBundleReader 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. + $bundle = new \ResourceBundle($locale, $path); + + if (null === $bundle) { + throw new RuntimeException(sprintf( + 'Could not load the resource bundle "%s/%s.res".', + $path, + $locale + )); + } + + return new ArrayAccessibleResourceBundle($bundle); + } + + /** + * {@inheritdoc} + */ + protected function getFileExtension() + { + return 'res'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php new file mode 100644 index 0000000..ad17f9b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.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\Intl\ResourceBundle\Reader; + +use Symfony\Component\Intl\ResourceBundle\Util\RingBuffer; + +/** + * @author Bernhard Schussek + */ +class BufferedBundleReader implements BundleReaderInterface +{ + /** + * @var BundleReaderInterface + */ + private $reader; + + + private $buffer; + + /** + * Buffers a given reader. + * + * @param BundleReaderInterface $reader The reader to buffer. + * @param integer $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]; + } + + /** + * {@inheritdoc} + */ + public function getLocales($path) + { + return $this->reader->getLocales($path); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php new file mode 100644 index 0000000..bc485cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.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\Intl\ResourceBundle\Reader; + +/** + * Reads resource bundle files. + * + * @author Bernhard Schussek + */ +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); + + /** + * Reads the available locales of a resource bundle. + * + * @param string $path The path to the resource bundle. + * + * @return string[] A list of supported locale codes. + */ + public function getLocales($path); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php new file mode 100644 index 0000000..663bcc9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.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\Intl\ResourceBundle\Reader; + +use Symfony\Component\Intl\Exception\InvalidArgumentException; +use Symfony\Component\Intl\Exception\RuntimeException; + +/** + * Reads .php resource bundles. + * + * @author Bernhard Schussek + */ +class PhpBundleReader extends AbstractBundleReader implements BundleReaderInterface +{ + /** + * {@inheritdoc} + */ + public function read($path, $locale) + { + if ('en' !== $locale) { + throw new InvalidArgumentException('Only the locale "en" is supported.'); + } + + $fileName = $path . '/' . $locale . '.php'; + + if (!file_exists($fileName)) { + throw new RuntimeException(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; + } + + /** + * {@inheritdoc} + */ + protected function getFileExtension() + { + return 'php'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php new file mode 100644 index 0000000..e3656fe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.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\Intl\ResourceBundle\Reader; + +use Symfony\Component\Intl\ResourceBundle\Util\RecursiveArrayAccess; + +/** + * A structured reader wrapping an existing resource bundle reader. + * + * @author Bernhard Schussek + * + * @see StructuredResourceBundleBundleReaderInterface + */ +class StructuredBundleReader implements StructuredBundleReaderInterface +{ + /** + * @var BundleReaderInterface + */ + private $reader; + + /** + * 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; + } + + /** + * {@inheritdoc} + */ + public function read($path, $locale) + { + return $this->reader->read($path, $locale); + } + + /** + * {@inheritdoc} + */ + public function getLocales($path) + { + return $this->reader->getLocales($path); + } + + /** + * {@inheritdoc} + */ + public function readEntry($path, $locale, array $indices, $fallback = true) + { + $data = $this->reader->read($path, $locale); + + $entry = RecursiveArrayAccess::get($data, $indices); + $multivalued = is_array($entry) || $entry instanceof \Traversable; + + if (!($fallback && (null === $entry || $multivalued))) { + return $entry; + } + + if (null !== ($fallbackLocale = $this->getFallbackLocale($locale))) { + $parentEntry = $this->readEntry($path, $fallbackLocale, $indices, true); + + if ($entry || $parentEntry) { + $multivalued = $multivalued || is_array($parentEntry) || $parentEntry instanceof \Traversable; + + if ($multivalued) { + if ($entry instanceof \Traversable) { + $entry = iterator_to_array($entry); + } + + if ($parentEntry instanceof \Traversable) { + $parentEntry = iterator_to_array($parentEntry); + } + + $entry = array_merge( + $parentEntry ?: array(), + $entry ?: array() + ); + } else { + $entry = null === $entry ? $parentEntry : $entry; + } + } + } + + return $entry; + } + + /** + * Returns the fallback locale for a given locale, if any + * + * @param string $locale The locale to find the fallback for. + * + * @return string|null The fallback locale, or null if no parent exists + */ + private function getFallbackLocale($locale) + { + if (false === $pos = strrpos($locale, '_')) { + return null; + } + + return substr($locale, 0, $pos); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php new file mode 100644 index 0000000..c22ad93 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.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\Intl\ResourceBundle\Reader; + +/** + * Reads individual entries of a resource file. + * + * @author Bernhard Schussek + */ +interface StructuredBundleReaderInterface 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 Boolean $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, a scalar value for simple data and NULL + * if the given path could not be accessed. + */ + public function readEntry($path, $locale, array $indices, $fallback = true); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/RegionBundle.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/RegionBundle.php new file mode 100644 index 0000000..a3cd9bd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/RegionBundle.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\Intl\ResourceBundle; + +/** + * Default implementation of {@link RegionBundleInterface}. + * + * @author Bernhard Schussek + */ +class RegionBundle extends AbstractBundle implements RegionBundleInterface +{ + /** + * {@inheritdoc} + */ + public function getCountryName($country, $locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + return $this->readEntry($locale, array('Countries', $country)); + } + + /** + * {@inheritdoc} + */ + public function getCountryNames($locale = null) + { + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + if (null === ($countries = $this->readEntry($locale, array('Countries')))) { + return array(); + } + + if ($countries instanceof \Traversable) { + $countries = iterator_to_array($countries); + } + + return $countries; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/RegionBundleInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/RegionBundleInterface.php new file mode 100644 index 0000000..4e55f2d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/RegionBundleInterface.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\Intl\ResourceBundle; + +/** + * Gives access to region-related ICU data. + * + * @author Bernhard Schussek + */ +interface RegionBundleInterface extends ResourceBundleInterface +{ + /** + * Returns the name of a country. + * + * @param string $country A country code (e.g. "US"). + * @param string $locale Optional. The locale to return the name in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string|null The name of the country or NULL if not found. + */ + public function getCountryName($country, $locale = null); + + /** + * Returns the names of all known countries. + * + * @param string $locale Optional. The locale to return the names in. + * Defaults to {@link \Locale::getDefault()}. + * + * @return string[] A list of country names indexed by country codes. + */ + public function getCountryNames($locale = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/ResourceBundleInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/ResourceBundleInterface.php new file mode 100644 index 0000000..497a66a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/ResourceBundleInterface.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\Intl\ResourceBundle; + +/** + * Gives access to ICU data. + * + * @author Bernhard Schussek + */ +interface ResourceBundleInterface +{ + /** + * Returns the list of locales that this bundle supports. + * + * @return string[] A list of locale codes. + */ + public function getLocales(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php new file mode 100644 index 0000000..0692d6f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.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\Intl\ResourceBundle\Transformer; + +use Symfony\Component\Intl\Exception\RuntimeException; +use Symfony\Component\Intl\ResourceBundle\Transformer\Rule\TransformationRuleInterface; +use Symfony\Component\Intl\ResourceBundle\Writer\PhpBundleWriter; + +/** + * Compiles a number of resource bundles based on predefined compilation rules. + * + * @author Bernhard Schussek + */ +class BundleTransformer +{ + /** + * @var TransformationRuleInterface[] + */ + private $rules = array(); + + /** + * Adds a new compilation rule. + * + * @param TransformationRuleInterface $rule The compilation rule. + */ + public function addRule(TransformationRuleInterface $rule) + { + $this->rules[] = $rule; + } + + /** + * Runs the compilation with the given compilation context. + * + * @param CompilationContextInterface $context The context storing information + * needed to run the compilation. + * + * @throws RuntimeException If any of the files to be compiled by the loaded + * compilation rules does not exist. + */ + public function compileBundles(CompilationContextInterface $context) + { + $filesystem = $context->getFilesystem(); + $compiler = $context->getCompiler(); + + $filesystem->remove($context->getBinaryDir()); + $filesystem->mkdir($context->getBinaryDir()); + + foreach ($this->rules as $rule) { + $filesystem->mkdir($context->getBinaryDir() . '/' . $rule->getBundleName()); + + $resources = (array) $rule->beforeCompile($context); + + foreach ($resources as $resource) { + if (!file_exists($resource)) { + throw new RuntimeException(sprintf( + 'The file "%s" to be compiled by %s does not exist.', + $resource, + get_class($rule) + )); + } + + $compiler->compile($resource, $context->getBinaryDir() . '/' . $rule->getBundleName()); + } + + $rule->afterCompile($context); + } + } + + public function createStubs(StubbingContextInterface $context) + { + $filesystem = $context->getFilesystem(); + $phpWriter = new PhpBundleWriter(); + + $filesystem->remove($context->getStubDir()); + $filesystem->mkdir($context->getStubDir()); + + foreach ($this->rules as $rule) { + $filesystem->mkdir($context->getStubDir() . '/' . $rule->getBundleName()); + + $data = $rule->beforeCreateStub($context); + + $phpWriter->write($context->getStubDir() . '/' . $rule->getBundleName(), 'en', $data); + + $rule->afterCreateStub($context); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php new file mode 100644 index 0000000..cdc1951 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.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\Intl\ResourceBundle\Transformer; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompilerInterface; + +/** + * Default implementation of {@link CompilationContextInterface}. + * + * @author Bernhard Schussek + */ +class CompilationContext implements CompilationContextInterface +{ + /** + * @var string + */ + private $sourceDir; + + /** + * @var string + */ + private $binaryDir; + + /** + * @var FileSystem + */ + private $filesystem; + + /** + * @var BundleCompilerInterface + */ + private $compiler; + + /** + * @var string + */ + private $icuVersion; + + public function __construct($sourceDir, $binaryDir, Filesystem $filesystem, BundleCompilerInterface $compiler, $icuVersion) + { + $this->sourceDir = $sourceDir; + $this->binaryDir = $binaryDir; + $this->filesystem = $filesystem; + $this->compiler = $compiler; + $this->icuVersion = $icuVersion; + } + + /** + * {@inheritdoc} + */ + public function getSourceDir() + { + return $this->sourceDir; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDir() + { + return $this->binaryDir; + } + + /** + * {@inheritdoc} + */ + public function getFilesystem() + { + return $this->filesystem; + } + + /** + * {@inheritdoc} + */ + public function getCompiler() + { + return $this->compiler; + } + + /** + * {@inheritdoc} + */ + public function getIcuVersion() + { + return $this->icuVersion; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php new file mode 100644 index 0000000..f05c280 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.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\ResourceBundle\Transformer; + +/** + * Stores contextual information for resource bundle compilation. + * + * @author Bernhard Schussek + */ +interface CompilationContextInterface +{ + /** + * Returns the directory where the source versions of the resource bundles + * are stored. + * + * @return string An absolute path to a directory. + */ + public function getSourceDir(); + + /** + * Returns the directory where the binary resource bundles are stored. + * + * @return string An absolute path to a directory. + */ + public function getBinaryDir(); + + /** + * Returns a tool for manipulating the filesystem. + * + * @return \Symfony\Component\Filesystem\Filesystem The filesystem manipulator. + */ + public function getFilesystem(); + + /** + * Returns a resource bundle compiler. + * + * @return \Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompilerInterface The loaded resource bundle compiler. + */ + public function getCompiler(); + + /** + * Returns the ICU version of the bundles being converted. + * + * @return string The ICU version string. + */ + public function getIcuVersion(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php new file mode 100644 index 0000000..95783b3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.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\Intl\ResourceBundle\Transformer\Rule; + +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\ResourceBundle\CurrencyBundle; +use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface; +use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface; +use Symfony\Component\Intl\Util\IcuVersion; + +/** + * The rule for compiling the currency bundle. + * + * @author Bernhard Schussek + */ +class CurrencyBundleTransformationRule implements TransformationRuleInterface +{ + /** + * {@inheritdoc} + */ + public function getBundleName() + { + return 'curr'; + } + + /** + * {@inheritdoc} + */ + public function beforeCompile(CompilationContextInterface $context) + { + // The currency data is contained in the locales and misc bundles + // in ICU <= 4.2 + if (IcuVersion::compare($context->getIcuVersion(), '4.2', '<=', 1)) { + return array( + $context->getSourceDir() . '/misc/supplementalData.txt', + $context->getSourceDir() . '/locales' + ); + } + + return $context->getSourceDir() . '/curr'; + } + + /** + * {@inheritdoc} + */ + public function afterCompile(CompilationContextInterface $context) + { + // \ResourceBundle does not like locale names with uppercase chars, so rename + // the resource file + // See: http://bugs.php.net/bug.php?id=54025 + $fileName = $context->getBinaryDir() . '/curr/supplementalData.res'; + $fileNameLower = $context->getBinaryDir() . '/curr/supplementaldata.res'; + + $context->getFilesystem()->rename($fileName, $fileNameLower); + } + + /** + * {@inheritdoc} + */ + public function beforeCreateStub(StubbingContextInterface $context) + { + $currencies = array(); + $currencyBundle = Intl::getCurrencyBundle(); + + foreach ($currencyBundle->getCurrencyNames('en') as $code => $name) { + $currencies[$code] = array( + CurrencyBundle::INDEX_NAME => $name, + CurrencyBundle::INDEX_SYMBOL => $currencyBundle->getCurrencySymbol($code, 'en'), + CurrencyBundle::INDEX_FRACTION_DIGITS => $currencyBundle->getFractionDigits($code), + CurrencyBundle::INDEX_ROUNDING_INCREMENT => $currencyBundle->getRoundingIncrement($code), + ); + } + + return array( + 'Currencies' => $currencies, + ); + } + + /** + * {@inheritdoc} + */ + public function afterCreateStub(StubbingContextInterface $context) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php new file mode 100644 index 0000000..5e6f901 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.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\Intl\ResourceBundle\Transformer\Rule; + +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface; +use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface; +use Symfony\Component\Intl\Util\IcuVersion; + +/** + * The rule for compiling the language bundle. + * + * @author Bernhard Schussek + */ +class LanguageBundleTransformationRule implements TransformationRuleInterface +{ + /** + * {@inheritdoc} + */ + public function getBundleName() + { + return 'lang'; + } + + /** + * {@inheritdoc} + */ + public function beforeCompile(CompilationContextInterface $context) + { + // The language data is contained in the locales bundle in ICU <= 4.2 + if (IcuVersion::compare($context->getIcuVersion(), '4.2', '<=', 1)) { + return $context->getSourceDir() . '/locales'; + } + + return $context->getSourceDir() . '/lang'; + } + + /** + * {@inheritdoc} + */ + public function afterCompile(CompilationContextInterface $context) + { + } + + /** + * {@inheritdoc} + */ + public function beforeCreateStub(StubbingContextInterface $context) + { + return array( + 'Languages' => Intl::getLanguageBundle()->getLanguageNames('en'), + 'Scripts' => Intl::getLanguageBundle()->getScriptNames('en'), + ); + } + + /** + * {@inheritdoc} + */ + public function afterCreateStub(StubbingContextInterface $context) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php new file mode 100644 index 0000000..b2576d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.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\Component\Intl\ResourceBundle\Transformer\Rule; + +use Symfony\Component\Intl\Exception\RuntimeException; +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface; +use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface; +use Symfony\Component\Intl\ResourceBundle\Writer\TextBundleWriter; + +/** + * The rule for compiling the locale bundle. + * + * @author Bernhard Schussek + */ +class LocaleBundleTransformationRule implements TransformationRuleInterface +{ + /** + * @var \Symfony\Component\Intl\ResourceBundle\LanguageBundleInterface + */ + private $languageBundle; + + /** + * @var \Symfony\Component\Intl\ResourceBundle\RegionBundleInterface + */ + private $regionBundle; + + public function __construct() + { + $this->languageBundle = Intl::getLanguageBundle(); + $this->regionBundle = Intl::getRegionBundle(); + } + + /** + * {@inheritdoc} + */ + public function getBundleName() + { + return 'locales'; + } + + /** + * {@inheritdoc} + */ + public function beforeCompile(CompilationContextInterface $context) + { + $tempDir = sys_get_temp_dir() . '/icu-data-locales'; + + $context->getFilesystem()->remove($tempDir); + $context->getFilesystem()->mkdir($tempDir); + + $this->generateTextFiles($tempDir, $this->scanLocales($context)); + + return $tempDir; + } + + /** + * {@inheritdoc} + */ + public function afterCompile(CompilationContextInterface $context) + { + $context->getFilesystem()->remove(sys_get_temp_dir() . '/icu-data-locales'); + } + + /** + * {@inheritdoc} + */ + public function beforeCreateStub(StubbingContextInterface $context) + { + return array( + 'Locales' => Intl::getLocaleBundle()->getLocaleNames('en'), + ); + } + + /** + * {@inheritdoc} + */ + public function afterCreateStub(StubbingContextInterface $context) + { + } + + private function scanLocales(CompilationContextInterface $context) + { + $tempDir = sys_get_temp_dir() . '/icu-data-locales-source'; + + $context->getFilesystem()->remove($tempDir); + $context->getFilesystem()->mkdir($tempDir); + + // Temporarily generate the resource bundles + $context->getCompiler()->compile($context->getSourceDir() . '/locales', $tempDir); + + // Discover the list of supported locales, which are the names of the resource + // bundles in the "locales" directory + $locales = glob($tempDir . '/*.res'); + + // Remove file extension and sort + array_walk($locales, function (&$locale) { $locale = basename($locale, '.res'); }); + sort($locales); + + // Delete unneeded locales + foreach ($locales as $key => $locale) { + // Delete all aliases from the list + // i.e., "az_AZ" is an alias for "az_Latn_AZ" + $content = file_get_contents($context->getSourceDir() . '/locales/' . $locale . '.txt'); + + // The key "%%ALIAS" is not accessible through the \ResourceBundle class, + // so look in the original .txt file instead + if (strpos($content, '%%ALIAS') !== false) { + unset($locales[$key]); + } + + // Delete locales that have no content (i.e. only "Version" key) + $bundle = new \ResourceBundle($locale, $tempDir); + + if (null === $bundle) { + throw new RuntimeException('The resource bundle for locale ' . $locale . ' could not be loaded from directory ' . $tempDir); + } + + // There seems to be no other way for identifying all keys in this specific + // resource bundle + if (array_keys(iterator_to_array($bundle)) === array('Version')) { + unset($locales[$key]); + } + } + + $context->getFilesystem()->remove($tempDir); + + return $locales; + } + + private function generateTextFiles($targetDirectory, array $locales) + { + $displayLocales = array_unique(array_merge( + $this->languageBundle->getLocales(), + $this->regionBundle->getLocales() + )); + + $txtWriter = new TextBundleWriter(); + + // Generate a list of locale names in the language of each display locale + // Each locale name has the form: "Language (Script, Region, Variant1, ...) + // Script, Region and Variants are optional. If none of them is available, + // the braces are not printed. + foreach ($displayLocales as $displayLocale) { + // Don't include ICU's root resource bundle + if ('root' === $displayLocale) { + continue; + } + + $names = array(); + + foreach ($locales as $locale) { + // Don't include ICU's root resource bundle + if ($locale === 'root') { + continue; + } + + if (null !== ($name = $this->generateLocaleName($locale, $displayLocale))) { + $names[$locale] = $name; + } + } + + // If no names could be generated for the current locale, skip it + if (0 === count($names)) { + continue; + } + + $txtWriter->write($targetDirectory, $displayLocale, array('Locales' => $names)); + } + } + + private function generateLocaleName($locale, $displayLocale) + { + $name = null; + + $lang = \Locale::getPrimaryLanguage($locale); + $script = \Locale::getScript($locale); + $region = \Locale::getRegion($locale); + $variants = \Locale::getAllVariants($locale); + + // Currently the only available variant is POSIX, which we don't want + // to include in the list + if (count($variants) > 0) { + return null; + } + + // Some languages are translated together with their region, + // i.e. "en_GB" is translated as "British English" + // we don't include these languages though because they mess up + // the name sorting + // $name = $this->langBundle->getLanguageName($displayLocale, $lang, $region); + + // Some languages are simply not translated + // Example: "az" (Azerbaijani) has no translation in "af" (Afrikaans) + if (null === ($name = $this->languageBundle->getLanguageName($lang, null, $displayLocale))) { + return null; + } + + // "as" (Assamese) has no "Variants" block + //if (!$langBundle->get('Variants')) { + // continue; + //} + + $extras = array(); + + // Discover the name of the script part of the locale + // i.e. in zh_Hans_MO, "Hans" is the script + if ($script) { + // Some scripts are not translated into every language + if (null === ($scriptName = $this->languageBundle->getScriptName($script, $lang, $displayLocale))) { + return null; + } + + $extras[] = $scriptName; + } + + // Discover the name of the region part of the locale + // i.e. in de_AT, "AT" is the region + if ($region) { + // Some regions are not translated into every language + if (null === ($regionName = $this->regionBundle->getCountryName($region, $displayLocale))) { + return null; + } + + $extras[] = $regionName; + } + + if (count($extras) > 0) { + // Remove any existing extras + // For example, in German, zh_Hans is "Chinesisch (vereinfacht)". + // The latter is the script part which is already included in the + // extras and will be appended again with the other extras. + if (preg_match('/^(.+)\s+\([^\)]+\)$/', $name, $matches)) { + $name = $matches[1]; + } + + $name .= ' ('.implode(', ', $extras).')'; + } + + return $name; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php new file mode 100644 index 0000000..52fdbed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.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\Intl\ResourceBundle\Transformer\Rule; + +use Symfony\Component\Intl\Intl; +use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface; +use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface; +use Symfony\Component\Intl\Util\IcuVersion; + +/** + * The rule for compiling the region bundle. + * + * @author Bernhard Schussek + */ +class RegionBundleTransformationRule implements TransformationRuleInterface +{ + /** + * {@inheritdoc} + */ + public function getBundleName() + { + return 'region'; + } + + /** + * {@inheritdoc} + */ + public function beforeCompile(CompilationContextInterface $context) + { + // The region data is contained in the locales bundle in ICU <= 4.2 + if (IcuVersion::compare($context->getIcuVersion(), '4.2', '<=', 1)) { + return $context->getSourceDir() . '/locales'; + } + + return $context->getSourceDir() . '/region'; + } + + /** + * {@inheritdoc} + */ + public function afterCompile(CompilationContextInterface $context) + { + } + + /** + * {@inheritdoc} + */ + public function beforeCreateStub(StubbingContextInterface $context) + { + return array( + 'Countries' => Intl::getRegionBundle()->getCountryNames('en'), + ); + } + + /** + * {@inheritdoc} + */ + public function afterCreateStub(StubbingContextInterface $context) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php new file mode 100644 index 0000000..3965e0d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.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\Intl\ResourceBundle\Transformer\Rule; + +use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface; +use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface; + +/** + * Contains instruction for compiling a resource bundle. + * + * @author Bernhard Schussek + */ +interface TransformationRuleInterface +{ + /** + * Returns the name of the compiled resource bundle. + * + * @return string The name of the bundle. + */ + public function getBundleName(); + + /** + * Runs instructions to be executed before compiling the sources of the + * resource bundle. + * + * @param CompilationContextInterface $context The contextual information of + * the compilation. + * + * @return string[] The source directories/files of the bundle. + */ + public function beforeCompile(CompilationContextInterface $context); + + /** + * Runs instructions to be executed after compiling the sources of the + * resource bundle. + * + * @param CompilationContextInterface $context The contextual information of + * the compilation. + */ + public function afterCompile(CompilationContextInterface $context); + + /** + * Runs instructions to be executed before creating the stub version of the + * resource bundle. + * + * @param StubbingContextInterface $context The contextual information of + * the compilation. + * + * @return mixed The data to include in the stub version. + */ + public function beforeCreateStub(StubbingContextInterface $context); + + /** + * Runs instructions to be executed after creating the stub version of the + * resource bundle. + * + * @param StubbingContextInterface $context The contextual information of + * the compilation. + */ + public function afterCreateStub(StubbingContextInterface $context); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php new file mode 100644 index 0000000..25ab68d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.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\Intl\ResourceBundle\Transformer; + +use Symfony\Component\Filesystem\Filesystem; + +/** + * @author Bernhard Schussek + */ +class StubbingContext implements StubbingContextInterface +{ + /** + * @var string + */ + private $binaryDir; + + /** + * @var string + */ + private $stubDir; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var string + */ + private $icuVersion; + + public function __construct($binaryDir, $stubDir, Filesystem $filesystem, $icuVersion) + { + $this->binaryDir = $binaryDir; + $this->stubDir = $stubDir; + $this->filesystem = $filesystem; + $this->icuVersion = $icuVersion; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDir() + { + return $this->binaryDir; + } + + /** + * {@inheritdoc} + */ + public function getStubDir() + { + return $this->stubDir; + } + + /** + * {@inheritdoc} + */ + public function getFilesystem() + { + return $this->filesystem; + } + + /** + * {@inheritdoc} + */ + public function getIcuVersion() + { + return $this->icuVersion; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php new file mode 100644 index 0000000..dc49255 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.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\Intl\ResourceBundle\Transformer; + +/** + * @author Bernhard Schussek + */ +interface StubbingContextInterface +{ + /** + * Returns the directory where the binary resource bundles are stored. + * + * @return string An absolute path to a directory. + */ + public function getBinaryDir(); + + /** + * Returns the directory where the stub resource bundles are stored. + * + * @return string An absolute path to a directory. + */ + public function getStubDir(); + + /** + * Returns a tool for manipulating the filesystem. + * + * @return \Symfony\Component\Filesystem\Filesystem The filesystem manipulator. + */ + public function getFilesystem(); + + /** + * Returns the ICU version of the bundles being converted. + * + * @return string The ICU version string. + */ + public function getIcuVersion(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php new file mode 100644 index 0000000..9a4cccb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.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\Intl\ResourceBundle\Util; + +use Symfony\Component\Intl\Exception\BadMethodCallException; + +/** + * Work-around for a bug in PHP's \ResourceBundle implementation. + * + * More information can be found on https://bugs.php.net/bug.php?id=64356. + * This class can be removed once that bug is fixed. + * + * @author Bernhard Schussek + */ +class ArrayAccessibleResourceBundle implements \ArrayAccess, \IteratorAggregate, \Countable +{ + private $bundleImpl; + + public function __construct(\ResourceBundle $bundleImpl) + { + $this->bundleImpl = $bundleImpl; + } + + public function get($offset, $fallback = null) + { + $value = $this->bundleImpl->get($offset, $fallback); + + return $value instanceof \ResourceBundle ? new static($value) : $value; + } + + public function offsetExists($offset) + { + return null !== $this->bundleImpl[$offset]; + } + + public function offsetGet($offset) + { + return $this->get($offset); + } + + public function offsetSet($offset, $value) + { + throw new BadMethodCallException('Resource bundles cannot be modified.'); + } + + public function offsetUnset($offset) + { + throw new BadMethodCallException('Resource bundles cannot be modified.'); + } + + public function getIterator() + { + return $this->bundleImpl; + } + + public function count() + { + return $this->bundleImpl->count(); + } + + public function getErrorCode() + { + return $this->bundleImpl->getErrorCode(); + } + + public function getErrorMessage() + { + return $this->bundleImpl->getErrorMessage(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php new file mode 100644 index 0000000..e1feaa2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.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\ResourceBundle\Util; + +/** + * @author Bernhard Schussek + */ +class RecursiveArrayAccess +{ + public static function get($array, array $indices) + { + foreach ($indices as $index) { + if (!$array instanceof \ArrayAccess && !is_array($array)) { + return null; + } + + $array = $array[$index]; + } + + return $array; + } + + private function __construct() {} +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php new file mode 100644 index 0000000..7ccbd1e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.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\Intl\ResourceBundle\Util; + +use Symfony\Component\Intl\Exception\OutOfBoundsException; + +/** + * Implements a ring buffer. + * + * A ring buffer is an array-like structure with a fixed size. If the buffer + * is full, the next written element overwrites the first bucket in the buffer, + * then the second and so on. + * + * @author Bernhard Schussek + */ +class RingBuffer implements \ArrayAccess +{ + private $values = array(); + + private $indices = array(); + + private $cursor = 0; + + private $size; + + public function __construct($size) + { + $this->size = $size; + } + + /** + * {@inheritdoc} + */ + public function offsetExists($key) + { + return isset($this->indices[$key]); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($key) + { + if (!isset($this->indices[$key])) { + throw new OutOfBoundsException(sprintf( + 'The index "%s" does not exist.', + $key + )); + } + + return $this->values[$this->indices[$key]]; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($key, $value) + { + if (false !== ($keyToRemove = array_search($this->cursor, $this->indices))) { + unset($this->indices[$keyToRemove]); + } + + $this->values[$this->cursor] = $value; + $this->indices[$key] = $this->cursor; + + $this->cursor = ($this->cursor + 1) % $this->size; + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($key) + { + if (isset($this->indices[$key])) { + $this->values[$this->indices[$key]] = null; + unset($this->indices[$key]); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php new file mode 100644 index 0000000..cc3b958 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.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\Intl\ResourceBundle\Writer; + +/** + * Writes resource bundle files. + * + * @author Bernhard Schussek + */ +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/ResourceBundle/Writer/PhpBundleWriter.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.php new file mode 100644 index 0000000..5738f1d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.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\Intl\ResourceBundle\Writer; + +/** + * Writes .php resource bundles. + * + * @author Bernhard Schussek + */ +class PhpBundleWriter implements BundleWriterInterface +{ + /** + * {@inheritdoc} + */ + function write($path, $locale, $data) + { + $template = <<