Merge branch '2.8'

* 2.8: (65 commits)
  [VarDumper] Fix tests for HHVM
  Update DateTimeToArrayTransformer.php
  Mock microtime() and time() in transient tests
  Azerbaijani language pluralization rule
  Move HHVM tests out of the allowed failures
  Fix merge
  [2.6] Towards 100% HHVM compat
  [Security/Http] Fix test
  [Stopwatch] Fix test
  Minor fixes
  [Validator] Added missing error codes and turned codes into UUIDs
  Towards 100% HHVM compat
  Warmup twig templates in non-standard paths (closes #12507)
  [Bridge/PhpUnit] Enforce a consistent locale
  Fix param order of assertEquals (expected, actual) in test for Finder\Glob
  Fix choice translation domain for expanded choice widget
  unify default AccessDeniedExeption message
  trigger event with right user (add test)
  [Security] Initialize SwitchUserEvent::targetUser on attemptExitUser
  fixed CS
  ...

Conflicts:
	UPGRADE-2.8.md
	src/Symfony/Bridge/ProxyManager/composer.json
	src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php
	src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php
	src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
	src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
	src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
	src/Symfony/Bundle/FrameworkBundle/Resources/config/old_assets.xml
	src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml
	src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml
	src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.json
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.md
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.txt
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.xml
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.json
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.md
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.txt
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.xml
	src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginFormType.php
	src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml
	src/Symfony/Bundle/SecurityBundle/composer.json
	src/Symfony/Component/Debug/ErrorHandler.php
	src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php
	src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
	src/Symfony/Component/DependencyInjection/Definition.php
	src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
	src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
	src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
	src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
	src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
	src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/legacy-container9.php
	src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/legacy-services9.dot
	src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy-services6.xml
	src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy-services9.xml
	src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy-services6.yml
	src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy-services9.yml
	src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
	src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
	src/Symfony/Component/Form/ResolvedFormType.php
	src/Symfony/Component/Form/Tests/CompoundFormTest.php
	src/Symfony/Component/Process/Tests/AbstractProcessTest.php
	src/Symfony/Component/VarDumper/Tests/CliDumperTest.php
	src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php
This commit is contained in:
Nicolas Grekas 2015-07-01 22:40:29 +02:00
commit d994a748dc
293 changed files with 3231 additions and 1102 deletions

View File

@ -20,7 +20,6 @@ matrix:
- php: hhvm
allow_failures:
- php: nightly
- php: hhvm
fast_finish: true
services: mongodb

View File

@ -50,30 +50,7 @@ class DbalLogger implements SQLLogger
}
if (is_array($params)) {
foreach ($params as $index => $param) {
if (!is_string($params[$index])) {
continue;
}
// non utf-8 strings break json encoding
if (!preg_match('//u', $params[$index])) {
$params[$index] = self::BINARY_DATA_VALUE;
continue;
}
// detect if the too long string must be shorten
if (function_exists('mb_strlen')) {
if (self::MAX_STRING_LENGTH < mb_strlen($params[$index], 'UTF-8')) {
$params[$index] = mb_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]';
continue;
}
} else {
if (self::MAX_STRING_LENGTH < strlen($params[$index])) {
$params[$index] = substr($params[$index], 0, self::MAX_STRING_LENGTH - 6).' [...]';
continue;
}
}
}
$params = $this->normalizeParams($params);
}
if (null !== $this->logger) {
@ -101,4 +78,40 @@ class DbalLogger implements SQLLogger
{
$this->logger->debug($message, $params);
}
private function normalizeParams(array $params)
{
foreach ($params as $index => $param) {
// normalize recursively
if (is_array($param)) {
$params[$index] = $this->normalizeParams($param);
continue;
}
if (!is_string($params[$index])) {
continue;
}
// non utf-8 strings break json encoding
if (!preg_match('//u', $params[$index])) {
$params[$index] = self::BINARY_DATA_VALUE;
continue;
}
// detect if the too long string must be shorten
if (function_exists('mb_strlen')) {
if (self::MAX_STRING_LENGTH < mb_strlen($params[$index], 'UTF-8')) {
$params[$index] = mb_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]';
continue;
}
} else {
if (self::MAX_STRING_LENGTH < strlen($params[$index])) {
$params[$index] = substr($params[$index], 0, self::MAX_STRING_LENGTH - 6).' [...]';
continue;
}
}
}
return $params;
}
}

View File

