diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 12b1d80..816c801 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -3,6 +3,7 @@ use Drupal\Core\Database\Database; use Symfony\Component\ClassLoader\UniversalClassLoader; use Symfony\Component\ClassLoader\ApcUniversalClassLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; /** * @file @@ -1353,12 +1354,11 @@ function drupal_unpack($obj, $field = 'data') { * @ingroup sanitization */ function t($string, array $args = array(), array $options = array()) { - global $language_interface; static $custom_strings; // Merge in default. if (empty($options['langcode'])) { - $options['langcode'] = isset($language_interface->langcode) ? $language_interface->langcode : LANGUAGE_SYSTEM; + $options['langcode'] = drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode; } if (empty($options['context'])) { $options['context'] = ''; @@ -2304,6 +2304,40 @@ function drupal_get_bootstrap_phase() { } /** + * Retrieves the Drupal Container to standardize object construction. + * + * Example: + * @code + * // Register the LANGUAGE_TYPE_INTERFACE definition. Registered definitions + * // do not necessarily need to be named by a constant. + * // See http://symfony.com/doc/current/components/dependency_injection.html + * // for usage examples of adding object initialization code after register(). + * $container = drupal_container(); + * $container->register(LANGUAGE_TYPE_INTERFACE, 'Drupal\\Core\\Language\\Language'); + * + * // Retrieve the LANGUAGE_TYPE_INTERFACE object. + * $language_interface = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); + * @endcode + * + * @param $reset + * TRUE or FALSE depending on whether the Container instance is to be reset. + * + * @return Symfony\Component\DependencyInjection\ContainerBuilder + * The instance of the Drupal Container used to set up and maintain object + * instances. + */ +function drupal_container($reset = FALSE) { + // We do not use drupal_static() here because we do not have a mechanism by + // which to reinitialize the stored objects, so a drupal_static_reset() call + // would leave Drupal in a nonfunctional state. + static $container = NULL; + if ($reset || !isset($container)) { + $container = new ContainerBuilder(); + } + return $container; +} + +/** * Returns the test prefix if this is an internal request from SimpleTest. * * @return @@ -2445,27 +2479,44 @@ function get_t() { /** * Initializes all the defined language types. + * + * @see Drupal\Core\Language\Language */ function drupal_language_initialize() { $types = language_types_get_all(); + $container = drupal_container(); - // Ensure the language is correctly returned, even without multilanguage - // support. Also make sure we have a $language fallback, in case a language - // negotiation callback needs to do a full bootstrap. - // Useful for eg. XML/HTML 'lang' attributes. - $default = language_default(); - foreach ($types as $type) { - $GLOBALS[$type] = $default; - } + // Ensure a language object is registered for each language type, whether the + // site is multilingual or not. if (language_multilingual()) { include_once DRUPAL_ROOT . '/core/includes/language.inc'; foreach ($types as $type) { - $GLOBALS[$type] = language_types_initialize($type); + $language = language_types_initialize($type); + $container->set($type, NULL); + $container->register($type, 'Drupal\\Core\\Language\\Language') + ->addMethodCall('extend', array($language)); } // Allow modules to react on language system initialization in multilingual // environments. bootstrap_invoke_all('language_init'); } + else { + $default = language_default(); + foreach ($types as $type) { + $container->set($type, NULL); + $container->register($type, 'Drupal\\Core\\Language\\Language') + ->addMethodCall('extend', array($default)); + } + } + + // @todo Temporary backwards compatibility for code still using globals. + // Remove after these issues: + // - $language_interface: http://drupal.org/node/1510686 + // - $language_url: http://drupal.org/node/1512310 + // - $language_content: http://drupal.org/node/1512308 + foreach ($types as $type) { + $GLOBALS[$type] = $container->get($type); + } } /** diff --git a/core/includes/common.inc b/core/includes/common.inc index 5ff6167..61abd2b 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -1653,8 +1653,7 @@ function filter_xss_bad_protocol($string, $decode = TRUE) { * Arbitrary elements may be added using the $args associative array. */ function format_rss_channel($title, $link, $description, $items, $langcode = NULL, $args = array()) { - global $language_content; - $langcode = $langcode ? $langcode : $language_content->langcode; + $langcode = $langcode ? $langcode : drupal_container()->get(LANGUAGE_TYPE_CONTENT)->langcode; $output = "\n"; $output .= ' ' . check_plain($title) . "\n"; @@ -1955,10 +1954,8 @@ function format_date($timestamp, $type = 'medium', $format = '', $timezone = NUL $timezones[$timezone] = timezone_open($timezone); } - // Use the default langcode if none is set. - global $language_interface; if (empty($langcode)) { - $langcode = isset($language_interface->langcode) ? $language_interface->langcode : LANGUAGE_SYSTEM; + $langcode = drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode; } switch ($type) { @@ -2398,7 +2395,6 @@ function drupal_attributes(array $attributes = array()) { * An HTML string containing a link to the given path. */ function l($text, $path, array $options = array()) { - global $language_url; static $use_theme = NULL; // Merge in defaults. @@ -2409,7 +2405,7 @@ function l($text, $path, array $options = array()) { // Append active class. if (($path == $_GET['q'] || ($path == '' && drupal_is_front_page())) && - (empty($options['language']) || $options['language']->langcode == $language_url->langcode)) { + (empty($options['language']) || $options['language']->langcode == drupal_container()->get(LANGUAGE_TYPE_URL)->langcode)) { $options['attributes']['class'][] = 'active'; } @@ -2565,9 +2561,7 @@ function drupal_deliver_html_page($page_callback_result) { drupal_add_http_header('X-UA-Compatible', 'IE=edge,chrome=1'); } - // Send appropriate HTTP-Header for browsers and search engines. - global $language_interface; - drupal_add_http_header('Content-Language', $language_interface->langcode); + drupal_add_http_header('Content-Language', drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode); // Menu status constants are integers; page content is a string or array. if (is_int($page_callback_result)) { diff --git a/core/includes/language.inc b/core/includes/language.inc index 2c46c76..01d418a 100644 --- a/core/includes/language.inc +++ b/core/includes/language.inc @@ -25,6 +25,10 @@ function language_types_initialize($type) { $negotiation = variable_get("language_negotiation_$type", array()); foreach ($negotiation as $method_id => $method) { + // Skip negotiation methods not appropriate for this type. + if (isset($method['types']) && !in_array($type, $method['types'])) { + continue; + } $language = language_negotiation_method_invoke($method_id, $method); if ($language) { // Remember the method ID used to detect the language. diff --git a/core/includes/menu.inc b/core/includes/menu.inc index fecd158..96791e3 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -1096,10 +1096,12 @@ function menu_tree_output($tree) { function menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) { $tree = &drupal_static(__FUNCTION__, array()); + $language_interface = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); + // Use $mlid as a flag for whether the data being loaded is for the whole tree. $mlid = isset($link['mlid']) ? $link['mlid'] : 0; // Generate a cache ID (cid) specific for this $menu_name, $link, $language, and depth. - $cid = 'links:' . $menu_name . ':all:' . $mlid . ':' . $GLOBALS['language_interface']->langcode . ':' . (int) $max_depth; + $cid = 'links:' . $menu_name . ':all:' . $mlid . ':' . $language_interface->langcode . ':' . (int) $max_depth; if (!isset($tree[$cid])) { // If the static variable doesn't have the data, check {cache_menu}. @@ -1205,6 +1207,8 @@ function menu_tree_get_path($menu_name) { function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail = FALSE) { $tree = &drupal_static(__FUNCTION__, array()); + $language_interface = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); + // Check if the active trail has been overridden for this menu tree. $active_path = menu_tree_get_path($menu_name); // Load the menu item corresponding to the current page. @@ -1213,7 +1217,7 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail = $max_depth = min($max_depth, MENU_MAX_DEPTH); } // Generate a cache ID (cid) specific for this page. - $cid = 'links:' . $menu_name . ':page:' . $item['href'] . ':' . $GLOBALS['language_interface']->langcode . ':' . (int) $item['access'] . ':' . (int) $max_depth; + $cid = 'links:' . $menu_name . ':page:' . $item['href'] . ':' . $language_interface->langcode . ':' . (int) $item['access'] . ':' . (int) $max_depth; // If we are asked for the active trail only, and $menu_name has not been // built and cached for this page yet, then this likely means that it // won't be built anymore, as this function is invoked from @@ -1360,12 +1364,14 @@ function _menu_build_tree($menu_name, array $parameters = array()) { // Static cache of already built menu trees. $trees = &drupal_static(__FUNCTION__, array()); + $language_interface = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); + // Build the cache id; sort parents to prevent duplicate storage and remove // default parameter values. if (isset($parameters['expanded'])) { sort($parameters['expanded']); } - $tree_cid = 'links:' . $menu_name . ':tree-data:' . $GLOBALS['language_interface']->langcode . ':' . hash('sha256', serialize($parameters)); + $tree_cid = 'links:' . $menu_name . ':tree-data:' . $language_interface->langcode . ':' . hash('sha256', serialize($parameters)); // If we do not have this tree in the static cache, check {cache_menu}. if (!isset($trees[$tree_cid])) { diff --git a/core/includes/path.inc b/core/includes/path.inc index 223ab04..d6a0201 100644 --- a/core/includes/path.inc +++ b/core/includes/path.inc @@ -43,7 +43,6 @@ function drupal_path_initialize() { * found. */ function drupal_lookup_path($action, $path = '', $langcode = NULL) { - global $language_url; // Use the advanced drupal_static() pattern, since this is called very often. static $drupal_static_fast; if (!isset($drupal_static_fast)) { @@ -74,7 +73,7 @@ function drupal_lookup_path($action, $path = '', $langcode = NULL) { // language. If we used a language different from the one conveyed by the // requested URL, we might end up being unable to check if there is a path // alias matching the URL path. - $langcode = $langcode ? $langcode : $language_url->langcode; + $langcode = $langcode ? $langcode : drupal_container()->get(LANGUAGE_TYPE_URL)->langcode; if ($action == 'wipe') { $cache = array(); diff --git a/core/includes/theme.inc b/core/includes/theme.inc index f5c25f9..6872600 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1684,7 +1684,7 @@ function theme_link($variables) { * http://www.w3.org/TR/WCAG-TECHS/H42.html for more information. */ function theme_links($variables) { - global $language_url; + $language_url = drupal_container()->get(LANGUAGE_TYPE_URL); $links = $variables['links']; $attributes = $variables['attributes']; @@ -2466,6 +2466,8 @@ function template_process(&$variables, $hook) { * @see html.tpl.php */ function template_preprocess_html(&$variables) { + $language_interface = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); + // Compile a list of classes that are going to be applied to the body element. // This allows advanced theming based on context (home page, node of certain type, etc.). // Add a class that tells us whether we're on the front page or not. @@ -2509,8 +2511,8 @@ function template_preprocess_html(&$variables) { $variables['body_attributes_array'] = array(); // HTML element attributes. - $variables['html_attributes_array']['lang'] = $GLOBALS['language_interface']->langcode; - $variables['html_attributes_array']['dir'] = $GLOBALS['language_interface']->direction ? 'rtl' : 'ltr'; + $variables['html_attributes_array']['lang'] = $language_interface->langcode; + $variables['html_attributes_array']['dir'] = $language_interface->direction ? 'rtl' : 'ltr'; // Add favicon. if (theme_get_setting('toggle_favicon')) { @@ -2559,6 +2561,8 @@ function template_preprocess_html(&$variables) { * @see page.tpl.php */ function template_preprocess_page(&$variables) { + $language_interface = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); + // Move some variables to the top level for themer convenience and template cleanliness. $variables['show_messages'] = $variables['page']['#show_messages']; @@ -2580,8 +2584,8 @@ function template_preprocess_page(&$variables) { $variables['base_path'] = base_path(); $variables['front_page'] = url(); $variables['feed_icons'] = drupal_get_feeds(); - $variables['language'] = $GLOBALS['language_interface']; - $variables['language']->dir = $GLOBALS['language_interface']->direction ? 'rtl' : 'ltr'; + $variables['language'] = $language_interface; + $variables['language']->dir = $language_interface->direction ? 'rtl' : 'ltr'; $variables['logo'] = theme_get_setting('logo'); $variables['main_menu'] = theme_get_setting('toggle_main_menu') ? menu_main_menu() : array(); $variables['secondary_menu'] = theme_get_setting('toggle_secondary_menu') ? menu_secondary_menu() : array(); @@ -2782,8 +2786,7 @@ function template_preprocess_maintenance_page(&$variables) { } } - // set the default language if necessary - $language = isset($GLOBALS['language_interface']) ? $GLOBALS['language_interface'] : language_default(); + $language_interface = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); $variables['head_title_array'] = $head_title; $variables['head_title'] = implode(' | ', $head_title); @@ -2792,8 +2795,8 @@ function template_preprocess_maintenance_page(&$variables) { $variables['breadcrumb'] = ''; $variables['feed_icons'] = ''; $variables['help'] = ''; - $variables['language'] = $language; - $variables['language']->dir = $language->direction ? 'rtl' : 'ltr'; + $variables['language'] = $language_interface; + $variables['language']->dir = $language_interface->direction ? 'rtl' : 'ltr'; $variables['logo'] = theme_get_setting('logo'); $variables['messages'] = $variables['show_messages'] ? theme('status_messages') : ''; $variables['main_menu'] = array(); diff --git a/core/lib/Drupal/Core/Language/Language.php b/core/lib/Drupal/Core/Language/Language.php new file mode 100644 index 0000000..9b9223d --- /dev/null +++ b/core/lib/Drupal/Core/Language/Language.php @@ -0,0 +1,53 @@ + $value) { + $this->$name = $value; + } + } + + /** + * Extend $this with properties from the given object. + * + * @todo Remove this function once $GLOBALS['language'] is gone. + */ + public function extend($obj) { + $vars = get_object_vars($obj); + foreach ($vars as $var => $value) { + $this->$var = $value; + } + } +} diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc index c90457f..56deb3e 100644 --- a/core/modules/language/language.negotiation.inc +++ b/core/modules/language/language.negotiation.inc @@ -52,8 +52,7 @@ const LANGUAGE_NEGOTIATION_URL_DOMAIN = 1; * The current interface language code. */ function language_from_interface() { - global $language_interface; - return isset($language_interface->langcode) ? $language_interface->langcode : FALSE; + return drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode; } /** @@ -284,7 +283,7 @@ function language_url_fallback($language = NULL, $language_type = LANGUAGE_TYPE_ return $default->langcode; } else { - return $GLOBALS[$language_type]->langcode; + return drupal_container()->get($language_type)->langcode; } } @@ -361,7 +360,7 @@ function language_url_rewrite_url(&$path, &$options) { // Language can be passed as an option, or we go for current URL language. if (!isset($options['language'])) { - global $language_url; + $language_url = drupal_container()->get(LANGUAGE_TYPE_URL); $options['language'] = $language_url; } // We allow only enabled languages here. diff --git a/core/modules/language/language.test b/core/modules/language/language.test index 6c64c29..3195141 100644 --- a/core/modules/language/language.test +++ b/core/modules/language/language.test @@ -6,9 +6,9 @@ * * The test file includes: * - a functional test for the language configuration forms; + * - comparison of $GLOBALS default language against dependency injection; */ - /** * Functional tests for the language list configuration forms. */ @@ -179,3 +179,90 @@ class LanguageListTest extends DrupalWebTestCase { $this->assertRaw(t('The %language (%langcode) language has been removed.', $t_args), t('The English language has been removed.')); } } + +/** + * Test for dependency injected language object. + */ +class LanguageDependencyInjectionTest extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Language dependency injection', + 'description' => 'Compares the default language from $GLOBALS against the dependency injected language object.', + 'group' => 'Language', + ); + } + + function setUp() { + parent::setUp('language'); + + // Set up a new container to ensure we are building a new Language object + // for each test. + drupal_container(TRUE); + } + + /** + * Test dependency injected Language against the GLOBAL language object. + * + * @todo Once the PHP global is gone, we won't need this test as the same + * test is done without the PHP global in the following test. + */ + function testDependencyInjectedLanguage() { + // Initialize the language system. + drupal_language_initialize(); + + $expected = $GLOBALS[LANGUAGE_TYPE_INTERFACE]; + $result = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); + foreach ($expected as $property => $value) { + $this->assertEqual($expected->$property, $result->$property, t('The dependency injected language object %prop property equals the $GLOBAL language object %prop property.', array('%prop' => $property))); + } + } + + /** + * Test dependency injected languages against a new Language object. + * + * @see Drupal\Core\Language\Language + */ + function testDependencyInjectedNewLanguage() { + // Initialize the language system. + drupal_language_initialize(); + + $expected = new Drupal\Core\Language\Language((array) language_default()); + $result = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); + foreach ($expected as $property => $value) { + $this->assertEqual($expected->$property, $result->$property, t('The dependency injected language object %prop property equals the new Language object %prop property.', array('%prop' => $property))); + } + } + + /** + * Test dependency injected Language object against a new default language + * object. + * + * @see Drupal\Core\Language\Language + */ + function testDependencyInjectedNewDefaultLanguage() { + // Change the language default object to different values. + $new_language_default = (object) array( + 'langcode' => 'fr', + 'name' => 'French', + 'direction' => 0, + 'enabled' => 1, + 'weight' => 0, + 'default' => TRUE, + ); + variable_set('language_default', $new_language_default); + + // Initialize the language system. + drupal_language_initialize(); + + // The langauge system creates a Language object which contains the + // same properties as the new default language object. + $expected = $new_language_default; + $result = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); + foreach ($expected as $property => $value) { + $this->assertEqual($expected->$property, $result->$property, t('The dependency injected language object %prop property equals the default language object %prop property.', array('%prop' => $property))); + } + + // Delete the language_default variable we previously set. + variable_del('language_default'); + } +} diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index 6f81480..bc34524 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -1064,8 +1064,7 @@ function _locale_invalidate_js($langcode = NULL) { */ function _locale_rebuild_js($langcode = NULL) { if (!isset($langcode)) { - global $language_interface; - $language = $language_interface; + $language = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); } else { // Get information about the locale. diff --git a/core/modules/system/language.api.php b/core/modules/system/language.api.php index aa8fd2f..9f28619 100644 --- a/core/modules/system/language.api.php +++ b/core/modules/system/language.api.php @@ -24,9 +24,9 @@ * did not happen yet and thus they cannot rely on translated variables. */ function hook_language_init() { - global $language_interface, $conf; + global $conf; - switch ($language_interface->langcode) { + switch (drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode) { case 'it': $conf['site_name'] = 'Il mio sito Drupal'; break; diff --git a/core/modules/system/tests/common.test b/core/modules/system/tests/common.test index dbb471e..cc51a01 100644 --- a/core/modules/system/tests/common.test +++ b/core/modules/system/tests/common.test @@ -2438,7 +2438,9 @@ class CommonFormatDateTestCase extends DrupalWebTestCase { * Tests for the format_date() function. */ function testFormatDate() { - global $user, $language_interface; + global $user; + + $language_interface = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); $timestamp = strtotime('2007-03-26T00:00:00+00:00'); $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', t('Test all parameters.')); diff --git a/core/update.php b/core/update.php index 3d858a2..9797833 100644 --- a/core/update.php +++ b/core/update.php @@ -382,8 +382,15 @@ drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION); // The interface language global has been renamed in D8, we must ensure that it // contains a valid value while language settings are upgraded. +// @todo Remove this globals reference entirely: http://drupal.org/node/1510686 $GLOBALS[LANGUAGE_TYPE_INTERFACE] = language_default(); +// Ensure the default language is properly registered within the Dependency +// Injection container during the upgrade process. +$default = language_default(); +drupal_container()->register(LANGUAGE_TYPE_INTERFACE, 'Drupal\\Core\\Language\\Language') + ->addMethodCall('extend', array($default)); + // Only allow the requirements check to proceed if the current user has access // to run updates (since it may expose sensitive information about the site's // configuration). diff --git a/core/vendor/Symfony/Component/DependencyInjection/Alias.php b/core/vendor/Symfony/Component/DependencyInjection/Alias.php new file mode 100644 index 0000000..9a377ed --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php new file mode 100644 index 0000000..b565f3f --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.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\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()); + } + } + + 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 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/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php new file mode 100644 index 0000000..4f353d1 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php @@ -0,0 +1,70 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +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 array $edges An array of Nodes + * + * @throws ServiceCircularReferenceException When a circular reference is found. + */ + private function checkOutEdges(array $edges) + { + foreach ($edges as $edge) { + $node = $edge->getDestNode(); + $this->currentPath[] = $id = $node->getId(); + + if ($this->currentId === $id) { + throw new ServiceCircularReferenceException($this->currentId, $this->currentPath); + } + + $this->checkOutEdges($node->getOutEdges()); + array_pop($this->currentPath); + } + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php new file mode 100644 index 0000000..ad55b34 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php @@ -0,0 +1,79 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +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/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php new file mode 100644 index 0000000..2cf957a --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php @@ -0,0 +1,64 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +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/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php new file mode 100644 index 0000000..d271d9b --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php @@ -0,0 +1,167 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +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 = $children[$scope]; + $this->currentScopeAncestors = $ancestors[$scope]; + } + + $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/core/vendor/Symfony/Component/DependencyInjection/Compiler/Compiler.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/Compiler.php new file mode 100644 index 0000000..01f224b --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Compiler/CompilerPassInterface.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/CompilerPassInterface.php new file mode 100644 index 0000000..1ae8bb9 --- /dev/null +++ b/core/vendor/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 + */ + function process(ContainerBuilder $container); +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php new file mode 100644 index 0000000..9e21972 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.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\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 + */ + 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->isInlinableDefinition($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 isInlinableDefinition(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/core/vendor/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php new file mode 100644 index 0000000..daee911 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php @@ -0,0 +1,46 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +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/core/vendor/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php new file mode 100644 index 0000000..a9beb5b --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.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\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * 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 $name => $extension) { + if (!$config = $container->getExtensionConfig($name)) { + // this extension was not called + continue; + } + $config = $container->getParameterBag()->resolveValue($config); + + $tmpContainer = new ContainerBuilder($container->getParameterBag()); + $tmpContainer->addObjectResource($extension); + + $extension->load($config, $tmpContainer); + + $container->merge($tmpContainer); + } + + $container->addDefinitions($definitions); + $container->addAliases($aliases); + $container->getParameterBag()->add($parameters); + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/PassConfig.php new file mode 100644 index 0000000..eb2266b --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Compiler/RemoveAbstractDefinitionsPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/RemoveAbstractDefinitionsPass.php new file mode 100644 index 0000000..0f6ff5a --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/RemoveAbstractDefinitionsPass.php @@ -0,0 +1,39 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +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/core/vendor/Symfony/Component/DependencyInjection/Compiler/RemovePrivateAliasesPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/RemovePrivateAliasesPass.php new file mode 100644 index 0000000..4842337 --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php new file mode 100644 index 0000000..0c7be66 --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Compiler/RepeatablePassInterface.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/RepeatablePassInterface.php new file mode 100644 index 0000000..81209c3 --- /dev/null +++ b/core/vendor/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 + */ + function setRepeatedPass(RepeatedPass $repeatedPass); +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php new file mode 100644 index 0000000..d4af431 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\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 +{ + private $repeat; + private $passes; + + /** + * Constructor. + * + * @param array $passes An array of RepeatablePassInterface objects + * @throws InvalidArgumentException if a pass is not a RepeatablePassInterface instance + */ + 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 array An array of RepeatablePassInterface objects + */ + public function getPasses() + { + return $this->passes; + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php new file mode 100644 index 0000000..dc83807 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +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 + */ + public function process(ContainerBuilder $container) + { + $this->compiler = $container->getCompiler(); + $this->formatter = $this->compiler->getLoggingFormatter(); + + foreach ($container->getAliases() as $id => $alias) { + $aliasId = (string) $alias; + + $definition = $container->getDefinition($aliasId); + + 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 + */ + 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/core/vendor/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php new file mode 100644 index 0000000..5242f41 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -0,0 +1,148 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +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 + */ + 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/core/vendor/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php new file mode 100644 index 0000000..6d3c78a --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.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\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 + */ + 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::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { + $arguments[$k] = new Reference($id); + } elseif (!$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/core/vendor/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php new file mode 100644 index 0000000..f6e4c85 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.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\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 + */ + 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/core/vendor/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php new file mode 100644 index 0000000..015bdeb --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php new file mode 100644 index 0000000..b241bf8 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.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\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 +{ + private $nodes; + + /** + * Constructor. + */ + public function __construct() + { + $this->nodes = array(); + } + + /** + * Checks if the graph has a specific node. + * + * @param string $id Id to check + */ + 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 array 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/core/vendor/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php new file mode 100644 index 0000000..60a9295 --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php b/core/vendor/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php new file mode 100644 index 0000000..da8dc70 --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Container.php b/core/vendor/Symfony/Component/DependencyInjection/Container.php new file mode 100644 index 0000000..759bd45 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Container.php @@ -0,0 +1,455 @@ + + * + * 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\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 ContainerInterface +{ + protected $parameterBag; + protected $services; + 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 + * + * @api + */ + public function set($id, $service, $scope = self::SCOPE_CONTAINER) + { + if (self::SCOPE_PROTOTYPE === $scope) { + throw new InvalidArgumentException('You cannot set services of scope "prototype".'); + } + + $id = strtolower($id); + + if (self::SCOPE_CONTAINER !== $scope) { + if (!isset($this->scopedServices[$scope])) { + throw new RuntimeException('You cannot set services of inactive scopes.'); + } + + $this->scopedServices[$scope][$id] = $service; + } + + $this->services[$id] = $service; + } + + /** + * 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->services[$id]) || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_')).'Service'); + } + + /** + * Gets a service. + * + * If a service is both defined through a set() method and + * with a set*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 + * + * @see Reference + * + * @api + */ + public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) + { + $id = strtolower($id); + + if (isset($this->services[$id])) { + return $this->services[$id]; + } + + if (isset($this->loading[$id])) { + throw new ServiceCircularReferenceException($id, array_keys($this->loading)); + } + + if (method_exists($this, $method = 'get'.strtr($id, array('_' => '', '.' => '_')).'Service')) { + $this->loading[$id] = true; + + try { + $service = $this->$method(); + } catch (\Exception $e) { + unset($this->loading[$id]); + throw $e; + } + + unset($this->loading[$id]); + + return $service; + } + + if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { + throw new ServiceNotFoundException($id); + } + } + + /** + * 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->getName(), $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 + * + * @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) { + $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; + + array_unshift($services, $this->services); + $this->services = call_user_func_array('array_merge', $services); + } + } + + /** + * Adds a scope to the container. + * + * @param ScopeInterface $scope + * + * @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 + */ + static public 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 + */ + static public 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/core/vendor/Symfony/Component/DependencyInjection/ContainerAware.php b/core/vendor/Symfony/Component/DependencyInjection/ContainerAware.php new file mode 100644 index 0000000..1ae1db4 --- /dev/null +++ b/core/vendor/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 + */ +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/core/vendor/Symfony/Component/DependencyInjection/ContainerAwareInterface.php b/core/vendor/Symfony/Component/DependencyInjection/ContainerAwareInterface.php new file mode 100644 index 0000000..1879ec0 --- /dev/null +++ b/core/vendor/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 $container A ContainerInterface instance + * + * @api + */ + function setContainer(ContainerInterface $container = null); +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/ContainerBuilder.php b/core/vendor/Symfony/Component/DependencyInjection/ContainerBuilder.php new file mode 100644 index 0000000..1d77ebc --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -0,0 +1,867 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\Compiler; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * ContainerBuilder is a DI container that provides an API to easily describe services. + * + * @author Fabien Potencier + * + * @api + */ +class ContainerBuilder extends Container implements TaggedContainerInterface +{ + private $extensions = array(); + private $extensionsByNs = array(); + private $definitions = array(); + private $aliases = array(); + private $resources = array(); + private $extensionConfigs = array(); + private $compiler; + + /** + * 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 + * + * @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 array 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) + { + $this->resources[] = $resource; + + return $this; + } + + public function setResources(array $resources) + { + $this->resources = $resources; + + return $this; + } + + /** + * Adds the object class hierarchy as resources. + * + * @param object $object An object instance + * + * @api + */ + public function addObjectResource($object) + { + $parent = new \ReflectionObject($object); + do { + $this->addResource(new FileResource($parent->getFileName())); + } while ($parent = $parent->getParentClass()); + } + + /** + * 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 + * + * @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 + * + * @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); + } + + /** + * 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) + { + if ($this->isFrozen()) { + throw new BadMethodCallException('Setting service on a frozen container is not allowed'); + } + + $id = strtolower($id); + + unset($this->definitions[$id], $this->aliases[$id]); + + parent::set($id, $service, $scope); + } + + /** + * 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->aliases[$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 if the service is not defined + * @throws LogicException if the service has a circular reference to itself + * + * @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 (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->aliases[$id])) { + return $this->get($this->aliases[$id]); + } + + try { + $definition = $this->getDefinition($id); + } catch (InvalidArgumentException $e) { + if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { + return null; + } + + throw $e; + } + + $this->loading[$id] = true; + + $service = $this->createService($definition, $id); + + 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()); + + 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]; + } + + /** + * 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(); + } + + foreach ($this->compiler->getPassConfig()->getPasses() as $pass) { + $this->addObjectResource($pass); + } + + $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->aliases), 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 service definitions + * + * @api + */ + public function setAliases(array $aliases) + { + $this->aliases = array(); + $this->addAliases($aliases); + } + + /** + * Sets an alias for an existing service. + * + * @param string $alias The alias to create + * @param mixed $id The service to alias + * + * @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('An alias can not reference itself, got a circular reference on "'.$alias.'".'); + } + + unset($this->definitions[$alias]); + + $this->aliases[$alias] = $id; + } + + /** + * Removes an alias. + * + * @param string $alias The alias to remove + * + * @api + */ + public function removeAlias($alias) + { + unset($this->aliases[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->aliases[strtolower($id)]); + } + + /** + * Gets all defined aliases. + * + * @return array An array of aliases + * + * @api + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Gets an alias. + * + * @param string $id The service identifier + * + * @return string The aliased service identifier + * + * @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->aliases[$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 array $definitions An array of service definitions + * + * @api + */ + public function setDefinitions(array $definitions) + { + $this->definitions = array(); + $this->addDefinitions($definitions); + } + + /** + * Gets all service definitions. + * + * @return array 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 + * + * @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->aliases[$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 + * + * @return object The service described by the service definition + * + * @throws RuntimeException When factory specification is incomplete or scope is inactive + * @throws InvalidArgumentException When configure callable is not callable + */ + private function createService(Definition $definition, $id) + { + if (null !== $definition->getFile()) { + require_once $this->getParameterBag()->resolveValue($definition->getFile()); + } + + $arguments = $this->resolveServices($this->getParameterBag()->resolveValue($definition->getArguments())); + + if (null !== $definition->getFactoryMethod()) { + if (null !== $definition->getFactoryClass()) { + $factory = $this->getParameterBag()->resolveValue($definition->getFactoryClass()); + } elseif (null !== $definition->getFactoryService()) { + $factory = $this->get($this->getParameterBag()->resolveValue($definition->getFactoryService())); + } else { + throw new RuntimeException('Cannot create service from factory method without a factory service or factory class.'); + } + + $service = call_user_func_array(array($factory, $definition->getFactoryMethod()), $arguments); + } else { + $r = new \ReflectionClass($this->getParameterBag()->resolveValue($definition->getClass())); + + $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); + } + + if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { + if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) { + throw new RuntimeException('You tried to create a service of an inactive scope.'); + } + + $this->services[$lowerId = strtolower($id)] = $service; + + if (self::SCOPE_CONTAINER !== $scope) { + $this->scopedServices[$scope][$lowerId] = $service; + } + } + + foreach ($definition->getMethodCalls() as $call) { + $services = self::getServiceConditionals($call[1]); + + $ok = true; + foreach ($services as $s) { + if (!$this->has($s)) { + $ok = false; + break; + } + } + + if ($ok) { + call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->resolveValue($call[1]))); + } + } + + $properties = $this->resolveServices($this->getParameterBag()->resolveValue($definition->getProperties())); + foreach ($properties as $name => $value) { + $service->$name = $value; + } + + if ($callable = $definition->getConfigurator()) { + if (is_array($callable) && is_object($callable[0]) && $callable[0] instanceof Reference) { + $callable[0] = $this->get((string) $callable[0]); + } elseif (is_array($callable)) { + $callable[0] = $this->getParameterBag()->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 (is_object($value) && $value instanceof Reference) { + $value = $this->get((string) $value, $value->getInvalidBehavior()); + } elseif (is_object($value) && $value instanceof Definition) { + $value = $this->createService($value, null); + } + + return $value; + } + + /** + * Returns service ids for a given tag. + * + * @param string $name The tag name + * + * @return array An array of tags + * + * @api + */ + public function findTaggedServiceIds($name) + { + $tags = array(); + foreach ($this->getDefinitions() as $id => $definition) { + if ($definition->getTag($name)) { + $tags[$id] = $definition->getTag($name); + } + } + + return $tags; + } + + /** + * Returns the Service Conditionals. + * + * @param mixed $value An array of conditionals to return. + * + * @return array An array of Service conditionals + */ + static public function getServiceConditionals($value) + { + $services = array(); + + if (is_array($value)) { + foreach ($value as $v) { + $services = array_unique(array_merge($services, self::getServiceConditionals($v))); + } + } elseif (is_object($value) && $value instanceof Reference && $value->getInvalidBehavior() === ContainerInterface::IGNORE_ON_INVALID_REFERENCE) { + $services[] = (string) $value; + } + + return $services; + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/ContainerInterface.php b/core/vendor/Symfony/Component/DependencyInjection/ContainerInterface.php new file mode 100644 index 0000000..93cbf3a --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/ContainerInterface.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\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * 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 + */ + 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 + * + * @see Reference + * + * @api + */ + 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 + */ + 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 + */ + function getParameter($name); + + /** + * Checks if a parameter exists. + * + * @param string $name The parameter name + * + * @return Boolean The presence of parameter in container + * + * @api + */ + function hasParameter($name); + + /** + * Sets a parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + * + * @api + */ + function setParameter($name, $value); + + /** + * Enters the given scope + * + * @param string $name + * + * @api + */ + function enterScope($name); + + /** + * Leaves the current scope, and re-enters the parent scope + * + * @param string $name + * + * @api + */ + function leaveScope($name); + + /** + * Adds a scope to the container + * + * @param ScopeInterface $scope + * + * @api + */ + function addScope(ScopeInterface $scope); + + /** + * Whether this container has the given scope + * + * @param string $name + * + * @return Boolean + * + * @api + */ + 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 + */ + function isScopeActive($name); +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Definition.php b/core/vendor/Symfony/Component/DependencyInjection/Definition.php new file mode 100644 index 0000000..ed5976f --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Definition.php @@ -0,0 +1,639 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\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; + + 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->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; + } + + /** + * Sets 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 + * + * @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 + * + * @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 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 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 mixed $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 mixed The PHP callable to call + * + * @api + */ + public function getConfigurator() + { + return $this->configurator; + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/DefinitionDecorator.php b/core/vendor/Symfony/Component/DependencyInjection/DefinitionDecorator.php new file mode 100644 index 0000000..083fc5b --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/DefinitionDecorator.php @@ -0,0 +1,205 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +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 Definition $parent The Definition instance to decorate. + * + * @api + */ + public function __construct($parent) + { + parent::__construct(); + + $this->parent = $parent; + $this->changes = array(); + } + + /** + * Returns the Definition being decorated. + * + * @return Definition + * + * @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 + * + * @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/core/vendor/Symfony/Component/DependencyInjection/Dumper/Dumper.php b/core/vendor/Symfony/Component/DependencyInjection/Dumper/Dumper.php new file mode 100644 index 0000000..9892401 --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Dumper/DumperInterface.php b/core/vendor/Symfony/Component/DependencyInjection/Dumper/DumperInterface.php new file mode 100644 index 0000000..c05dc6d --- /dev/null +++ b/core/vendor/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 + */ + function dump(array $options = array()); +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php b/core/vendor/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php new file mode 100644 index 0000000..133c621 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * 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 (is_object($argument) && $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 = clone $this->container; + + 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)) { + $nodes[$id] = array('class' => str_replace('\\', '\\\\', get_class($service)), 'attributes' => $this->options['node.instance']); + } + } + + return $nodes; + } + + /** + * 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/core/vendor/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/core/vendor/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php new file mode 100644 index 0000000..a567dfe --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -0,0 +1,1137 @@ + + * + * 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; + +/** + * 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'); + + /** + * {@inheritDoc} + * + * @api + */ + public function __construct(ContainerBuilder $container) + { + parent::__construct($container); + + $this->inlinedDefinitions = new \SplObjectStorage; + } + + /** + * 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() + ; + + return $code; + } + + /** + * 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 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 + */ + 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 || count($sDefinition->getMethodCalls()) > 0 || $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)); + } + + $arguments = array(); + foreach ($sDefinition->getArguments() as $argument) { + $arguments[] = $this->dumpValue($argument); + } + + if (null !== $sDefinition->getFactoryMethod()) { + if (null !== $sDefinition->getFactoryClass()) { + $code .= sprintf(" \$%s = call_user_func(array(%s, '%s')%s);\n", $name, $this->dumpValue($sDefinition->getFactoryClass()), $sDefinition->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); + } elseif (null !== $sDefinition->getFactoryService()) { + $code .= sprintf(" \$%s = %s->%s(%s);\n", $name, $this->getServiceCall($sDefinition->getFactoryService()), $sDefinition->getFactoryMethod(), implode(', ', $arguments)); + } else { + throw new RuntimeException('Factory service or factory class must be defined in service definition for '.$id); + } + } elseif (false !== strpos($class, '$')) { + $code .= sprintf(" \$class = %s;\n \$%s = new \$class(%s);\n", $class, $name, implode(', ', $arguments)); + } else { + $code .= sprintf(" \$%s = new \\%s(%s);\n", $name, substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments)); + } + + if (!$this->hasReference($id, $sDefinition->getMethodCalls()) && !$this->hasReference($id, $sDefinition->getProperties())) { + $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)); + } + + $arguments = array(); + foreach ($definition->getArguments() as $value) { + $arguments[] = $this->dumpValue($value); + } + + $simple = $this->isSimpleInstance($id, $definition); + + $instantiation = ''; + if (ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) { + $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance'); + } elseif (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 .= ' = '; + } + + if (null !== $definition->getFactoryMethod()) { + if (null !== $definition->getFactoryClass()) { + $code = sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass()), $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : ''); + } elseif (null !== $definition->getFactoryService()) { + $code = sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService()), $definition->getFactoryMethod(), implode(', ', $arguments)); + } else { + throw new RuntimeException('Factory method requires a factory service or factory class in service definition for '.$id); + } + } elseif (false !== strpos($class, '$')) { + $code = sprintf(" \$class = %s;\n $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments)); + } else { + $code = sprintf(" $return{$instantiation}new \\%s(%s);\n", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments)); + } + + 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())) { + continue; + } + + if ($iDefinition->getMethodCalls()) { + $code .= $this->addServiceMethodCalls(null, $iDefinition, (string) $this->definitionVariables->offsetGet($iDefinition)); + } + if ($iDefinition->getConfigurator()) { + $code .= $this->addServiceConfigurator(null, $iDefinition, (string) $this->definitionVariables->offsetGet($iDefinition)); + } + } + + 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 (is_object($callable[0]) && $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 = ''; + if ($definition->isSynthetic()) { + $return = sprintf('@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()); + } + + $doc = ''; + if (ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope()) { + $doc .= <<isPublic()) { + $doc .= <<getScope(); + if (ContainerInterface::SCOPE_CONTAINER !== $scope && ContainerInterface::SCOPE_PROTOTYPE !== $scope) { + $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 a service alias. + * + * @param string $alias + * @param string $id + * + * @return string + */ + private function addServiceAlias($alias, $id) + { + $name = Container::camelize($alias); + $type = 'Object'; + + if ($this->container->hasDefinition($id)) { + $class = $this->container->getDefinition($id)->getClass(); + $type = 0 === strpos($class, '%') ? 'Object' : $class; + } + + return <<getServiceCall($id)}; + } + +EOF; + } + + /** + * Adds multiple services + * + * @return string + */ + private function addServices() + { + $publicServices = $privateServices = $aliasServices = ''; + $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); + } + } + + $aliases = $this->container->getAliases(); + ksort($aliases); + foreach ($aliases as $alias => $id) { + $aliasServices .= $this->addServiceAlias($alias, $id); + } + + return $publicServices.$aliasServices.$privateServices; + } + + /** + * 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 .= <<parameters = \$this->getDefaultParameters(); + + \$this->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 .= <<container->getParameterBag()->all()) { + return ''; + } + + $parameters = $this->exportParameters($this->container->getParameterBag()->all()); + + $code = ''; + if ($this->container->isFrozen()) { + $code .= <<parameters)) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', \$name)); + } + + return \$this->parameters[\$name]; + } + + /** + * {@inheritdoc} + */ + public function hasParameter(\$name) + { + return array_key_exists(strtolower(\$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 string &$calls By reference + * @param string &$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 + * + * @return Boolean + */ + private function hasReference($id, array $arguments) + { + foreach ($arguments as $argument) { + if (is_array($argument)) { + if ($this->hasReference($id, $argument)) { + return true; + } + } elseif ($argument instanceof Reference) { + if ($id === (string) $argument) { + return true; + } + } + } + + return false; + } + + /** + * Dumps values. + * + * @param array $value + * @param Boolean $interpolate + * + * @return string + */ + 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 (is_object($value) && $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 (is_object($value) && $value instanceof Variable) { + return '$'.$value; + } elseif (is_object($value) && $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 (is_object($value) && $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/core/vendor/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/core/vendor/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php new file mode 100644 index 0000000..2af81b4 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Dumper/XmlDumper.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\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\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'); + } + + 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((is_object($callable[0]) && $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 string $id + * @param DOMElement $parent + */ + private function addServiceAlias($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); + } + + foreach ($this->container->getAliases() as $alias => $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 (is_object($value) && $value instanceof Reference) { + $element->setAttribute('type', 'service'); + $element->setAttribute('id', (string) $value); + $behaviour = $value->getInvalidBehavior(); + if ($behaviour == ContainerInterface::NULL_ON_INVALID_REFERENCE) { + $element->setAttribute('on-invalid', 'null'); + } elseif ($behaviour == ContainerInterface::IGNORE_ON_INVALID_REFERENCE) { + $element->setAttribute('on-invalid', 'ignore'); + } + } elseif (is_object($value) && $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 + */ + static public function phpToXml($value) + { + switch (true) { + case null === $value: + return 'null'; + case true === $value: + return 'true'; + case false === $value: + return 'false'; + case is_object($value) && $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/core/vendor/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/core/vendor/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php new file mode 100644 index 0000000..5768c1e --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -0,0 +1,278 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\Yaml\Yaml; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * YamlDumper dumps a service container as a YAML string. + * + * @author Fabien Potencier + * + * @api + */ +class YamlDumper extends Dumper +{ + /** + * Dumps the service container as an YAML string. + * + * @param array $options An array of options + * + * @return string A YAML string representing of the service container + * + * @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', Yaml::dump($key), Yaml::dump($value)); + } + $att = $att ? ', '.implode(' ', $att) : ''; + + $tagsCode .= sprintf(" - { name: %s%s }\n", Yaml::dump($name), $att); + } + } + if ($tagsCode) { + $code .= " tags:\n".$tagsCode; + } + + if ($definition->getFile()) { + $code .= sprintf(" file: %s\n", $definition->getFile()); + } + + 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", Yaml::dump($this->dumpValue($definition->getArguments()), 0)); + } + + if ($definition->getProperties()) { + $code .= sprintf(" properties: %s\n", Yaml::dump($this->dumpValue($definition->getProperties()), 0)); + } + + if ($definition->getMethodCalls()) { + $code .= sprintf(" calls:\n %s\n", str_replace("\n", "\n ", Yaml::dump($this->dumpValue($definition->getMethodCalls()), 1))); + } + + if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope()) { + $code .= sprintf(" scope: %s\n", $scope); + } + + if ($callable = $definition->getConfigurator()) { + if (is_array($callable)) { + if (is_object($callable[0]) && $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", Yaml::dump($callable, 0)); + } + + return $code; + } + + /** + * Adds a service alias + * + * @param string $alias + * @param string $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); + } + + foreach ($this->container->getAliases() as $alias => $id) { + $code .= $this->addServiceAlias($alias, $id); + } + + return $code; + } + + /** + * Adds parameters + * + * @return string + */ + private function addParameters() + { + if (!$this->container->getParameterBag()->all()) { + return ''; + } + + if ($this->container->isFrozen()) { + $parameters = $this->prepareParameters($this->container->getParameterBag()->all()); + } else { + $parameters = $this->container->getParameterBag()->all(); + } + + return Yaml::dump(array('parameters' => $parameters), 2); + } + + /** + * Dumps the value to YAML format + * + * @param mixed $value + * + * @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 (is_object($value) && $value instanceof Reference) { + return $this->getServiceCall((string) $value, $value); + } elseif (is_object($value) && $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 + * + * @return array + */ + private function prepareParameters($parameters) + { + $filtered = array(); + foreach ($parameters as $key => $value) { + if (is_array($value)) { + $value = $this->prepareParameters($value); + } elseif ($value instanceof Reference) { + $value = '@'.$value; + } + + $filtered[$key] = $value; + } + + return $this->escape($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/core/vendor/Symfony/Component/DependencyInjection/Exception/BadMethodCallException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/BadMethodCallException.php new file mode 100644 index 0000000..8072ed8 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Exception/BadMethodCallException.php @@ -0,0 +1,19 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base BadMethodCallException for Dependency Injection component. + */ +class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface +{ +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Exception/ExceptionInterface.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/ExceptionInterface.php new file mode 100644 index 0000000..f5e9099 --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Exception/InactiveScopeException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/InactiveScopeException.php new file mode 100644 index 0000000..3dc2759 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Exception/InactiveScopeException.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\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) + { + parent::__construct(sprintf('You cannot create a service ("%s") of an inactive scope ("%s").', $serviceId, $scope)); + + $this->serviceId = $serviceId; + $this->scope = $scope; + } + + public function getServiceId() + { + return $this->serviceId; + } + + public function getScope() + { + return $this->scope; + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Exception/InvalidArgumentException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..119bb7d --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Exception/LogicException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/LogicException.php new file mode 100644 index 0000000..502c4fa --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Exception/LogicException.php @@ -0,0 +1,19 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base LogicException for Dependency Injection component. + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Exception/OutOfBoundsException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/OutOfBoundsException.php new file mode 100644 index 0000000..75946b5 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Exception/OutOfBoundsException.php @@ -0,0 +1,19 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base OutOfBoundsException for Dependency Injection component. + */ +class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface +{ +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Exception/ParameterCircularReferenceException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/ParameterCircularReferenceException.php new file mode 100644 index 0000000..958ade0 --- /dev/null +++ b/core/vendor/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) + { + parent::__construct(sprintf('Circular reference detected for parameter "%s" ("%s" > "%s").', $parameters[0], implode('" > "', $parameters), $parameters[0])); + + $this->parameters = $parameters; + } + + public function getParameters() + { + return $this->parameters; + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php new file mode 100644 index 0000000..200fbec --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\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; + + /** + * 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 + */ + public function __construct($key, $sourceId = null, $sourceKey = null) + { + $this->key = $key; + $this->sourceId = $sourceId; + $this->sourceKey = $sourceKey; + + $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); + } + } + + 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/core/vendor/Symfony/Component/DependencyInjection/Exception/RuntimeException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/RuntimeException.php new file mode 100644 index 0000000..64c822c --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Exception/RuntimeException.php @@ -0,0 +1,21 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base RuntimeException for Dependency Injection component. + * + * @author Johannes M. Schmitt + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Exception/ScopeCrossingInjectionException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/ScopeCrossingInjectionException.php new file mode 100644 index 0000000..fbc7385 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Exception/ScopeCrossingInjectionException.php @@ -0,0 +1,65 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +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) + { + 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 + )); + + $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/core/vendor/Symfony/Component/DependencyInjection/Exception/ScopeWideningInjectionException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/ScopeWideningInjectionException.php new file mode 100644 index 0000000..fc0b50e --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Exception/ScopeWideningInjectionException.php @@ -0,0 +1,64 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +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) + { + 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 + )); + + $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/core/vendor/Symfony/Component/DependencyInjection/Exception/ServiceCircularReferenceException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/ServiceCircularReferenceException.php new file mode 100644 index 0000000..e000001 --- /dev/null +++ b/core/vendor/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) + { + parent::__construct(sprintf('Circular reference detected for service "%s", path: "%s".', $serviceId, implode(' -> ', $path))); + + $this->serviceId = $serviceId; + $this->path = $path; + } + + public function getServiceId() + { + return $this->serviceId; + } + + public function getPath() + { + return $this->path; + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Exception/ServiceNotFoundException.php b/core/vendor/Symfony/Component/DependencyInjection/Exception/ServiceNotFoundException.php new file mode 100644 index 0000000..fc9898f --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Exception/ServiceNotFoundException.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\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) + { + 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); + } + + parent::__construct($msg); + + $this->id = $id; + $this->sourceId = $sourceId; + } + + public function getId() + { + return $this->id; + } + + public function getSourceId() + { + return $this->sourceId; + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php b/core/vendor/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php new file mode 100644 index 0000000..b633f90 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.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\Extension; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * 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 + */ + function getConfiguration(array $config, ContainerBuilder $container); +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php b/core/vendor/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php new file mode 100644 index 0000000..09ebcd8 --- /dev/null +++ b/core/vendor/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 + */ + function load(array $config, ContainerBuilder $container); + + /** + * Returns the namespace to be used for this extension (XML namespace). + * + * @return string The XML namespace + * + * @api + */ + function getNamespace(); + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + * + * @api + */ + 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 + */ + function getAlias(); +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/LICENSE b/core/vendor/Symfony/Component/DependencyInjection/LICENSE new file mode 100644 index 0000000..cdffe7a --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/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/core/vendor/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php b/core/vendor/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php new file mode 100644 index 0000000..fa24565 --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/core/vendor/Symfony/Component/DependencyInjection/Loader/FileLoader.php new file mode 100644 index 0000000..0699ab9 --- /dev/null +++ b/core/vendor/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\FileLocator; + +/** + * 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 FileLocator $locator A FileLocator instance + */ + public function __construct(ContainerBuilder $container, FileLocator $locator) + { + $this->container = $container; + + parent::__construct($locator); + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Loader/IniFileLoader.php b/core/vendor/Symfony/Component/DependencyInjection/Loader/IniFileLoader.php new file mode 100644 index 0000000..4eabd6d --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php b/core/vendor/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php new file mode 100644 index 0000000..f2bf441 --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/core/vendor/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php new file mode 100644 index 0000000..98fb2da --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -0,0 +1,505 @@ + + * + * 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\ContainerInterface; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\SimpleXMLElement; +use Symfony\Component\Config\Resource\FileResource; +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 + $xml = $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', '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 + * + * @throws InvalidArgumentException When loading of XML file returns error + */ + private function parseFile($file) + { + $dom = new \DOMDocument(); + libxml_use_internal_errors(true); + if (!$dom->load($file, defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0)) { + throw new InvalidArgumentException(implode("\n", $this->getXmlErrors())); + } + $dom->validateOnParse = true; + $dom->normalizeDocument(); + libxml_use_internal_errors(false); + $this->validate($dom, $file); + + return simplexml_import_dom($dom, 'Symfony\\Component\\DependencyInjection\\SimpleXMLElement'); + } + + /** + * Processes anonymous services + * + * @param SimpleXMLElement $xml + * @param string $file + * + * @return array An array of anonymous services + */ + private function processAnonymousServices(SimpleXMLElement $xml, $file) + { + $definitions = array(); + $count = 0; + + // anonymous services as arguments + if (false === $nodes = $xml->xpath('//container:argument[@type="service"][not(@id)]')) { + return $xml; + } + 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)]')) { + return $xml; + } + 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); + } + } + + return $xml; + } + + /** + * Validates an XML document. + * + * @param DOMDocument $dom + * @param string $file + */ + private function validate(\DOMDocument $dom, $file) + { + $this->validateSchema($dom, $file); + $this->validateExtensions($dom, $file); + } + + /** + * Validates a documents XML schema. + * + * @param \DOMDocument $dom + * @param string $file + * + * @throws RuntimeException When extension references a non-existent XSD file + * @throws InvalidArgumentException When XML doesn't validate its XSD schema + */ + private function validateSchema(\DOMDocument $dom, $file) + { + $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 + ; + + $current = libxml_use_internal_errors(true); + $valid = $dom->schemaValidateSource($source); + foreach ($tmpfiles as $tmpfile) { + @unlink($tmpfile); + } + if (!$valid) { + throw new InvalidArgumentException(implode("\n", $this->getXmlErrors())); + } + libxml_use_internal_errors($current); + } + + /** + * 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' + )); + } + } + } + + /** + * Returns an array of XML errors. + * + * @return array + */ + private function getXmlErrors() + { + $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(); + + return $errors; + } + + /** + * 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 + */ + static public function convertDomElementToArray(\DomElement $element) + { + $empty = true; + $config = array(); + foreach ($element->attributes as $name => $node) { + $config[$name] = SimpleXMLElement::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 (!$node instanceof \DOMComment) { + if ($node instanceof \DOMElement && '_services' === $node->nodeName) { + $value = new Reference($node->getAttribute('id')); + } else { + $value = static::convertDomElementToArray($node); + } + + $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 = SimpleXMLElement::phpize($nodeValue); + if (count($config)) { + $config['value'] = $value; + } else { + $config = $value; + } + } + + return !$empty ? $config : null; + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/core/vendor/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php new file mode 100644 index 0000000..35dbc75 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.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\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\Yaml; + +/** + * 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 +{ + /** + * 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 + */ + 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['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) { + $definition->addMethodCall($call[0], $this->resolveServices($call[1])); + } + } + + 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']); + + $definition->addTag($name, $tag); + } + } + + $this->container->setDefinition($id, $definition); + } + + /** + * Loads a YAML file. + * + * @param string $file + * + * @return array The file content + */ + private function loadFile($file) + { + return $this->validate(Yaml::parse($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, 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; + } + + $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/core/vendor/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/core/vendor/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd new file mode 100644 index 0000000..7d46e8c --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/vendor/Symfony/Component/DependencyInjection/Parameter.php b/core/vendor/Symfony/Component/DependencyInjection/Parameter.php new file mode 100644 index 0000000..7ba8c3a --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php b/core/vendor/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php new file mode 100644 index 0000000..9664b13 --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php b/core/vendor/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php new file mode 100644 index 0000000..dade577 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php @@ -0,0 +1,261 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\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)) { + throw new ParameterNotFoundException($name); + } + + 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); + } + + /** + * 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; + } + + private 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/core/vendor/Symfony/Component/DependencyInjection/ParameterBag/ParameterBagInterface.php b/core/vendor/Symfony/Component/DependencyInjection/ParameterBag/ParameterBagInterface.php new file mode 100644 index 0000000..b530d5d --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/ParameterBag/ParameterBagInterface.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\DependencyInjection\ParameterBag; + +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; + +/** + * ParameterBagInterface. + * + * @author Fabien Potencier + * + * @api + */ +interface ParameterBagInterface +{ + /** + * Clears all parameters. + * + * @api + */ + function clear(); + + /** + * Adds parameters to the service container parameters. + * + * @param array $parameters An array of parameters + * + * @api + */ + function add(array $parameters); + + /** + * Gets the service container parameters. + * + * @return array An array of parameters + * + * @api + */ + 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 + */ + function get($name); + + /** + * Sets a service container parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + * + * @api + */ + 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 + */ + function has($name); + + /** + * Replaces parameter placeholders (%name%) by their values for all parameters. + */ + 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 + */ + function resolveValue($value); +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/README.md b/core/vendor/Symfony/Component/DependencyInjection/README.md new file mode 100644 index 0000000..6add4cb --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/README.md @@ -0,0 +1,26 @@ +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'); + +Resources +--------- + +Unit tests: + +https://github.com/symfony/symfony/tree/master/tests/Symfony/Tests/Component/DependencyInjection diff --git a/core/vendor/Symfony/Component/DependencyInjection/Reference.php b/core/vendor/Symfony/Component/DependencyInjection/Reference.php new file mode 100644 index 0000000..1517da2 --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/Scope.php b/core/vendor/Symfony/Component/DependencyInjection/Scope.php new file mode 100644 index 0000000..6bc6acb --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/Scope.php @@ -0,0 +1,50 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +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/core/vendor/Symfony/Component/DependencyInjection/ScopeInterface.php b/core/vendor/Symfony/Component/DependencyInjection/ScopeInterface.php new file mode 100644 index 0000000..42ac3e2 --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/ScopeInterface.php @@ -0,0 +1,32 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * Scope Interface. + * + * @author Johannes M. Schmitt + * + * @api + */ +interface ScopeInterface +{ + /** + * @api + */ + function getName(); + + /** + * @api + */ + function getParentName(); +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/SimpleXMLElement.php b/core/vendor/Symfony/Component/DependencyInjection/SimpleXMLElement.php new file mode 100644 index 0000000..457d54f --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/SimpleXMLElement.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\DependencyInjection; + +/** + * 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 + */ + static public 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(str_replace(',', '', $value)); + default: + return $value; + } + } +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/TaggedContainerInterface.php b/core/vendor/Symfony/Component/DependencyInjection/TaggedContainerInterface.php new file mode 100644 index 0000000..81adb20 --- /dev/null +++ b/core/vendor/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 + */ + function findTaggedServiceIds($name); +} diff --git a/core/vendor/Symfony/Component/DependencyInjection/Variable.php b/core/vendor/Symfony/Component/DependencyInjection/Variable.php new file mode 100644 index 0000000..c84b8fd --- /dev/null +++ b/core/vendor/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/core/vendor/Symfony/Component/DependencyInjection/composer.json b/core/vendor/Symfony/Component/DependencyInjection/composer.json new file mode 100644 index 0000000..fd3ef3a --- /dev/null +++ b/core/vendor/Symfony/Component/DependencyInjection/composer.json @@ -0,0 +1,36 @@ +{ + "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.2" + }, + "recommend": { + "symfony/config": "self.version" + }, + "suggest": { + "symfony/yaml": "self.version" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\DependencyInjection": "" } + }, + "target-dir": "Symfony/Component/DependencyInjection", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + } +} \ No newline at end of file