@ -73,6 +73,37 @@ class DbalLoggerTest extends \PHPUnit_Framework_TestCase
));
}
public function testLogNonUtf8Array()
{
$logger = $this->getMock('Psr\\Log\\LoggerInterface');
$dbalLogger = $this
->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger')
->setConstructorArgs(array($logger, null))
->setMethods(array('log'))
->getMock()
;
$dbalLogger
->expects($this->once())
->method('log')
->with('SQL', array(
'utf8' => 'foo',
array(
'nonutf8' => DbalLogger::BINARY_DATA_VALUE,
)
)
)
;
$dbalLogger->startQuery('SQL', array(
'utf8' => 'foo',
array(
'nonutf8' => "\x7F\xFF",
)
));
}
public function testLogLongString()
{
$logger = $this->getMock('Psr\\Log\\LoggerInterface');

View File

@ -82,7 +82,7 @@ class UniqueEntityValidator extends ConstraintValidator
$criteria = array();
foreach ($fields as $fieldName) {
if (!$class->hasField($fieldName) && !$class->hasAssociation($fieldName)) {
throw new ConstraintDefinitionException(sprintf("The field '%s' is not mapped by Doctrine, so it cannot be validated for uniqueness.", $fieldName));
throw new ConstraintDefinitionException(sprintf('The field "%s" is not mapped by Doctrine, so it cannot be validated for uniqueness.', $fieldName));
}
$criteria[$fieldName] = $class->reflFields[$fieldName]->getValue($entity);

View File

@ -21,10 +21,10 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent;
*/
class WebProcessor extends BaseWebProcessor
{
public function __construct()
public function __construct(array $extraFields = null)
{
// Pass an empty array as the default null value would access $_SERVER
parent::__construct(array());
parent::__construct(array(), $extraFields);
}
public function onKernelRequest(GetResponseEvent $event)

View File

@ -18,6 +18,42 @@ use Symfony\Component\HttpFoundation\Request;
class WebProcessorTest extends \PHPUnit_Framework_TestCase
{
public function testUsesRequestServerData()
{
list($event, $server) = $this->createRequestEvent();
$processor = new WebProcessor();
$processor->onKernelRequest($event);
$record = $processor($this->getRecord());
$this->assertCount(5, $record['extra']);
$this->assertEquals($server['REQUEST_URI'], $record['extra']['url']);
$this->assertEquals($server['REMOTE_ADDR'], $record['extra']['ip']);
$this->assertEquals($server['REQUEST_METHOD'], $record['extra']['http_method']);
$this->assertEquals($server['SERVER_NAME'], $record['extra']['server']);
$this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']);
}
public function testCanBeConstructedWithExtraFields()
{
if (!$this->isExtraFieldsSupported()) {
$this->markTestSkipped('WebProcessor of the installed Monolog version does not support $extraFields parameter');
}
list($event, $server) = $this->createRequestEvent();
$processor = new WebProcessor(array('url', 'referrer'));
$processor->onKernelRequest($event);
$record = $processor($this->getRecord());
$this->assertCount(2, $record['extra']);
$this->assertEquals($server['REQUEST_URI'], $record['extra']['url']);
$this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']);
}
/**
* @return array
*/
private function createRequestEvent()
{
$server = array(
'REQUEST_URI' => 'A',
@ -40,15 +76,7 @@ class WebProcessorTest extends \PHPUnit_Framework_TestCase
->method('getRequest')
->will($this->returnValue($request));
$processor = new WebProcessor();
$processor->onKernelRequest($event);
$record = $processor($this->getRecord());
$this->assertEquals($server['REQUEST_URI'], $record['extra']['url']);
$this->assertEquals($server['REMOTE_ADDR'], $record['extra']['ip']);
$this->assertEquals($server['REQUEST_METHOD'], $record['extra']['http_method']);
$this->assertEquals($server['SERVER_NAME'], $record['extra']['server']);
$this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']);
return array($event, $server);
}
/**
@ -57,7 +85,7 @@ class WebProcessorTest extends \PHPUnit_Framework_TestCase
*
* @return array Record
*/
protected function getRecord($level = Logger::WARNING, $message = 'test')
private function getRecord($level = Logger::WARNING, $message = 'test')
{
return array(
'message' => $message,
@ -69,4 +97,17 @@ class WebProcessorTest extends \PHPUnit_Framework_TestCase
'extra' => array(),
);
}
private function isExtraFieldsSupported()
{
$monologWebProcessorClass = new \ReflectionClass('Monolog\Processor\WebProcessor');
foreach ($monologWebProcessorClass->getConstructor()->getParameters() as $parameter) {
if ('extraFields' === $parameter->getName()) {
return true;
}
}
return false;
}
}

View File

@ -6,6 +6,7 @@ Provides utilities for PHPUnit, especially user deprecation notices management.
It comes with the following features:
* disable the garbage collector;
* enforce a consistent `C` locale;
* auto-register `class_exists` to load Doctrine annotations;
* print a user deprecation notices summary at the end of the test suite.

View File

@ -12,6 +12,9 @@ if (!defined('PHPUNIT_COMPOSER_INSTALL') && !class_exists('PHPUnit_TextUI_Comman
// https://bugs.php.net/bug.php?id=53976
gc_disable();
// Enforce a consistent locale
setlocale(LC_ALL, 'C');
if (class_exists('Doctrine\Common\Annotations\AnnotationRegistry')) {
AnnotationRegistry::registerLoader('class_exists');
}

View File

@ -68,9 +68,9 @@ class ProxyDumper implements DumperInterface
{
$instantiation = 'return';
if (ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) {
if ($definition->isShared() && ContainerInterface::SCOPE_CONTAINER === $definition->getScope(false)) {
$instantiation .= " \$this->services['$id'] =";
} elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
} elseif ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope(false)) {
$instantiation .= " \$this->services['$id'] = \$this->scopedServices['$scope']['$id'] =";
}

View File

@ -98,6 +98,7 @@
{%- for child in form %}
{{- form_widget(child, {
parent_label_class: label_attr.class|default(''),
translation_domain: choice_translation_domain,
}) -}}
{% endfor -%}
</div>
@ -106,6 +107,7 @@
{%- for child in form %}
{{- form_widget(child, {
parent_label_class: label_attr.class|default(''),
translation_domain: choice_translation_domain,
}) -}}
{% endfor -%}
</div>
@ -156,7 +158,7 @@
{%- endblock radio_label %}
{% block checkbox_radio_label %}
{# Do no display the label if widget is not defined in order to prevent double label rendering #}
{# Do not display the label if widget is not defined in order to prevent double label rendering #}
{% if widget is defined %}
{% if required %}
{% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %}
@ -169,7 +171,7 @@
{% endif %}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>
{{- widget|raw -}}
{{- label is not sameas(false) ? label|trans({}, translation_domain) -}}
{{- label is not sameas(false) ? (translation_domain is sameas(false) ? label : label|trans({}, translation_domain)) -}}
</label>
{% endif %}
{% endblock checkbox_radio_label %}

View File

@ -46,7 +46,7 @@
<div {{ block('widget_container_attributes') }}>
{%- for child in form %}
{{- form_widget(child) -}}
{{- form_label(child) -}}
{{- form_label(child, null, {translation_domain: choice_translation_domain}) -}}
{% endfor -%}
</div>
{%- endblock choice_widget_expanded -%}
@ -57,7 +57,7 @@
{%- endif -%}
<select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{%- if placeholder is not none -%}
<option value=""{% if required and value is empty %} selected="selected"{% endif %}>{{ placeholder|trans({}, translation_domain) }}</option>
<option value=""{% if required and value is empty %} selected="selected"{% endif %}>{{ placeholder != '' ? placeholder|trans({}, translation_domain) }}</option>
{%- endif -%}
{%- if preferred_choices|length > 0 -%}
{% set options = preferred_choices %}
@ -225,7 +225,7 @@
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ label|trans({}, translation_domain) }}</label>
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ translation_domain is sameas(false) ? label : label|trans({}, translation_domain) }}</label>
{%- endif -%}
{%- endblock form_label -%}

View File

@ -37,6 +37,7 @@ class ServerStartCommand extends ServerCommand
new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'),
new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', null),
new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'),
new InputOption('force', 'f', InputOption::VALUE_NONE, 'Force web server startup'),
))
->setName('server:start')
->setDescription('Starts PHP built-in web server in the background')
@ -110,8 +111,9 @@ EOF
$address = $address.':'.$input->getOption('port');
}
if ($this->isOtherServerProcessRunning($address)) {
if (!$input->getOption('force') && $this->isOtherServerProcessRunning($address)) {
$output->writeln(sprintf('<error>A process is already listening on http://%s.</error>', $address));
$output->writeln(sprintf('<error>Use the --force option if the server process terminated unexpectedly to start a new web server process.</error>'));
return 1;
}

View File

@ -100,106 +100,103 @@ EOF
$kernel = $this->getContainer()->get('kernel');
// Define Root Path to App folder
$rootPaths = array($kernel->getRootDir());
$transPaths = array($kernel->getRootDir().'/Resources/');
// Override with provided Bundle info
if (null !== $input->getArgument('bundle')) {
try {
$rootPaths = array($kernel->getBundle($input->getArgument('bundle'))->getPath());
$bundle = $kernel->getBundle($input->getArgument('bundle'));
$transPaths = array(
$bundle->getPath().'/Resources/',
sprintf('%s/Resources/%s/', $kernel->getRootDir(), $bundle->getName()),
);
} catch (\InvalidArgumentException $e) {
// such a bundle does not exist, so treat the argument as path
$rootPaths = array($input->getArgument('bundle'));
$transPaths = array($input->getArgument('bundle').'/Resources/');
if (!is_dir($rootPaths[0])) {
throw new \InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.</error>', $rootPaths[0]));
if (!is_dir($transPaths[0])) {
throw new \InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0]));
}
}
} elseif ($input->getOption('all')) {
foreach ($kernel->getBundles() as $bundle) {
$rootPaths[] = $bundle->getPath();
$transPaths[] = $bundle->getPath().'/Resources/';
$transPaths[] = sprintf('%s/Resources/%s/', $kernel->getRootDir(), $bundle->getName());
}
}
foreach ($rootPaths as $rootPath) {
// get bundle directory
$translationsPath = $rootPath.'/Resources/translations';
// Extract used messages
$extractedCatalogue = $this->extractMessages($locale, $transPaths);
$output->writeln(sprintf('Translations in <info>%s</info>', $translationsPath));
// Load defined messages
$currentCatalogue = $this->loadCurrentMessages($locale, $transPaths, $loader);
// Extract used messages
$extractedCatalogue = $this->extractMessages($locale, $rootPath);
// Merge defined and extracted messages to get all message ids
$mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue);
$allMessages = $mergeOperation->getResult()->all($domain);
if (null !== $domain) {
$allMessages = array($domain => $allMessages);
}
// Load defined messages
$currentCatalogue = $this->loadCurrentMessages($locale, $translationsPath, $loader);
// No defined or extracted messages
if (empty($allMessages) || null !== $domain && empty($allMessages[$domain])) {
$outputMessage = sprintf('No defined or extracted messages for locale "%s"', $locale);
// Merge defined and extracted messages to get all message ids
$mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue);
$allMessages = $mergeOperation->getResult()->all($domain);
if (null !== $domain) {
$allMessages = array($domain => $allMessages);
$outputMessage .= sprintf(' and domain "%s"', $domain);
}
// No defined or extracted messages
if (empty($allMessages) || null !== $domain && empty($allMessages[$domain])) {
$outputMessage = sprintf('<info>No defined or extracted messages for locale "%s"</info>', $locale);
$output->warning($outputMessage);
if (null !== $domain) {
$outputMessage .= sprintf(' <info>and domain "%s"</info>', $domain);
}
$output->writeln($outputMessage);
continue;
}
// Load the fallback catalogues
$fallbackCatalogues = $this->loadFallbackCatalogues($locale, $translationsPath, $loader);
// Display header line
$headers = array('State', 'Domain', 'Id', sprintf('Message Preview (%s)', $locale));
foreach ($fallbackCatalogues as $fallbackCatalogue) {
$headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale());
}
// Iterate all message ids and determine their state
$rows = array();
foreach ($allMessages as $domain => $messages) {
foreach (array_keys($messages) as $messageId) {
$value = $currentCatalogue->get($messageId, $domain);
$states = array();
if ($extractedCatalogue->defines($messageId, $domain)) {
if (!$currentCatalogue->defines($messageId, $domain)) {
$states[] = self::MESSAGE_MISSING;
}
} elseif ($currentCatalogue->defines($messageId, $domain)) {
$states[] = self::MESSAGE_UNUSED;
}
if (!in_array(self::MESSAGE_UNUSED, $states) && true === $input->getOption('only-unused')
|| !in_array(self::MESSAGE_MISSING, $states) && true === $input->getOption('only-missing')) {
continue;
}
foreach ($fallbackCatalogues as $fallbackCatalogue) {
if ($fallbackCatalogue->defines($messageId, $domain) && $value === $fallbackCatalogue->get($messageId, $domain)) {
$states[] = self::MESSAGE_EQUALS_FALLBACK;
break;
}
}
$row = array($this->formatStates($states), $domain, $this->formatId($messageId), $this->sanitizeString($value));
foreach ($fallbackCatalogues as $fallbackCatalogue) {
$row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain));
}
$rows[] = $row;
}
}
$output->table($headers, $rows);
return;
}
// Load the fallback catalogues
$fallbackCatalogues = $this->loadFallbackCatalogues($locale, $transPaths, $loader);
// Display header line
$headers = array('State', 'Domain', 'Id', sprintf('Message Preview (%s)', $locale));
foreach ($fallbackCatalogues as $fallbackCatalogue) {
$headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale());
}
$rows = array();
// Iterate all message ids and determine their state
foreach ($allMessages as $domain => $messages) {
foreach (array_keys($messages) as $messageId) {
$value = $currentCatalogue->get($messageId, $domain);
$states = array();
if ($extractedCatalogue->defines($messageId, $domain)) {
if (!$currentCatalogue->defines($messageId, $domain)) {
$states[] = self::MESSAGE_MISSING;
}
} elseif ($currentCatalogue->defines($messageId, $domain)) {
$states[] = self::MESSAGE_UNUSED;
}
if (!in_array(self::MESSAGE_UNUSED, $states) && true === $input->getOption('only-unused')
|| !in_array(self::MESSAGE_MISSING, $states) && true === $input->getOption('only-missing')) {
continue;
}
foreach ($fallbackCatalogues as $fallbackCatalogue) {
if ($fallbackCatalogue->defines($messageId, $domain) && $value === $fallbackCatalogue->get($messageId, $domain)) {
$states[] = self::MESSAGE_EQUALS_FALLBACK;
break;
}
}
$row = array($this->formatStates($states), $domain, $this->formatId($messageId), $this->sanitizeString($value));
foreach ($fallbackCatalogues as $fallbackCatalogue) {
$row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain));
}
$rows[] = $row;
}
}
$output->table($headers, $rows);
}
private function formatState($state)
@ -251,15 +248,18 @@ EOF
/**
* @param string $locale
* @param string $rootPath
* @param array $transPaths
*
* @return MessageCatalogue
*/
private function extractMessages($locale, $rootPath)
private function extractMessages($locale, $transPaths)
{
$extractedCatalogue = new MessageCatalogue($locale);
if (is_dir($rootPath.'/Resources/views')) {
$this->getContainer()->get('translation.extractor')->extract($rootPath.'/Resources/views', $extractedCatalogue);
foreach ($transPaths as $path) {
$path = $path.'views';
if (is_dir($path)) {
$this->getContainer()->get('translation.extractor')->extract($path, $extractedCatalogue);
}
}
return $extractedCatalogue;
@ -267,16 +267,19 @@ EOF
/**
* @param string $locale
* @param string $translationsPath
* @param array $transPaths
* @param TranslationLoader $loader
*
* @return MessageCatalogue
*/
private function loadCurrentMessages($locale, $translationsPath, TranslationLoader $loader)
private function loadCurrentMessages($locale, $transPaths, TranslationLoader $loader)
{
$currentCatalogue = new MessageCatalogue($locale);
if (is_dir($translationsPath)) {
$loader->loadMessages($translationsPath, $currentCatalogue);
foreach ($transPaths as $path) {
$path = $path.'translations';
if (is_dir($path)) {
$loader->loadMessages($path, $currentCatalogue);
}
}
return $currentCatalogue;
@ -284,12 +287,12 @@ EOF
/**
* @param string $locale
* @param string $translationsPath
* @param array $transPaths
* @param TranslationLoader $loader
*
* @return MessageCatalogue[]
*/
private function loadFallbackCatalogues($locale, $translationsPath, TranslationLoader $loader)
private function loadFallbackCatalogues($locale, $transPaths, TranslationLoader $loader)
{
$fallbackCatalogues = array();
$translator = $this->getContainer()->get('translator');
@ -300,7 +303,12 @@ EOF
}
$fallbackCatalogue = new MessageCatalogue($fallbackLocale);
$loader->loadMessages($translationsPath, $fallbackCatalogue);
foreach ($transPaths as $path) {
$path = $path.'translations';
if (is_dir($path)) {
$loader->loadMessages($path, $fallbackCatalogue);
}
}
$fallbackCatalogues[] = $fallbackCatalogue;
}
}

View File

@ -69,6 +69,8 @@ EOF
protected function execute(InputInterface $input, OutputInterface $output)
{
$output = new SymfonyStyle($input, $output);
$kernel = $this->getContainer()->get('kernel');
// check presence of force or dump-message
if ($input->getOption('force') !== true && $input->getOption('dump-messages') !== true) {
$output->error('You must choose one of --force or --dump-messages');
@ -87,30 +89,30 @@ EOF
$kernel = $this->getContainer()->get('kernel');
// Define Root Path to App folder
$rootPath = $kernel->getRootDir();
$transPaths = array($kernel->getRootDir().'/Resources/');
$currentName = 'app folder';
// Override with provided Bundle info
if (null !== $input->getArgument('bundle')) {
try {
$foundBundle = $kernel->getBundle($input->getArgument('bundle'));
$rootPath = $foundBundle->getPath();
$transPaths = array(
$foundBundle->getPath().'/Resources/',
sprintf('%s/Resources/%s/', $kernel->getRootDir(), $foundBundle->getName()),
);
$currentName = $foundBundle->getName();
} catch (\InvalidArgumentException $e) {
// such a bundle does not exist, so treat the argument as path
$rootPath = $input->getArgument('bundle');
$currentName = $rootPath;
$transPaths = array($input->getArgument('bundle').'/Resources/');
$currentName = $transPaths[0];
if (!is_dir($rootPath)) {
throw new \InvalidArgumentException(sprintf('<error>"%s" is neither an enabled bundle nor a directory.</error>', $rootPath));
if (!is_dir($transPaths[0])) {
throw new \InvalidArgumentException(sprintf('<error>"%s" is neither an enabled bundle nor a directory.</error>', $transPaths[0]));
}
}
}
$output->title('Symfony translation update command');
// get bundle directory
$translationsPath = $rootPath.'/Resources/translations';
$output->text(sprintf('Generating "<info>%s</info>" translation files for "<info>%s</info>"', $input->getArgument('locale'), $currentName));
// load any messages from templates
@ -118,13 +120,23 @@ EOF
$output->text('Parsing templates');
$extractor = $this->getContainer()->get('translation.extractor');
$extractor->setPrefix($input->getOption('prefix'));
$extractor->extract($rootPath.'/Resources/views/', $extractedCatalogue);
foreach ($transPaths as $path) {
$path = $path.'views';
if (is_dir($path)) {
$extractor->extract($path, $extractedCatalogue);
}
}
// load any existing messages from the translation files
$currentCatalogue = new MessageCatalogue($input->getArgument('locale'));
$output->text('Loading translation files');
$loader = $this->getContainer()->get('translation.loader');
$loader->loadMessages($translationsPath, $currentCatalogue);
foreach ($transPaths as $path) {
$path = $path.'translations';
if (is_dir($path)) {
$loader->loadMessages($path, $currentCatalogue);
}
}
// process catalogues
$operation = $input->getOption('clean')
@ -150,7 +162,7 @@ EOF
array_map(function ($id) {
return sprintf('<fg=green>%s</>', $id);
}, $newKeys),
array_map(function($id) {
array_map(function ($id) {
return sprintf('<fg=red>%s</>', $id);
}, array_keys($operation->getObsoleteMessages($domain)))
));
@ -168,7 +180,18 @@ EOF
// save the files
if ($input->getOption('force') === true) {
$output->text('Writing files');
$writer->writeTranslations($operation->getResult(), $input->getOption('output-format'), array('path' => $translationsPath, 'default_locale' => $this->getContainer()->getParameter('kernel.default_locale')));
$bundleTransPath = false;
foreach ($transPaths as $path) {
$path = $path.'translations';
if (is_dir($path)) {
$bundleTransPath = $path;
}
}
if ($bundleTransPath) {
$writer->writeTranslations($operation->getResult(), $input->getOption('output-format'), array('path' => $bundleTransPath, 'default_locale' => $this->getContainer()->getParameter('kernel.default_locale')));
}
}
$output->newLine();

View File

@ -210,12 +210,16 @@ class JsonDescriptor extends Descriptor
{
$data = array(
'class' => (string) $definition->getClass(),
'scope' => $definition->getScope(),
'scope' => $definition->getScope(false),
'public' => $definition->isPublic(),
'synthetic' => $definition->isSynthetic(),
'lazy' => $definition->isLazy(),
);
if (method_exists($definition, 'isShared')) {
$data['shared'] = $definition->isShared();
}
$data['abstract'] = $definition->isAbstract();
$data['file'] = $definition->getFile();

View File

@ -176,12 +176,16 @@ class MarkdownDescriptor extends Descriptor
protected function describeContainerDefinition(Definition $definition, array $options = array())
{
$output = '- Class: `'.$definition->getClass().'`'
."\n".'- Scope: `'.$definition->getScope().'`'
."\n".'- Scope: `'.$definition->getScope(false).'`'
."\n".'- Public: '.($definition->isPublic() ? 'yes' : 'no')
."\n".'- Synthetic: '.($definition->isSynthetic() ? 'yes' : 'no')
."\n".'- Lazy: '.($definition->isLazy() ? 'yes' : 'no')
;
if (method_exists($definition, 'isShared')) {
$output .= "\n".'- Shared: '.($definition->isShared() ? 'yes' : 'no');
}
$output .= "\n".'- Abstract: '.($definition->isAbstract() ? 'yes' : 'no');
if ($definition->getFile()) {

View File

@ -170,7 +170,7 @@ class TextDescriptor extends Descriptor
$serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds();
$maxTags = array();
foreach ($serviceIds as $key => $serviceId) {
foreach ($serviceIds as $key => $serviceId) {
$definition = $this->resolveServiceDefinition($builder, $serviceId);
if ($definition instanceof Definition) {
// filter out private services unless shown explicitly
@ -256,10 +256,13 @@ class TextDescriptor extends Descriptor
$description[] = '<comment>Tags</comment> -';
}
$description[] = sprintf('<comment>Scope</comment> %s', $definition->getScope());
$description[] = sprintf('<comment>Scope</comment> %s', $definition->getScope(false));
$description[] = sprintf('<comment>Public</comment> %s', $definition->isPublic() ? 'yes' : 'no');
$description[] = sprintf('<comment>Synthetic</comment> %s', $definition->isSynthetic() ? 'yes' : 'no');
$description[] = sprintf('<comment>Lazy</comment> %s', $definition->isLazy() ? 'yes' : 'no');
if (method_exists($definition, 'isShared')) {
$description[] = sprintf('<comment>Shared</comment> %s', $definition->isShared() ? 'yes' : 'no');
}
$description[] = sprintf('<comment>Abstract</comment> %s', $definition->isAbstract() ? 'yes' : 'no');
if ($definition->getFile()) {

View File

@ -346,10 +346,13 @@ class XmlDescriptor extends Descriptor
}
}
$serviceXML->setAttribute('scope', $definition->getScope());
$serviceXML->setAttribute('scope', $definition->getScope(false));
$serviceXML->setAttribute('public', $definition->isPublic() ? 'true' : 'false');
$serviceXML->setAttribute('synthetic', $definition->isSynthetic() ? 'true' : 'false');
$serviceXML->setAttribute('lazy', $definition->isLazy() ? 'true' : 'false');
if (method_exists($definition, 'isShared')) {
$serviceXML->setAttribute('shared', $definition->isShared() ? 'true' : 'false');
}
$serviceXML->setAttribute('abstract', $definition->isAbstract() ? 'true' : 'false');
$serviceXML->setAttribute('file', $definition->getFile());

View File

@ -119,9 +119,9 @@ abstract class Controller extends ContainerAware
* @param mixed $attributes The attributes
* @param mixed $object The object
*
* @throws \LogicException
*
* @return bool
*
* @throws \LogicException
*/
protected function isGranted($attributes, $object = null)
{
@ -231,7 +231,7 @@ abstract class Controller extends ContainerAware
*
* @return AccessDeniedException
*/
protected function createAccessDeniedException($message = 'Access Denied', \Exception $previous = null)
protected function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null)
{
return new AccessDeniedException($message, $previous);
}

View File

@ -444,7 +444,7 @@ class FrameworkExtension extends Extension
/**
* Loads the request configuration.
*
* @param array $config A session configuration array
* @param array $config A request configuration array
* @param ContainerBuilder $container A ContainerBuilder instance
* @param XmlFileLoader $loader An XmlFileLoader instance
*/

View File

@ -1,31 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="templating.asset.path_package" class="Symfony\Bundle\FrameworkBundle\Templating\Asset\PathPackage" abstract="true">
<argument type="expression">service('request_stack').getMasterRequest()</argument>
<argument /> <!-- version -->
<argument /> <!-- version format -->
</service>
<service id="templating.asset.url_package" class="Symfony\Component\Templating\Asset\UrlPackage" abstract="true">
<argument /> <!-- base urls -->
<argument /> <!-- version -->
<argument /> <!-- version format -->
</service>
<service id="templating.asset.request_aware_package" class="Symfony\Component\Templating\Asset\PackageInterface" abstract="true">
<factory service="templating.asset.package_factory" method="getPackage" />
<argument type="expression">service('request_stack').getMasterRequest()</argument>
<argument /> <!-- HTTP id -->
<argument /> <!-- SSL id -->
</service>
<service id="templating.asset.package_factory" class="Symfony\Bundle\FrameworkBundle\Templating\Asset\PackageFactory">
<argument type="service" id="service_container" />
</service>
</services>
</container>

View File

@ -9,16 +9,16 @@
</parameters>
<services>
<service id="test.client" class="Symfony\Bundle\FrameworkBundle\Client" scope="prototype">
<service id="test.client" class="Symfony\Bundle\FrameworkBundle\Client" shared="false">
<argument type="service" id="kernel" />
<argument>%test.client.parameters%</argument>
<argument type="service" id="test.client.history" />
<argument type="service" id="test.client.cookiejar" />
</service>
<service id="test.client.history" class="Symfony\Component\BrowserKit\History" scope="prototype" />
<service id="test.client.history" class="Symfony\Component\BrowserKit\History" shared="false" />
<service id="test.client.cookiejar" class="Symfony\Component\BrowserKit\CookieJar" scope="prototype" />
<service id="test.client.cookiejar" class="Symfony\Component\BrowserKit\CookieJar" shared="false" />
<service id="test.session.listener" class="Symfony\Bundle\FrameworkBundle\EventListener\TestSessionListener">
<argument type="service" id="service_container" />

View File

@ -32,6 +32,16 @@
<argument>%validator.mapping.cache.prefix%</argument>
</service>
<service id="validator.mapping.cache.doctrine.apc" class="Symfony\Component\Validator\Mapping\Cache\DoctrineCache" public="false">
<argument type="service">
<service class="Doctrine\Common\Cache\ApcCache">
<call method="setNamespace">
<argument>%validator.mapping.cache.prefix%</argument>
</call>
</service>
</argument>
</service>
<service id="validator.validator_factory" class="Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory" public="false">
<argument type="service" id="service_container" />
<argument type="collection" />

View File

@ -7,7 +7,7 @@
)) ?>
<?php if ($multiple): ?> multiple="multiple"<?php endif ?>
>
<?php if (null !== $placeholder): ?><option value=""<?php if ($required and empty($value) && "0" !== $value): ?> selected="selected"<?php endif?>><?php echo $view->escape($view['translator']->trans($placeholder, array(), $translation_domain)) ?></option><?php endif; ?>
<?php if (null !== $placeholder): ?><option value=""<?php if ($required and empty($value) && '0' !== $value): ?> selected="selected"<?php endif?>><?php echo '' != $placeholder ? $view->escape($view['translator']->trans($placeholder, array(), $translation_domain)) : '' ?></option><?php endif; ?>
<?php if (count($preferred_choices) > 0): ?>
<?php echo $view['form']->block($form, 'choice_widget_options', array('choices' => $preferred_choices)) ?>
<?php if (count($choices) > 0 && null !== $separator): ?>

View File

@ -1,6 +1,6 @@
<div <?php echo $view['form']->block($form, 'widget_container_attributes') ?>>
<?php foreach ($form as $child): ?>
<?php echo $view['form']->widget($child) ?>
<?php echo $view['form']->label($child) ?>
<?php echo $view['form']->label($child, null, array('translation_domain' => $choice_translation_domain)) ?>
<?php endforeach ?>
</div>

View File

@ -4,5 +4,5 @@
<?php if (!$label) { $label = isset($label_format)
? strtr($label_format, array('%name%' => $name, '%id%' => $id))
: $view['form']->humanize($name); } ?>
<label <?php foreach ($label_attr as $k => $v) { printf('%s="%s" ', $view->escape($k), $view->escape($v)); } ?>><?php echo $view->escape($view['translator']->trans($label, array(), $translation_domain)) ?></label>
<label <?php foreach ($label_attr as $k => $v) { printf('%s="%s" ', $view->escape($k), $view->escape($v)); } ?>><?php echo $view->escape(false !== $translation_domain ? $view['translator']->trans($label, array(), $translation_domain) : $label) ?></label>
<?php endif ?>

View File

@ -14,7 +14,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Reference;
@ -515,14 +515,14 @@ abstract class FrameworkExtensionTest extends TestCase
return $container;
}
private function assertPathPackage(ContainerBuilder $container, Definition $package, $basePath, $version, $format)
private function assertPathPackage(ContainerBuilder $container, DefinitionDecorator $package, $basePath, $version, $format)
{
$this->assertEquals('assets.path_package', $package->getParent());
$this->assertEquals($basePath, $package->getArgument(0));
$this->assertVersionStrategy($container, $package->getArgument(1), $version, $format);
}
private function assertUrlPackage(ContainerBuilder $container, Definition $package, $baseUrls, $version, $format)
private function assertUrlPackage(ContainerBuilder $container, DefinitionDecorator $package, $baseUrls, $version, $format)
{
$this->assertEquals('assets.url_package', $package->getParent());
$this->assertEquals($baseUrls, $package->getArgument(0));

View File

@ -6,6 +6,7 @@
"public": true,
"synthetic": false,
"lazy": true,
"shared": true,
"abstract": true,
"file": null,
"factory_class": "Full\\Qualified\\FactoryClass",

View File

@ -12,6 +12,7 @@ definition_1
- Public: yes
- Synthetic: no
- Lazy: yes
- Shared: yes
- Abstract: yes
- Factory Class: `Full\Qualified\FactoryClass`
- Factory Method: `get`

View File

@ -2,7 +2,7 @@
<container>
<alias id="alias_1" service="service_1" public="true"/>
<alias id="alias_2" service="service_2" public="false"/>
<definition id="definition_1" class="Full\Qualified\Class1" scope="container" public="true" synthetic="false" lazy="true" abstract="true" file="">
<definition id="definition_1" class="Full\Qualified\Class1" scope="container" public="true" synthetic="false" lazy="true" shared="true" abstract="true" file="">
<factory class="Full\Qualified\FactoryClass" method="get"/>
</definition>
<service id="service_container" class="Symfony\Component\DependencyInjection\ContainerBuilder"/>

View File

@ -6,6 +6,7 @@
"public": true,
"synthetic": false,
"lazy": true,
"shared": true,
"abstract": true,
"file": null,
"factory_class": "Full\\Qualified\\FactoryClass",
@ -20,6 +21,7 @@
"public": false,
"synthetic": true,
"lazy": false,
"shared": true,
"abstract": false,
"file": "\/path\/to\/file",
"factory_service": "factory.service",

View File

@ -12,6 +12,7 @@ definition_1
- Public: yes
- Synthetic: no
- Lazy: yes
- Shared: yes
- Abstract: yes
- Factory Class: `Full\Qualified\FactoryClass`
- Factory Method: `get`
@ -24,6 +25,7 @@ definition_2
- Public: no
- Synthetic: yes
- Lazy: no
- Shared: yes
- Abstract: no
- File: `/path/to/file`
- Factory Service: `factory.service`

View File

@ -2,10 +2,10 @@
<container>
<alias id="alias_1" service="service_1" public="true"/>
<alias id="alias_2" service="service_2" public="false"/>
<definition id="definition_1" class="Full\Qualified\Class1" scope="container" public="true" synthetic="false" lazy="true" abstract="true" file="">
<definition id="definition_1" class="Full\Qualified\Class1" scope="container" public="true" synthetic="false" lazy="true" shared="true" abstract="true" file="">
<factory class="Full\Qualified\FactoryClass" method="get"/>
</definition>
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" lazy="false" abstract="false" file="/path/to/file">
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" lazy="false" shared="true" abstract="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
<tags>
<tag name="tag1">

View File

@ -6,6 +6,7 @@
"public": false,
"synthetic": true,
"lazy": false,
"shared": true,
"abstract": false,
"file": "\/path\/to\/file",
"factory_service": "factory.service",

View File

@ -12,6 +12,7 @@ definition_2
- Public: no
- Synthetic: yes
- Lazy: no
- Shared: yes
- Abstract: no
- File: `/path/to/file`
- Factory Service: `factory.service`

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<container>
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" lazy="false" abstract="false" file="/path/to/file">
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" lazy="false" shared="true" abstract="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
<tags>
<tag name="tag1">

View File

@ -6,6 +6,7 @@
"public": false,
"synthetic": true,
"lazy": false,
"shared": true,
"abstract": false,
"file": "\/path\/to\/file",
"factory_service": "factory.service",
@ -19,6 +20,7 @@
"public": false,
"synthetic": true,
"lazy": false,
"shared": true,
"abstract": false,
"file": "\/path\/to\/file",
"factory_service": "factory.service",

View File

@ -12,6 +12,7 @@ definition_2
- Public: no
- Synthetic: yes
- Lazy: no
- Shared: yes
- Abstract: no
- File: `/path/to/file`
- Factory Service: `factory.service`
@ -29,6 +30,7 @@ definition_2
- Public: no
- Synthetic: yes
- Lazy: no
- Shared: yes
- Abstract: no
- File: `/path/to/file`
- Factory Service: `factory.service`

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<container>
<tag name="tag1">
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" lazy="false" abstract="false" file="/path/to/file">
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" lazy="false" shared="true" abstract="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
</definition>
</tag>
<tag name="tag2">
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" lazy="false" abstract="false" file="/path/to/file">
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" lazy="false" shared="true" abstract="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
</definition>
</tag>

View File

@ -4,6 +4,7 @@
"public": true,
"synthetic": false,
"lazy": true,
"shared": true,
"abstract": true,
"file": null,
"factory_class": "Full\\Qualified\\FactoryClass",

View File

@ -3,6 +3,7 @@
- Public: yes
- Synthetic: no
- Lazy: yes
- Shared: yes
- Abstract: yes
- Factory Class: `Full\Qualified\FactoryClass`
- Factory Method: `get`

View File

@ -5,6 +5,7 @@
<comment>Public</comment> yes
<comment>Synthetic</comment> no
<comment>Lazy</comment> yes
<comment>Shared</comment> yes
<comment>Abstract</comment> yes
<comment>Factory Class</comment> Full\Qualified\FactoryClass
<comment>Factory Method</comment> get

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<definition class="Full\Qualified\Class1" scope="container" public="true" synthetic="false" lazy="true" abstract="true" file="">
<definition class="Full\Qualified\Class1" scope="container" public="true" synthetic="false" lazy="true" shared="true" abstract="true" file="">
<factory class="Full\Qualified\FactoryClass" method="get"/>
</definition>

View File

@ -4,6 +4,7 @@
"public": false,
"synthetic": true,
"lazy": false,
"shared": true,
"abstract": false,
"file": "\/path\/to\/file",
"factory_service": "factory.service",

View File

@ -3,6 +3,7 @@
- Public: no
- Synthetic: yes
- Lazy: no
- Shared: yes
- Abstract: no
- File: `/path/to/file`
- Factory Service: `factory.service`

View File

@ -8,6 +8,7 @@
<comment>Public</comment> no
<comment>Synthetic</comment> yes
<comment>Lazy</comment> no
<comment>Shared</comment> yes
<comment>Abstract</comment> no
<comment>Required File</comment> /path/to/file
<comment>Factory Service</comment> factory.service

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<definition class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" lazy="false" abstract="false" file="/path/to/file">
<definition class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" lazy="false" shared="true" abstract="false" file="/path/to/file">
<factory service="factory.service" method="get"/>
<tags>
<tag name="tag1">

View File

@ -4,7 +4,8 @@ imports:
services:
csrf_form_login.form.type:
class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form\UserLoginFormType
arguments: [ @request_stack ]
arguments:
- @request_stack
tags:
- { name: form.type, alias: user_login }

View File

@ -11,9 +11,11 @@
namespace Symfony\Bundle\TwigBundle\CacheWarmer;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinderInterface;
use Symfony\Component\Templating\TemplateReference;
/**
* Generates the Twig cache for all templates.
@ -27,14 +29,16 @@ class TemplateCacheCacheWarmer implements CacheWarmerInterface
{
protected $container;
protected $finder;
private $paths;
/**
* Constructor.
*
* @param ContainerInterface $container The dependency injection container
* @param TemplateFinderInterface $finder The template paths cache warmer
* @param array $paths Additional twig paths to warm
*/
public function __construct(ContainerInterface $container, TemplateFinderInterface $finder)
public function __construct(ContainerInterface $container, TemplateFinderInterface $finder, array $paths = array())
{
// We don't inject the Twig environment directly as it depends on the
// template locator (via the loader) which might be a cached one.
@ -42,6 +46,7 @@ class TemplateCacheCacheWarmer implements CacheWarmerInterface
// has been warmed up
$this->container = $container;
$this->finder = $finder;
$this->paths = $paths;
}
/**
@ -53,7 +58,13 @@ class TemplateCacheCacheWarmer implements CacheWarmerInterface
{
$twig = $this->container->get('twig');
foreach ($this->finder->findAllTemplates() as $template) {
$templates = $this->finder->findAllTemplates();
foreach ($this->paths as $path => $namespace) {
$templates = array_merge($templates, $this->findTemplatesInFolder($namespace, $path));
}
foreach ($templates as $template) {
if ('twig' !== $template->get('engine')) {
continue;
}
@ -75,4 +86,32 @@ class TemplateCacheCacheWarmer implements CacheWarmerInterface
{
return true;
}
/**
* Find templates in the given directory.
*
* @param string $namespace The namespace for these templates
* @param string $dir The folder where to look for templates
*
* @return array An array of templates of type TemplateReferenceInterface
*/
private function findTemplatesInFolder($namespace, $dir)
{
if (!is_dir($dir)) {
return array();
}
$templates = array();
$finder = new Finder();
foreach ($finder->files()->followLinks()->in($dir) as $file) {
$name = $file->getRelativePathname();
$templates[] = new TemplateReference(
$namespace ? sprintf('@%s/%s', $namespace, $name) : $name,
'twig'
);
}
return $templates;
}
}

View File

@ -77,6 +77,8 @@ class TwigExtension extends Extension
}
}
$container->getDefinition('twig.cache_warmer')->replaceArgument(2, $config['paths']);
// register bundles as Twig namespaces
foreach ($container->getParameter('kernel.bundles') as $bundle => $class) {
$dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/views';

View File

@ -25,6 +25,7 @@
<service id="twig.cache_warmer" class="Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheCacheWarmer" public="false">
<argument type="service" id="service_container" />
<argument type="service" id="templating.finder" />
<argument type="collection" /> <!-- Twig paths -->
</service>
<service id="twig.loader.native_filesystem" class="Twig_Loader_Filesystem" public="false">

View File

@ -18,7 +18,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Scope;
class WebProfilerExtensionTest extends TestCase
{
@ -49,8 +48,6 @@ class WebProfilerExtensionTest extends TestCase
$this->kernel = $this->getMock('Symfony\\Component\\HttpKernel\\KernelInterface');
$this->container = new ContainerBuilder();
$this->container->addScope(new Scope('request'));
$this->container->register('request', 'Symfony\\Component\\HttpFoundation\\Request')->setScope('request');
$this->container->register('router', $this->getMockClass('Symfony\\Component\\Routing\\RouterInterface'));
$this->container->register('twig', 'Twig_Environment');
$this->container->setParameter('kernel.bundles', array());
@ -125,7 +122,6 @@ class WebProfilerExtensionTest extends TestCase
eval('?>'.$dumper->dump(array('class' => $class)));
$container = new $class();
$container->enterScope('request');
$container->set('kernel', $this->kernel);
return $container;

View File

@ -77,7 +77,7 @@ class Cookie
if (null !== $expires) {
$timestampAsDateTime = \DateTime::createFromFormat('U', $expires);
if (false === $timestampAsDateTime) {
throw new \UnexpectedValueException(sprintf('The cookie expiration time "%s" is not valid.'), $expires);
throw new \UnexpectedValueException(sprintf('The cookie expiration time "%s" is not valid.', $expires));
}
$this->expires = $timestampAsDateTime->getTimestamp();

View File

@ -22,7 +22,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase
public function prepare_workspace()
{
$this->workspace = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.time().rand(0, 1000);
$this->workspace = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.time().mt_rand(0, 1000);
mkdir($this->workspace, 0777, true);
$this->workspace = realpath($this->workspace);
}

View File

@ -29,6 +29,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
protected $addIfNotSet = false;
protected $performDeepMerging = true;
protected $ignoreExtraKeys = false;
protected $removeExtraKeys = true;
protected $normalizeKeys = true;
public function setNormalizeKeys($normalizeKeys)
@ -140,10 +141,12 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
* Whether extra keys should just be ignore without an exception.
*
* @param bool $boolean To allow extra keys
* @param bool $remove To remove extra keys
*/
public function setIgnoreExtraKeys($boolean)
public function setIgnoreExtraKeys($boolean, $remove = true)
{
$this->ignoreExtraKeys = (bool) $boolean;
$this->removeExtraKeys = $this->ignoreExtraKeys && $remove;
}
/**
@ -300,6 +303,9 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
if (isset($this->children[$name])) {
$normalized[$name] = $this->children[$name]->normalize($val);
unset($value[$name]);
} elseif (false === $this->removeExtraKeys) {
$normalized[$name] = $val;
unset($value[$name]);
}
}

View File

@ -24,6 +24,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
{
protected $performDeepMerging = true;
protected $ignoreExtraKeys = false;
protected $removeExtraKeys = true;
protected $children = array();
protected $prototype;
protected $atLeastOne = false;
@ -284,11 +285,14 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
* you want to send an entire configuration array through a special
* tree that processes only part of the array.
*
* @param bool $remove Whether to remove the extra keys
*
* @return ArrayNodeDefinition
*/
public function ignoreExtraKeys()
public function ignoreExtraKeys($remove = true)
{
$this->ignoreExtraKeys = true;
$this->removeExtraKeys = $remove;
return $this;
}
@ -393,7 +397,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
$node->addEquivalentValue(false, $this->falseEquivalent);
$node->setPerformDeepMerging($this->performDeepMerging);
$node->setRequired($this->required);
$node->setIgnoreExtraKeys($this->ignoreExtraKeys);
$node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys);
$node->setNormalizeKeys($this->normalizeKeys);
if (null !== $this->normalization) {

View File

@ -50,6 +50,21 @@ class ArrayNodeTest extends \PHPUnit_Framework_TestCase
$this->assertTrue(true, 'No exception was thrown when setIgnoreExtraKeys is true');
}
/**
* Tests that extra keys are not removed when
* ignoreExtraKeys second option is set to false.
*
* Related to testExceptionThrownOnUnrecognizedChild
*/
public function testIgnoreExtraKeysNotRemoved()
{
$node = new ArrayNode('roo');
$node->setIgnoreExtraKeys(true, false);
$data = array('foo' => 'bar');
$this->assertSame($data, $node->normalize($data));
}
/**
* @dataProvider getPreNormalizationTests
*/

View File

@ -69,7 +69,7 @@ class DirectoryResourceTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if the resource has not changed');
$this->assertFalse($resource->isFresh(time() - 86400), '->isFresh() returns false if the resource has been updated');
$resource = new DirectoryResource('/____foo/foobar'.rand(1, 999999));
$resource = new DirectoryResource('/____foo/foobar'.mt_rand(1, 999999));
$this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the resource does not exist');
}

View File

@ -48,7 +48,7 @@ class FileResourceTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed');
$this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated');
$resource = new FileResource('/____foo/foobar'.rand(1, 999999));
$resource = new FileResource('/____foo/foobar'.mt_rand(1, 999999));
$this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource does not exist');
}

View File

@ -40,7 +40,7 @@ class TextDescriptor extends Descriptor
$totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName());
$spacingWidth = $totalWidth - strlen($argument->getName()) + 2;
$this->writeText(sprintf(" <info>%s</info>%s%s%s",
$this->writeText(sprintf(' <info>%s</info>%s%s%s',
$argument->getName(),
str_repeat(' ', $spacingWidth),
// + 17 = 2 spaces + <info> + </info> + 2 spaces
@ -77,7 +77,7 @@ class TextDescriptor extends Descriptor
$spacingWidth = $totalWidth - strlen($synopsis) + 2;
$this->writeText(sprintf(" <info>%s</info>%s%s%s%s",
$this->writeText(sprintf(' <info>%s</info>%s%s%s%s',
$synopsis,
str_repeat(' ', $spacingWidth),
// + 17 = 2 spaces + <info> + </info> + 2 spaces
@ -207,7 +207,7 @@ class TextDescriptor extends Descriptor
foreach ($namespace['commands'] as $name) {
$this->writeText("\n");
$spacingWidth = $width - strlen($name);
$this->writeText(sprintf(" <info>%s</info>%s%s", $name, str_repeat(' ', $spacingWidth), $description->getCommand($name)->getDescription()), $options);
$this->writeText(sprintf(' <info>%s</info>%s%s', $name, str_repeat(' ', $spacingWidth), $description->getCommand($name)->getDescription()), $options);
}
}
@ -262,7 +262,8 @@ class TextDescriptor extends Descriptor
{
$totalWidth = 0;
foreach ($options as $option) {
$nameLength = 4 + strlen($option->getName()) + 2; // - + shortcut + , + whitespace + name + --
// "-" + shortcut + ", --" + name
$nameLength = 1 + max(strlen($option->getShortcut()), 1) + 4 + strlen($option->getName());
if ($option->acceptValue()) {
$valueLength = 1 + strlen($option->getName()); // = + value

View File

@ -205,24 +205,26 @@ class Table
public function render()
{
$this->calculateNumberOfColumns();
$this->rows = $this->buildTableRows($this->rows);
$this->headers = $this->buildTableRows($this->headers);
$rows = $this->buildTableRows($this->rows);
$headers = $this->buildTableRows($this->headers);
$this->calculateColumnsWidth(array_merge($headers, $rows));
$this->renderRowSeparator();
if (!empty($this->headers)) {
foreach ($this->headers as $header) {
if (!empty($headers)) {
foreach ($headers as $header) {
$this->renderRow($header, $this->style->getCellHeaderFormat());
$this->renderRowSeparator();
}
}
foreach ($this->rows as $row) {
foreach ($rows as $row) {
if ($row instanceof TableSeparator) {
$this->renderRowSeparator();
} else {
$this->renderRow($row, $this->style->getCellRowFormat());
}
}
if (!empty($this->rows)) {
if (!empty($rows)) {
$this->renderRowSeparator();
}
@ -246,7 +248,7 @@ class Table
$markup = $this->style->getCrossingChar();
for ($column = 0; $column < $count; $column++) {
$markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->getColumnWidth($column)).$this->style->getCrossingChar();
$markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->columnWidths[$column]).$this->style->getCrossingChar();
}
$this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
@ -292,11 +294,11 @@ class Table
private function renderCell(array $row, $column, $cellFormat)
{
$cell = isset($row[$column]) ? $row[$column] : '';
$width = $this->getColumnWidth($column);
$width = $this->columnWidths[$column];
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
// add the width of the following columns(numbers of colspan).
foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
$width += $this->getColumnSeparatorWidth() + $this->getColumnWidth($nextColumn);
$width += $this->getColumnSeparatorWidth() + $this->columnWidths[$nextColumn];
}
}
@ -509,21 +511,20 @@ class Table
*
* @return int
*/
private function getColumnWidth($column)
private function calculateColumnsWidth($rows)
{
if (isset($this->columnWidths[$column])) {
return $this->columnWidths[$column];
}
for ($column = 0; $column < $this->numberOfColumns; $column++) {
$lengths = array();
foreach ($rows as $row) {
if ($row instanceof TableSeparator) {
continue;
}
foreach (array_merge($this->headers, $this->rows) as $row) {
if ($row instanceof TableSeparator) {
continue;
$lengths[] = $this->getCellWidth($row, $column);
}
$lengths[] = $this->getCellWidth($row, $column);
$this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2;
}
return $this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2;
}
/**

View File

@ -221,7 +221,7 @@ class ArgvInput extends Input
}
if (null !== $value && !$option->acceptValue()) {
throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name, $value));
throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
}
if (null === $value && $option->acceptValue() && count($this->parsed)) {

View File

@ -30,6 +30,9 @@ use Symfony\Component\Console\Formatter\OutputFormatterInterface;
*/
class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
{
/**
* @var StreamOutput
*/
private $stderr;
/**
@ -43,14 +46,12 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
*/
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
{
$outputStream = 'php://stdout';
if (!$this->hasStdoutSupport()) {
$outputStream = 'php://output';
}
$outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output';
$errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output';
parent::__construct(fopen($outputStream, 'w'), $verbosity, $decorated, $formatter);
$this->stderr = new StreamOutput(fopen('php://stderr', 'w'), $verbosity, $decorated, $this->getFormatter());
$this->stderr = new StreamOutput(fopen($errorStream, 'w'), $verbosity, $decorated, $this->getFormatter());
}
/**
@ -100,14 +101,32 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
* Returns true if current environment supports writing console output to
* STDOUT.
*
* IBM iSeries (OS400) exhibits character-encoding issues when writing to
* STDOUT and doesn't properly convert ASCII to EBCDIC, resulting in garbage
* output.
*
* @return bool
*/
protected function hasStdoutSupport()
{
return ('OS400' != php_uname('s'));
return false === $this->isRunningOS400();
}
/**
* Returns true if current environment supports writing console output to
* STDERR.
*
* @return bool
*/
protected function hasStderrSupport()
{
return false === $this->isRunningOS400();
}
/**
* Checks if current executing environment is IBM iSeries (OS400), which
* doesn't properly convert character-encodings between ASCII to EBCDIC.
*
* @return bool
*/
private function isRunningOS400()
{
return 'OS400' === php_uname('s');
}
}

View File

@ -42,6 +42,7 @@ class ObjectsProvider
'input_option_3' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, 'option description'),
'input_option_4' => new InputOption('option_name', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'option description', array()),
'input_option_5' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, "multiline\noption description"),
'input_option_6' => new InputOption('option_name', array('o', 'O'), InputOption::VALUE_REQUIRED, 'option with multiple shortcuts'),
);
}

View File

@ -0,0 +1 @@
{"name":"--option_name","shortcut":"-o|-O","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option with multiple shortcuts","default":null}

View File

@ -0,0 +1,9 @@
**option_name:**
* Name: `--option_name`
* Shortcut: `-o|-O`
* Accept value: yes
* Is value required: yes
* Is multiple: no
* Description: option with multiple shortcuts
* Default: `NULL`

View File

@ -0,0 +1 @@
<info>-o|O, --option_name=OPTION_NAME</info> option with multiple shortcuts

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<option name="--option_name" shortcut="-o" shortcuts="-o|-O" accept_value="1" is_value_required="1" is_multiple="0">
<description>option with multiple shortcuts</description>
<defaults/>
</option>

View File

@ -427,7 +427,7 @@ TABLE
array('ISBN', 'Author'),
array(
array(
new TableCell("9971-5-0210-0", array('rowspan' => 3, 'colspan' => 1)),
new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 1)),
'Dante Alighieri',
),
array(new TableSeparator()),
@ -554,6 +554,33 @@ TABLE;
$this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works');
}
public function testRenderMultiCalls()
{
$table = new Table($output = $this->getOutputStream());
$table->setRows(array(
array(new TableCell('foo', array('colspan' => 2))),
));
$table->render();
$table->render();
$table->render();
$expected =
<<<TABLE
+---+--+
| foo |
+---+--+
+---+--+
| foo |
+---+--+
+---+--+
| foo |
+---+--+
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
}
protected function getOutputStream()
{
return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false);

View File

@ -95,6 +95,7 @@ class ErrorHandler
private static $reservedMemory;
private static $stackedErrors = array();
private static $stackedErrorLevels = array();
private static $toStringException = null;
/**
* Registers the error handler.
@ -351,13 +352,56 @@ class ErrorHandler
}
if ($throw) {
if (($this->scopedErrors & $type) && class_exists(ContextErrorException::class)) {
// Checking for class existence is a work around for https://bugs.php.net/42098
if (null !== self::$toStringException) {
$throw = self::$toStringException;
self::$toStringException = null;
} elseif (($this->scopedErrors & $type) && class_exists(ContextErrorException::class)) {
$throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context);
} else {
$throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
}
if (E_USER_ERROR & $type) {
$backtrace = $backtrace ?: $throw->getTrace();
for ($i = 1; isset($backtrace[$i]); ++$i) {
if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
&& '__toString' === $backtrace[$i]['function']
&& '->' === $backtrace[$i]['type']
&& !isset($backtrace[$i - 1]['class'])
&& ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function'])
) {
// Here, we know trigger_error() has been called from __toString().
// HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead.
// A small convention allows working around the limitation:
// given a caught $e exception in __toString(), quitting the method with
// `return trigger_error($e, E_USER_ERROR);` allows this error handler
// to make $e get through the __toString() barrier.
foreach ($context as $e) {
if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) {
if (1 === $i) {
// On HHVM
$throw = $e;
break;
}
self::$toStringException = $e;
return true;
}
}
if (1 < $i) {
// On PHP (not on HHVM), display the original error message instead of the default one.
$this->handleException($throw);
// Stop the process by giving back the error to the native handler.
return false;
}
}
}
}
throw $throw;
}

View File

@ -266,6 +266,33 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
}
}
public function testHandleUserError()
{
try {
$handler = ErrorHandler::register();
$handler->throwAt(0, true);
$e = null;
$x = new \Exception('Foo');
try {
$f = new Fixtures\ToStringThrower($x);
$f .= ''; // Trigger $f->__toString()
} catch (\Exception $e) {
}
$this->assertSame($x, $e);
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
public function testHandleException()
{
try {

View File

@ -0,0 +1,24 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
class ToStringThrower
{
private $exception;
public function __construct(\Exception $e)
{
$this->exception = $e;
}
public function __toString()
{
try {
throw $this->exception;
} catch (\Exception $e) {
// Using user_error() here is on purpose so we do not forget
// that this alias also should work alongside with trigger_error().
return user_error($e, E_USER_ERROR);
}
}
}

View File

@ -5,6 +5,8 @@ CHANGELOG
-----
* allowed specifying a directory to recursively load all configuration files it contains
* deprecated the concept of scopes
* added `Definition::setShared()` and `Definition::isShared()`
2.7.0
-----

View File

@ -25,6 +25,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
* - non synthetic, non abstract services always have a class set
* - synthetic services are always public
* - synthetic services are always of non-prototype scope
* - shared services are always of non-prototype scope
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
@ -46,10 +47,15 @@ class CheckDefinitionValidityPass implements CompilerPassInterface
}
// synthetic service has non-prototype scope
if ($definition->isSynthetic() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope()) {
if ($definition->isSynthetic() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) {
throw new RuntimeException(sprintf('A synthetic service ("%s") cannot be of scope "prototype".', $id));
}
// shared service has non-prototype scope
if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) {
throw new RuntimeException(sprintf('A shared service ("%s") cannot be of scope "prototype".', $id));
}
// non-synthetic, non-abstract service has class
if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) {
if ($definition->getFactory()) {

View File

@ -46,10 +46,10 @@ class CheckReferenceValidityPass implements CompilerPassInterface
{
$this->container = $container;
$children = $this->container->getScopeChildren();
$children = $this->container->getScopeChildren(false);
$ancestors = array();
$scopes = $this->container->getScopes();
$scopes = $this->container->getScopes(false);
foreach ($scopes as $name => $parent) {
$ancestors[$name] = array($parent);
@ -65,7 +65,7 @@ class CheckReferenceValidityPass implements CompilerPassInterface
$this->currentId = $id;
$this->currentDefinition = $definition;
$this->currentScope = $scope = $definition->getScope();
$this->currentScope = $scope = $definition->getScope(false);
if (ContainerInterface::SCOPE_CONTAINER === $scope) {
$this->currentScopeChildren = array_keys($scopes);
@ -125,7 +125,7 @@ class CheckReferenceValidityPass implements CompilerPassInterface
return;
}
if (!$reference->isStrict()) {
if (!$reference->isStrict(false)) {
return;
}
@ -133,7 +133,7 @@ class CheckReferenceValidityPass implements CompilerPassInterface
return;
}
if ($this->currentScope === $scope = $definition->getScope()) {
if ($this->currentScope === $scope = $definition->getScope(false)) {
return;
}

View File

@ -48,27 +48,7 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
$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())
);
$configurator = $this->inlineArguments($container, array($definition->getConfigurator()));
$definition->setConfigurator($configurator[0]);
$factory = $this->inlineArguments($container, array($definition->getFactory()));
$definition->setFactory($factory[0]);
}
$container->setDefinitions($this->inlineArguments($container, $container->getDefinitions(), true));
}
/**
@ -76,12 +56,16 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
*
* @param ContainerBuilder $container The ContainerBuilder
* @param array $arguments An array of arguments
* @param bool $isRoot If we are processing the root definitions or not
*
* @return array
*/
private function inlineArguments(ContainerBuilder $container, array $arguments)
private function inlineArguments(ContainerBuilder $container, array $arguments, $isRoot = false)
{
foreach ($arguments as $k => $argument) {
if ($isRoot) {
$this->currentId = $k;
}
if (is_array($argument)) {
$arguments[$k] = $this->inlineArguments($container, $argument);
} elseif ($argument instanceof Reference) {
@ -92,7 +76,7 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
if ($this->isInlineableDefinition($container, $id, $definition = $container->getDefinition($id))) {
$this->compiler->addLogMessage($this->formatter->formatInlineService($this, $id, $this->currentId));
if (ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope()) {
if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope(false)) {
$arguments[$k] = $definition;
} else {
$arguments[$k] = clone $definition;
@ -102,6 +86,12 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
$argument->setArguments($this->inlineArguments($container, $argument->getArguments()));
$argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls()));
$argument->setProperties($this->inlineArguments($container, $argument->getProperties()));
$configurator = $this->inlineArguments($container, array($argument->getConfigurator()));
$argument->setConfigurator($configurator[0]);
$factory = $this->inlineArguments($container, array($argument->getFactory()));
$argument->setFactory($factory[0]);
}
}
@ -119,7 +109,7 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
*/
private function isInlineableDefinition(ContainerBuilder $container, $id, Definition $definition)
{
if (ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope()) {
if (!$definition->isShared() || ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) {
return true;
}
@ -148,6 +138,6 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
return false;
}
return $container->getDefinition(reset($ids))->getScope() === $definition->getScope();
return $container->getDefinition(reset($ids))->getScope(false) === $definition->getScope(false);
}
}

View File

@ -21,12 +21,13 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
* merged Definition instance.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/
class ResolveDefinitionTemplatesPass implements CompilerPassInterface
{
private $container;
private $compiler;
private $formatter;
private $currentId;
/**
* Process the ContainerBuilder to replace DefinitionDecorator instances with their real Definition instances.
@ -35,44 +36,80 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface
*/
public function process(ContainerBuilder $container)
{
$this->container = $container;
$this->compiler = $container->getCompiler();
$this->formatter = $this->compiler->getLoggingFormatter();
foreach ($container->getDefinitions() as $id => $definition) {
// 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;
}
$container->setDefinitions($this->resolveArguments($container, $container->getDefinitions(), true));
}
$this->resolveDefinition($id, $definition);
/**
* Resolves definition decorator arguments.
*
* @param ContainerBuilder $container The ContainerBuilder
* @param array $arguments An array of arguments
* @param bool $isRoot If we are processing the root definitions or not
*
* @return array
*/
private function resolveArguments(ContainerBuilder $container, array $arguments, $isRoot = false)
{
foreach ($arguments as $k => $argument) {
if ($isRoot) {
// yes, we are specifically fetching the definition from the
// container to ensure we are not operating on stale data
$arguments[$k] = $argument = $container->getDefinition($k);
$this->currentId = $k;
}
if (is_array($argument)) {
$arguments[$k] = $this->resolveArguments($container, $argument);
} elseif ($argument instanceof Definition) {
if ($argument instanceof DefinitionDecorator) {
$arguments[$k] = $argument = $this->resolveDefinition($container, $argument);
if ($isRoot) {
$container->setDefinition($k, $argument);
}
}
$argument->setArguments($this->resolveArguments($container, $argument->getArguments()));
$argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls()));
$argument->setProperties($this->resolveArguments($container, $argument->getProperties()));
$configurator = $this->resolveArguments($container, array($argument->getConfigurator()));
$argument->setConfigurator($configurator[0]);
$factory = $this->resolveArguments($container, array($argument->getFactory()));
$argument->setFactory($factory[0]);
}
}
return $arguments;
}
/**
* Resolves the definition.
*
* @param string $id The definition identifier
* @param ContainerBuilder $container The ContainerBuilder
* @param DefinitionDecorator $definition
*
* @return Definition
*
* @throws \RuntimeException When the definition is invalid
*/
private function resolveDefinition($id, DefinitionDecorator $definition)
private function resolveDefinition(ContainerBuilder $container, 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));
if (!$container->hasDefinition($parent = $definition->getParent())) {
throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $this->currentId));
}
$parentDef = $this->container->getDefinition($parent);
$parentDef = $container->getDefinition($parent);
if ($parentDef instanceof DefinitionDecorator) {
$parentDef = $this->resolveDefinition($parent, $parentDef);
$id = $this->currentId;
$this->currentId = $parent;
$parentDef = $this->resolveDefinition($container, $parentDef);
$container->setDefinition($parent, $parentDef);
$this->currentId = $id;
}
$this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $id, $parent));
$this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $this->currentId, $parent));
$def = new Definition();
// merge in parent definition
@ -107,6 +144,14 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface
if (isset($changes['lazy'])) {
$def->setLazy($definition->isLazy());
}
if (isset($changes['decorated_service'])) {
$decoratedService = $definition->getDecoratedService();
if (null === $decoratedService) {
$def->setDecoratedService($decoratedService);
} else {
$def->setDecoratedService($decoratedService[0], $decoratedService[1]);
}
}
// merge arguments
foreach ($definition->getArguments() as $k => $v) {
@ -135,12 +180,9 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface
// these attributes are always taken from the child
$def->setAbstract($definition->isAbstract());
$def->setScope($definition->getScope());
$def->setScope($definition->getScope(false), false);
$def->setTags($definition->getTags());
// set new definition on container
$this->container->setDefinition($id, $def);
return $def;
}
}

View File

@ -68,7 +68,7 @@ class ResolveReferencesToAliasesPass implements CompilerPassInterface
$defId = $this->getDefinitionId($id = (string) $argument);
if ($defId !== $id) {
$arguments[$k] = new Reference($defId, $argument->getInvalidBehavior(), $argument->isStrict());
$arguments[$k] = new Reference($defId, $argument->getInvalidBehavior(), $argument->isStrict(false));
}
}
}

View File

@ -180,6 +180,8 @@ class Container implements IntrospectableContainerInterface
* Setting a service to null resets the service: has() returns false and get()
* behaves in the same way as if the service was never created.
*
* Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0.
*
* @param string $id The service identifier
* @param object $service The service instance
* @param string $scope The scope of the service
@ -191,6 +193,10 @@ class Container implements IntrospectableContainerInterface
*/
public function set($id, $service, $scope = self::SCOPE_CONTAINER)
{
if (!in_array($scope, array('container', 'request')) || ('request' === $scope && 'request' !== $id)) {
@trigger_error('The concept of container scopes is deprecated since version 2.8 and will be removed in 3.0. Omit the third parameter.', E_USER_DEPRECATED);
}
if (self::SCOPE_PROTOTYPE === $scope) {
throw new InvalidArgumentException(sprintf('You cannot set service "%s" of scope "prototype".', $id));
}
@ -389,9 +395,15 @@ class Container implements IntrospectableContainerInterface
* @throws InvalidArgumentException When the scope does not exist
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function enterScope($name)
{
if ('request' !== $name) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
if (!isset($this->scopes[$name])) {
throw new InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name));
}
@ -437,9 +449,15 @@ class Container implements IntrospectableContainerInterface
* @throws InvalidArgumentException if the scope is not active
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function leaveScope($name)
{
if ('request' !== $name) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
if (!isset($this->scopedServices[$name])) {
throw new InvalidArgumentException(sprintf('The scope "%s" is not active.', $name));
}
@ -484,12 +502,17 @@ class Container implements IntrospectableContainerInterface
* @throws InvalidArgumentException
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function addScope(ScopeInterface $scope)
{
$name = $scope->getName();
$parentScope = $scope->getParentName();
if ('request' !== $name) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) {
throw new InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name));
}
@ -518,9 +541,15 @@ class Container implements IntrospectableContainerInterface
* @return bool
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function hasScope($name)
{
if ('request' !== $name) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return isset($this->scopes[$name]);
}
@ -534,9 +563,13 @@ class Container implements IntrospectableContainerInterface
* @return bool
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function isScopeActive($name)
{
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
return isset($this->scopedServices[$name]);
}

View File

@ -358,9 +358,15 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
* @return array An array of scopes
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function getScopes()
public function getScopes($triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return $this->scopes;
}
@ -370,15 +376,23 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
* @return array An array of scope children.
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function getScopeChildren()
public function getScopeChildren($triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return $this->scopeChildren;
}
/**
* Sets a service.
*
* Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0.
*
* @param string $id The service identifier
* @param object $service The service instance
* @param string $scope The scope
@ -1126,7 +1140,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
private function shareService(Definition $definition, $service, $id)
{
if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
if ($definition->isShared() && self::SCOPE_PROTOTYPE !== $scope = $definition->getScope(false)) {
if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) {
throw new InactiveScopeException($id, $scope);
}

View File

@ -34,6 +34,8 @@ interface ContainerInterface
/**
* Sets a service.
*
* Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0.
*
* @param string $id The service identifier
* @param object $service The service instance
* @param string $scope The scope of the service
@ -110,6 +112,8 @@ interface ContainerInterface
* @param string $name
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function enterScope($name);
@ -119,6 +123,8 @@ interface ContainerInterface
* @param string $name
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function leaveScope($name);
@ -128,6 +134,8 @@ interface ContainerInterface
* @param ScopeInterface $scope
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function addScope(ScopeInterface $scope);
@ -139,6 +147,8 @@ interface ContainerInterface
* @return bool
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function hasScope($name);
@ -152,6 +162,8 @@ interface ContainerInterface
* @return bool
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function isScopeActive($name);
}

View File

@ -26,6 +26,7 @@ class Definition
private $class;
private $file;
private $factory;
private $shared = true;
private $scope = ContainerInterface::SCOPE_CONTAINER;
private $properties = array();
private $calls = array();
@ -484,6 +485,34 @@ class Definition
return $this->file;
}
/**
* Sets if the service must be shared or not.
*
* @param bool $shared Whether the service must be shared or not
*
* @return Definition The current instance
*
* @api
*/
public function setShared($shared)
{
$this->shared = (bool) $shared;
return $this;
}
/**
* Whether this service is shared.
*
* @return bool
*
* @api
*/
public function isShared()
{
return $this->shared;
}
/**
* Sets the scope of the service.
*
@ -492,9 +521,19 @@ class Definition
* @return Definition The current instance
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function setScope($scope)
public function setScope($scope, $triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
if (ContainerInterface::SCOPE_PROTOTYPE === $scope) {
$this->setShared(false);
}
$this->scope = $scope;
return $this;
@ -506,9 +545,15 @@ class Definition
* @return string
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function getScope()
public function getScope($triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return $this->scope;
}

View File

@ -134,6 +134,16 @@ class DefinitionDecorator extends Definition
return parent::setLazy($boolean);
}
/**
* {@inheritdoc}
*/
public function setDecoratedService($id, $renamedId = null)
{
$this->changes['decorated_service'] = true;
return parent::setDecoratedService($id, $renamedId);
}
/**
* Gets an argument to pass to the service constructor/factory method.
*

View File

@ -173,7 +173,7 @@ class GraphvizDumper extends Dumper
} catch (ParameterNotFoundException $e) {
}
$nodes[$id] = array('class' => str_replace('\\', '\\\\', $className), 'attributes' => array_merge($this->options['node.definition'], array('style' => ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope() ? 'filled' : 'dotted')));
$nodes[$id] = array('class' => str_replace('\\', '\\\\', $className), 'attributes' => array_merge($this->options['node.definition'], array('style' => $definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope(false) ? 'filled' : 'dotted')));
$container->setDefinition($id, new Definition('stdClass'));
}
@ -201,7 +201,7 @@ class GraphvizDumper extends Dumper
$container->setDefinitions($this->container->getDefinitions());
$container->setAliases($this->container->getAliases());
$container->setResources($this->container->getResources());
foreach ($this->container->getScopes() as $scope => $parentScope) {
foreach ($this->container->getScopes(false) as $scope => $parentScope) {
$container->addScope(new Scope($scope, $parentScope));
}
foreach ($this->container->getExtensions() as $extension) {

View File

@ -377,9 +377,9 @@ class PhpDumper extends Dumper
$isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
$instantiation = '';
if (!$isProxyCandidate && ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) {
if (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_CONTAINER === $definition->getScope(false)) {
$instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance');
} elseif (!$isProxyCandidate && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
} elseif (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope(false)) {
$instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance');
} elseif (!$simple) {
$instantiation = '$instance';
@ -569,7 +569,7 @@ class PhpDumper extends Dumper
}
}
$scope = $definition->getScope();
$scope = $definition->getScope(false);
if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
if ($return && 0 === strpos($return[count($return) - 1], '@return')) {
$return[] = '';
@ -580,7 +580,7 @@ class PhpDumper extends Dumper
$return = implode("\n * ", $return);
$doc = '';
if (ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
$doc .= <<<EOF
*
@ -832,10 +832,10 @@ EOF;
EOF;
if (count($scopes = $this->container->getScopes()) > 0) {
if (count($scopes = $this->container->getScopes(false)) > 0) {
$code .= "\n";
$code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n";
$code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n";
$code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren(false)).";\n";
}
$code .= $this->addMethodMap();
@ -879,9 +879,9 @@ EOF;
EOF;
$code .= "\n";
if (count($scopes = $this->container->getScopes()) > 0) {
if (count($scopes = $this->container->getScopes(false)) > 0) {
$code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n";
$code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n";
$code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren(false)).";\n";
} else {
$code .= " \$this->scopes = array();\n";
$code .= " \$this->scopeChildren = array();\n";
@ -1273,11 +1273,6 @@ EOF;
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->getFactory()) {
$factory = $value->getFactory();
@ -1303,7 +1298,16 @@ EOF;
throw new RuntimeException('Cannot dump definition because of invalid factory');
}
return sprintf("new \\%s(%s)", substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
$class = $value->getClass();
if (null === $class) {
throw new RuntimeException('Cannot dump definitions which have no class nor factory.');
}
$class = $this->dumpValue($class);
if (false !== strpos($class, '$')) {
throw new RuntimeException('Cannot dump definitions which have a variable class name.');
}
return sprintf('new \\%s(%s)', substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
} elseif ($value instanceof Variable) {
return '$'.$value;
} elseif ($value instanceof Reference) {

View File

@ -117,7 +117,10 @@ class XmlDumper extends Dumper
if ($definition->getClass()) {
$service->setAttribute('class', $definition->getClass());
}
if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope()) {
if (!$definition->isShared()) {
$service->setAttribute('shared', 'false');
}
if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope(false)) {
$service->setAttribute('scope', $scope);
}
if (!$definition->isPublic()) {
@ -271,7 +274,7 @@ class XmlDumper extends Dumper
} elseif ($behaviour == ContainerInterface::IGNORE_ON_INVALID_REFERENCE) {
$element->setAttribute('on-invalid', 'ignore');
}
if (!$value->isStrict()) {
if (!$value->isStrict(false)) {
$element->setAttribute('strict', 'false');
}
} elseif ($value instanceof Definition) {

View File

@ -112,7 +112,11 @@ class YamlDumper extends Dumper
$code .= sprintf(" calls:\n%s\n", $this->dumper->dump($this->dumpValue($definition->getMethodCalls()), 1, 12));
}
if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope()) {
if (!$definition->isShared()) {
$code .= " shared: false\n";
}
if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope(false)) {
$code .= sprintf(" scope: %s\n", $scope);
}
@ -196,7 +200,7 @@ class YamlDumper extends Dumper
}
/**
* Dumps callable to YAML format
* Dumps callable to YAML format.
*
* @param callable $callable
*

View File

@ -148,11 +148,8 @@ class XmlFileLoader extends FileLoader
$definition = new Definition();
}
foreach (array('class', 'scope', 'public', 'synthetic', 'lazy', 'abstract') as $key) {
foreach (array('class', 'shared', 'public', 'synthetic', 'lazy', 'abstract') as $key) {
if ($value = $service->getAttribute($key)) {
if (in_array($key, array('factory-class', 'factory-method', 'factory-service'))) {
@trigger_error(sprintf('The "%s" attribute in file "%s" is deprecated since version 2.6 and will be removed in 3.0. Use the "factory" element instead.', $key, $file), E_USER_DEPRECATED);
}
$method = 'set'.str_replace('-', '', $key);
$definition->$method(XmlUtils::phpize($value));
}

View File

@ -163,8 +163,15 @@ class YamlFileLoader extends FileLoader
$definition->setClass($service['class']);
}
if (isset($service['shared'])) {
$definition->setShared($service['shared']);
}
if (isset($service['scope'])) {
$definition->setScope($service['scope']);
if ('request' !== $id) {
@trigger_error(sprintf('The "scope" key of service "%s" in file "%s" is deprecated since version 2.8 and will be removed in 3.0.', $id, $file), E_USER_DEPRECATED);
}
$definition->setScope($service['scope'], false);
}
if (isset($service['synthetic'])) {

View File

@ -87,6 +87,7 @@
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="class" type="xsd:string" />
<xsd:attribute name="shared" type="boolean" />
<xsd:attribute name="scope" type="xsd:string" />
<xsd:attribute name="public" type="boolean" />
<xsd:attribute name="synthetic" type="boolean" />

View File

@ -27,6 +27,8 @@ class Reference
/**
* Constructor.
*
* Note: The $strict parameter is deprecated since version 2.8 and will be removed in 3.0.
*
* @param string $id The service identifier
* @param int $invalidBehavior The behavior when the service does not exist
* @param bool $strict Sets how this reference is validated
@ -64,9 +66,15 @@ class Reference
* Returns true when this Reference is strict.
*
* @return bool
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
public function isStrict()
public function isStrict($triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
}
return $this->strict;
}
}

View File

@ -17,6 +17,8 @@ namespace Symfony\Component\DependencyInjection;
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
class Scope implements ScopeInterface
{

View File

@ -17,6 +17,8 @@ namespace Symfony\Component\DependencyInjection;
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
* @api
*
* @deprecated since version 2.8, to be removed in 3.0.
*/
interface ScopeInterface
{

View File

@ -30,6 +30,7 @@ class CheckDefinitionValidityPassTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @group legacy
*/
public function testProcessDetectsSyntheticPrototypeDefinitions()
{
@ -39,6 +40,18 @@ class CheckDefinitionValidityPassTest extends \PHPUnit_Framework_TestCase
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @group legacy
*/
public function testProcessDetectsSharedPrototypeDefinitions()
{
$container = new ContainerBuilder();
$container->register('a')->setShared(true)->setScope(ContainerInterface::SCOPE_PROTOTYPE);
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
*/

View File

@ -19,6 +19,9 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
class CheckReferenceValidityPassTest extends \PHPUnit_Framework_TestCase
{
/**
* @group legacy
*/
public function testProcessIgnoresScopeWideningIfNonStrictReference()
{
$container = new ContainerBuilder();
@ -30,6 +33,7 @@ class CheckReferenceValidityPassTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \RuntimeException
* @group legacy
*/
public function testProcessDetectsScopeWidening()
{
@ -40,6 +44,9 @@ class CheckReferenceValidityPassTest extends \PHPUnit_Framework_TestCase
$this->process($container);
}
/**
* @group legacy
*/
public function testProcessIgnoresCrossScopeHierarchyReferenceIfNotStrict()
{
$container = new ContainerBuilder();
@ -54,6 +61,7 @@ class CheckReferenceValidityPassTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \RuntimeException
* @group legacy
*/
public function testProcessDetectsCrossScopeHierarchyReference()
{

View File

@ -41,6 +41,29 @@ class InlineServiceDefinitionsPassTest extends \PHPUnit_Framework_TestCase
$this->assertSame($container->getDefinition('inlinable.service'), $arguments[0]);
}
public function testProcessDoesNotInlinesWhenAliasedServiceIsShared()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(false)
;
$container->setAlias('moo', 'foo');
$container
->register('service')
->setArguments(array($ref = new Reference('foo')))
;
$this->process($container);
$arguments = $container->getDefinition('service')->getArguments();
$this->assertSame($ref, $arguments[0]);
}
/**
* @group legacy
*/
public function testProcessDoesNotInlineWhenAliasedServiceIsNotOfPrototypeScope()
{
$container = new ContainerBuilder();
@ -61,6 +84,38 @@ class InlineServiceDefinitionsPassTest extends \PHPUnit_Framework_TestCase
$this->assertSame($ref, $arguments[0]);
}
public function testProcessDoesInlineNonSharedService()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setShared(false)
;
$container
->register('bar')
->setPublic(false)
->setShared(false)
;
$container->setAlias('moo', 'bar');
$container
->register('service')
->setArguments(array(new Reference('foo'), $ref = new Reference('moo'), new Reference('bar')))
;
$this->process($container);
$arguments = $container->getDefinition('service')->getArguments();
$this->assertEquals($container->getDefinition('foo'), $arguments[0]);
$this->assertNotSame($container->getDefinition('foo'), $arguments[0]);
$this->assertSame($ref, $arguments[1]);
$this->assertEquals($container->getDefinition('bar'), $arguments[2]);
$this->assertNotSame($container->getDefinition('bar'), $arguments[2]);
}
/**
* @group legacy
*/
public function testProcessDoesInlineServiceOfPrototypeScope()
{
$container = new ContainerBuilder();
@ -188,6 +243,9 @@ class InlineServiceDefinitionsPassTest extends \PHPUnit_Framework_TestCase
$this->assertSame($ref, $args[0]);
}
/**
* @group legacy
*/
public function testProcessInlinesOnlyIfSameScope()
{
$container = new ContainerBuilder();

View File

@ -79,6 +79,9 @@ class ResolveDefinitionTemplatesPassTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($def->isAbstract());
}
/**
* @group legacy
*/
public function testProcessDoesNotCopyScope()
{
$container = new ContainerBuilder();
@ -117,6 +120,25 @@ class ResolveDefinitionTemplatesPassTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(), $def->getTags());
}
public function testProcessDoesNotCopyDecoratedService()
{
$container = new ContainerBuilder();
$container
->register('parent')
->setDecoratedService('foo')
;
$container
->setDefinition('child', new DefinitionDecorator('parent'))
;
$this->process($container);
$def = $container->getDefinition('child');
$this->assertNull($def->getDecoratedService());
}
public function testProcessHandlesMultipleInheritance()
{
$container = new ContainerBuilder();
@ -173,6 +195,55 @@ class ResolveDefinitionTemplatesPassTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($container->getDefinition('child1')->isLazy());
}
public function testDeepDefinitionsResolving()
{
$container = new ContainerBuilder();
$container->register('parent', 'parentClass');
$container->register('sibling', 'siblingClass')
->setConfigurator(new DefinitionDecorator('parent'), 'foo')
->setFactory(array(new DefinitionDecorator('parent'), 'foo'))
->addArgument(new DefinitionDecorator('parent'))
->setProperty('prop', new DefinitionDecorator('parent'))
->addMethodCall('meth', array(new DefinitionDecorator('parent')))
;
$this->process($container);
$configurator = $container->getDefinition('sibling')->getConfigurator();
$this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($configurator));
$this->assertSame('parentClass', $configurator->getClass());
$factory = $container->getDefinition('sibling')->getFactory();
$this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($factory[0]));
$this->assertSame('parentClass', $factory[0]->getClass());
$argument = $container->getDefinition('sibling')->getArgument(0);
$this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($argument));
$this->assertSame('parentClass', $argument->getClass());
$properties = $container->getDefinition('sibling')->getProperties();
$this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($properties['prop']));
$this->assertSame('parentClass', $properties['prop']->getClass());
$methodCalls = $container->getDefinition('sibling')->getMethodCalls();
$this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($methodCalls[0][1][0]));
$this->assertSame('parentClass', $methodCalls[0][1][0]->getClass());
}
public function testSetDecoratedServiceOnServiceHasParent()
{
$container = new ContainerBuilder();
$container->register('parent', 'stdClass');
$container->setDefinition('child1', new DefinitionDecorator('parent'))
->setDecoratedService('foo', 'foo_inner')
;
$this->assertEquals(array('foo', 'foo_inner'), $container->getDefinition('child1')->getDecoratedService());
}
protected function process(ContainerBuilder $container)
{
$pass = new ResolveDefinitionTemplatesPass();

View File

@ -61,6 +61,9 @@ class ResolveInvalidReferencesPassTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(), $def->getProperties());
}
/**
* @group legacy
*/
public function testStrictFlagIsPreserved()
{
$container = new ContainerBuilder();

View File

@ -117,10 +117,21 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('Circular reference detected for service "baz", path: "baz".', $e->getMessage(), '->get() throws a LogicException if the service has a circular reference to itself');
}
$builder->register('foobar', 'stdClass')->setScope('container');
$this->assertTrue($builder->get('bar') === $builder->get('bar'), '->get() always returns the same instance if the service is shared');
}
/**
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::get
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::setShared
*/
public function testNonSharedServicesReturnsDifferentInstances()
{
$builder = new ContainerBuilder();
$builder->register('bar', 'stdClass')->setShared(false);
$this->assertNotSame($builder->get('bar'), $builder->get('bar'));
}
/**
* @covers \Symfony\Component\DependencyInjection\ContainerBuilder::get
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
@ -143,6 +154,7 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
/**
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::get
* @group legacy
*/
public function testGetReturnsNullOnInactiveScope()
{
@ -154,6 +166,7 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
/**
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::get
* @group legacy
*/
public function testGetReturnsNullOnInactiveScopeWhenServiceIsCreatedByAMethod()
{

View File

@ -171,6 +171,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \InvalidArgumentException
* @group legacy
*/
public function testSetDoesNotAllowPrototypeScope()
{
@ -180,6 +181,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \RuntimeException
* @group legacy
*/
public function testSetDoesNotAllowInactiveScope()
{
@ -188,6 +190,9 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$c->set('foo', new \stdClass(), 'foo');
}
/**
* @group legacy
*/
public function testSetAlsoSetsScopedService()
{
$c = new Container();
@ -264,6 +269,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
/**
* @covers Symfony\Component\DependencyInjection\Container::get
* @group legacy
*/
public function testGetReturnsNullOnInactiveScope()
{
@ -302,6 +308,9 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($sc->initialized('alias'), '->initialized() returns true for alias if aliased service is initialized');
}
/**
* @group legacy
*/
public function testEnterLeaveCurrentScope()
{
$container = new ProjectServiceContainer();
@ -327,6 +336,9 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertSame($scopedFoo1, $scopedFoo3);
}
/**
* @group legacy
*/
public function testEnterLeaveScopeWithChildScopes()
{
$container = new Container();
@ -357,6 +369,9 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($container->has('a'));
}
/**
* @group legacy
*/
public function testEnterScopeRecursivelyWithInactiveChildScopes()
{
$container = new Container();
@ -398,6 +413,9 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($container->has('a'));
}
/**
* @group legacy
*/
public function testEnterChildScopeRecursively()
{
$container = new Container();
@ -435,6 +453,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \InvalidArgumentException
* @group legacy
*/
public function testEnterScopeNotAdded()
{
@ -444,6 +463,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \RuntimeException
* @group legacy
*/
public function testEnterScopeDoesNotAllowInactiveParentScope()
{
@ -453,6 +473,9 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$container->enterScope('bar');
}
/**
* @group legacy
*/
public function testLeaveScopeNotActive()
{
$container = new Container();
@ -477,7 +500,8 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \InvalidArgumentException
* @dataProvider getBuiltInScopes
* @dataProvider getLegacyBuiltInScopes
* @group legacy
*/
public function testAddScopeDoesNotAllowBuiltInScopes($scope)
{
@ -487,6 +511,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \InvalidArgumentException
* @group legacy
*/
public function testAddScopeDoesNotAllowExistingScope()
{
@ -497,7 +522,8 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \InvalidArgumentException
* @dataProvider getInvalidParentScopes
* @dataProvider getLegacyInvalidParentScopes
* @group legacy
*/
public function testAddScopeDoesNotAllowInvalidParentScope($scope)
{
@ -505,6 +531,9 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$c->addScope(new Scope('foo', $scope));
}
/**
* @group legacy
*/
public function testAddScope()
{
$c = new Container();
@ -520,6 +549,9 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array('foo' => array('bar', 'baz'), 'bar' => array('baz'), 'baz' => array()), $this->getField($c, 'scopeChildren'));
}
/**
* @group legacy
*/
public function testHasScope()
{
$c = new Container();
@ -568,6 +600,9 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($c->initialized('throws_exception_on_service_configuration'));
}
/**
* @group legacy
*/
public function testIsScopeActive()
{
$c = new Container();
@ -584,7 +619,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($c->isScopeActive('foo'));
}
public function getInvalidParentScopes()
public function getLegacyInvalidParentScopes()
{
return array(
array(ContainerInterface::SCOPE_PROTOTYPE),
@ -592,7 +627,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
);
}
public function getBuiltInScopes()
public function getLegacyBuiltInScopes()
{
return array(
array(ContainerInterface::SCOPE_CONTAINER),

Some files were not shown because too many files have changed in this diff Show More