Merge branch '3.0'

This commit is contained in:
Tobias Schultze 2015-12-15 03:03:25 +01:00
commit e612b92ed0
21 changed files with 173 additions and 61 deletions

View File

@ -19,15 +19,29 @@ namespace Symfony\Bridge\PhpUnit;
class DeprecationErrorHandler class DeprecationErrorHandler
{ {
const MODE_WEAK = 'weak'; const MODE_WEAK = 'weak';
const MODE_WEAK_VERBOSE = 'weak-verbose';
private static $isRegistered = false; private static $isRegistered = false;
public static function register($mode = false) /**
* Registers and configures the deprecation handler.
*
* The following reporting modes are supported:
* - use "weak" to hide the deprecation report but keep a global count;
* - use "/some-regexp/" to stop the test suite whenever a deprecation
* message matches the given regular expression;
* - use a number to define the upper bound of allowed deprecations,
* making the test suite fail whenever more notices are trigerred.
*
* @param int|string|false $mode The reporting mode. Defaults to not allowing any deprecations.
*/
public static function register($mode = 0)
{ {
if (self::$isRegistered) { if (self::$isRegistered) {
return; return;
} }
if (self::MODE_WEAK !== $mode && (!isset($mode[0]) || '/' !== $mode[0])) {
$mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0;
}
$deprecations = array( $deprecations = array(
'unsilencedCount' => 0, 'unsilencedCount' => 0,
'remainingCount' => 0, 'remainingCount' => 0,
@ -147,7 +161,8 @@ class DeprecationErrorHandler
if (!empty($notices)) { if (!empty($notices)) {
echo "\n"; echo "\n";
} }
if (self::MODE_WEAK !== $mode && self::MODE_WEAK_VERBOSE !== $mode && ($deprecations['unsilenced'] || $deprecations['remaining'] || $deprecations['other'])) {
if (DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']) {
exit(1); exit(1);
} }
}); });

View File

@ -12,11 +12,11 @@ It comes with the following features:
* display the stack trace of a deprecation on-demand. * display the stack trace of a deprecation on-demand.
By default any non-legacy-tagged or any non-@-silenced deprecation notices will By default any non-legacy-tagged or any non-@-silenced deprecation notices will
make tests fail. make tests fail. This can be changed by setting the `SYMFONY_DEPRECATIONS_HELPER`
This can be changed by setting the `SYMFONY_DEPRECATIONS_HELPER` environment environment variable to the maximum number of deprecations that are allowed to be
variable to `weak` or `weak-verbose`. This will make the bridge ignore triggered before making the test suite fail. Alternatively, setting it to `weak`
deprecation notices and is useful to projects that must use deprecated interfaces will make the bridge ignore any deprecation notices and is useful to projects
for backward compatibility reasons. that must use deprecated interfaces for backward compatibility reasons.
A summary of deprecation notices is displayed at the end of the test suite: A summary of deprecation notices is displayed at the end of the test suite:

View File

@ -185,7 +185,7 @@ class Client extends BaseClient
$code = <<<EOF $code = <<<EOF
<?php <?php
error_reporting($errorReporting & ~E_USER_DEPRECATED); error_reporting($errorReporting);
if ('$autoloader') { if ('$autoloader') {
require_once '$autoloader'; require_once '$autoloader';

View File

@ -118,7 +118,7 @@ EOF
$helper = new DescriptorHelper(); $helper = new DescriptorHelper();
$options['format'] = $input->getOption('format'); $options['format'] = $input->getOption('format');
$options['raw_text'] = $input->getOption('raw'); $options['raw_text'] = $input->getOption('raw');
$options['output'] = $output; $options['output'] = $io;
$helper->describe($output, $object, $options); $helper->describe($output, $object, $options);
if (!$input->getArgument('name') && $input->isInteractive()) { if (!$input->getArgument('name') && $input->isInteractive()) {

View File

@ -15,6 +15,10 @@
<tag name="data_collector" template="@WebProfiler/Collector/request.html.twig" id="request" priority="335" /> <tag name="data_collector" template="@WebProfiler/Collector/request.html.twig" id="request" priority="335" />
</service> </service>
<service id="data_collector.ajax" class="Symfony\Component\HttpKernel\DataCollector\AjaxDataCollector" public="false">
<tag name="data_collector" template="@WebProfiler/Collector/ajax.html.twig" id="ajax" priority="315" />
</service>
<service id="data_collector.exception" class="Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector" public="false"> <service id="data_collector.exception" class="Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector" public="false">
<tag name="data_collector" template="@WebProfiler/Collector/exception.html.twig" id="exception" priority="305" /> <tag name="data_collector" template="@WebProfiler/Collector/exception.html.twig" id="exception" priority="305" />
</service> </service>

View File

@ -13,7 +13,6 @@
<argument type="service" id="event_dispatcher" /> <argument type="service" id="event_dispatcher" />
<argument type="service" id="controller_resolver" /> <argument type="service" id="controller_resolver" />
<argument type="service" id="request_stack" /> <argument type="service" id="request_stack" />
<argument>false</argument>
</service> </service>
<service id="request_stack" class="Symfony\Component\HttpFoundation\RequestStack" /> <service id="request_stack" class="Symfony\Component\HttpFoundation\RequestStack" />

View File

@ -53,10 +53,12 @@
</div> </div>
{% else %} {% else %}
{# sort collected logs in groups #} {# sort collected logs in groups #}
{% set deprecation_logs, debug_logs, info_and_error_logs = [], [], [] %} {% set deprecation_logs, debug_logs, info_and_error_logs, silenced_logs = [], [], [], [] %}
{% for log in collector.logs %} {% for log in collector.logs %}
{% if log.context.level is defined and log.context.type is defined and log.context.type in [constant('E_DEPRECATED'), constant('E_USER_DEPRECATED')] %} {% if log.context.level is defined and log.context.type is defined and log.context.type in [constant('E_DEPRECATED'), constant('E_USER_DEPRECATED')] %}
{% set deprecation_logs = deprecation_logs|merge([log]) %} {% set deprecation_logs = deprecation_logs|merge([log]) %}
{% elseif log.context.scream is defined and log.context.scream == true %}
{% set silenced_logs = silenced_logs|merge([log]) %}
{% elseif log.priorityName == 'DEBUG' %} {% elseif log.priorityName == 'DEBUG' %}
{% set debug_logs = debug_logs|merge([log]) %} {% set debug_logs = debug_logs|merge([log]) %}
{% else %} {% else %}
@ -108,6 +110,21 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="tab">
<h3 class="tab-title">Silenced Errors <span class="badge">{{ collector.countscreams|default(0) }}</span></h3>
<div class="tab-content">
{% if silenced_logs is empty %}
<div class="empty">
<p>There are no log messages of this level.</p>
</div>
{% else %}
{{ helper.render_table(silenced_logs) }}
{% endif %}
</div>
</div>
</div> </div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -33,7 +33,7 @@ class SymfonyQuestionHelper extends QuestionHelper
{ {
$validator = $question->getValidator(); $validator = $question->getValidator();
$question->setValidator(function ($value) use ($validator) { $question->setValidator(function ($value) use ($validator) {
if (null !== $validator && is_callable($validator)) { if (null !== $validator) {
$value = $validator($value); $value = $validator($value);
} }

View File

@ -384,7 +384,7 @@ class Table
$columns[] = $this->getNumberOfColumns($row); $columns[] = $this->getNumberOfColumns($row);
} }
return $this->numberOfColumns = max($columns); $this->numberOfColumns = max($columns);
} }
private function buildTableRows($rows) private function buildTableRows($rows)
@ -539,7 +539,7 @@ class Table
* *
* @param array $row * @param array $row
* *
* @return array() * @return array
*/ */
private function getRowColumns($row) private function getRowColumns($row)
{ {
@ -555,11 +555,9 @@ class Table
} }
/** /**
* Gets column width. * Calculates columns widths.
* *
* @param int $column * @param array $rows
*
* @return int
*/ */
private function calculateColumnsWidth($rows) private function calculateColumnsWidth($rows)
{ {
@ -580,8 +578,6 @@ class Table
/** /**
* Gets column width. * Gets column width.
* *
* @param int $column
*
* @return int * @return int
*/ */
private function getColumnSeparatorWidth() private function getColumnSeparatorWidth()

View File

@ -105,7 +105,7 @@ class Client extends BaseClient
$code = <<<EOF $code = <<<EOF
<?php <?php
error_reporting($errorReporting & ~E_USER_DEPRECATED); error_reporting($errorReporting);
require_once '$requirePath'; require_once '$requirePath';

View File

@ -22,6 +22,24 @@ use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
*/ */
class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface
{ {
private $errorNames = array(
E_DEPRECATED => 'E_DEPRECATED',
E_USER_DEPRECATED => 'E_USER_DEPRECATED',
E_NOTICE => 'E_NOTICE',
E_USER_NOTICE => 'E_USER_NOTICE',
E_STRICT => 'E_STRICT',
E_WARNING => 'E_WARNING',
E_USER_WARNING => 'E_USER_WARNING',
E_COMPILE_WARNING => 'E_COMPILE_WARNING',
E_CORE_WARNING => 'E_CORE_WARNING',
E_USER_ERROR => 'E_USER_ERROR',
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
E_COMPILE_ERROR => 'E_COMPILE_ERROR',
E_PARSE => 'E_PARSE',
E_ERROR => 'E_ERROR',
E_CORE_ERROR => 'E_CORE_ERROR',
);
private $logger; private $logger;
public function __construct($logger = null) public function __construct($logger = null)
@ -106,6 +124,9 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
if (isset($context['type'], $context['file'], $context['line'], $context['level'])) { if (isset($context['type'], $context['file'], $context['line'], $context['level'])) {
$errorId = md5("{$context['type']}/{$context['line']}/{$context['file']}\x00{$log['message']}", true); $errorId = md5("{$context['type']}/{$context['line']}/{$context['file']}\x00{$log['message']}", true);
$silenced = !($context['type'] & $context['level']); $silenced = !($context['type'] & $context['level']);
if (isset($this->errorNames[$context['type']])) {
$context = array_merge(array('name' => $this->errorNames[$context['type']]), $context);
}
if (isset($errorContextById[$errorId])) { if (isset($errorContextById[$errorId])) {
if (isset($errorContextById[$errorId]['errorCount'])) { if (isset($errorContextById[$errorId]['errorCount'])) {

View File

@ -153,16 +153,10 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
/** /**
* Gets the Surrogate instance. * Gets the Surrogate instance.
* *
* @throws \LogicException
*
* @return SurrogateInterface A Surrogate instance * @return SurrogateInterface A Surrogate instance
*/ */
public function getSurrogate() public function getSurrogate()
{ {
if (!$this->surrogate instanceof Esi) {
throw new \LogicException('This instance of HttpCache was not set up to use ESI as surrogate handler. You must overwrite and use createSurrogate');
}
return $this->surrogate; return $this->surrogate;
} }

View File

@ -75,8 +75,8 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
), ),
array( array(
1, 1,
array(array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'level' => 0, 'file' => __FILE__, 'line' => 123), 'priority' => 100, 'priorityName' => 'DEBUG')), array(array('message' => 'foo3', 'context' => array('name' => 'E_USER_WARNING', 'type' => E_USER_WARNING, 'level' => 0, 'file' => __FILE__, 'line' => 123), 'priority' => 100, 'priorityName' => 'DEBUG')),
array(array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'level' => 0, 'file' => __FILE__, 'line' => 123, 'scream' => true), 'priority' => 100, 'priorityName' => 'DEBUG')), array(array('message' => 'foo3', 'context' => array('name' => 'E_USER_WARNING', 'type' => E_USER_WARNING, 'level' => 0, 'file' => __FILE__, 'line' => 123, 'scream' => true), 'priority' => 100, 'priorityName' => 'DEBUG')),
0, 0,
1, 1,
), ),
@ -86,7 +86,7 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'level' => 0, 'file' => __FILE__, 'line' => 123), 'priority' => 100, 'priorityName' => 'DEBUG'), array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'level' => 0, 'file' => __FILE__, 'line' => 123), 'priority' => 100, 'priorityName' => 'DEBUG'),
array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'level' => -1, 'file' => __FILE__, 'line' => 123), 'priority' => 100, 'priorityName' => 'DEBUG'), array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'level' => -1, 'file' => __FILE__, 'line' => 123), 'priority' => 100, 'priorityName' => 'DEBUG'),
), ),
array(array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'level' => -1, 'file' => __FILE__, 'line' => 123, 'errorCount' => 2), 'priority' => 100, 'priorityName' => 'DEBUG')), array(array('message' => 'foo3', 'context' => array('name' => 'E_USER_WARNING', 'type' => E_USER_WARNING, 'level' => -1, 'file' => __FILE__, 'line' => 123, 'errorCount' => 2), 'priority' => 100, 'priorityName' => 'DEBUG')),
0, 0,
1, 1,
), ),

View File

@ -24,19 +24,74 @@ use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
*/ */
class PropertyAccessor implements PropertyAccessorInterface class PropertyAccessor implements PropertyAccessorInterface
{ {
/**
* @internal
*/
const VALUE = 0; const VALUE = 0;
/**
* @internal
*/
const IS_REF = 1; const IS_REF = 1;
/**
* @internal
*/
const IS_REF_CHAINED = 2; const IS_REF_CHAINED = 2;
/**
* @internal
*/
const ACCESS_HAS_PROPERTY = 0; const ACCESS_HAS_PROPERTY = 0;
/**
* @internal
*/
const ACCESS_TYPE = 1; const ACCESS_TYPE = 1;
/**
* @internal
*/
const ACCESS_NAME = 2; const ACCESS_NAME = 2;
/**
* @internal
*/
const ACCESS_REF = 3; const ACCESS_REF = 3;
/**
* @internal
*/
const ACCESS_ADDER = 4; const ACCESS_ADDER = 4;
/**
* @internal
*/
const ACCESS_REMOVER = 5; const ACCESS_REMOVER = 5;
/**
* @internal
*/
const ACCESS_TYPE_METHOD = 0; const ACCESS_TYPE_METHOD = 0;
/**
* @internal
*/
const ACCESS_TYPE_PROPERTY = 1; const ACCESS_TYPE_PROPERTY = 1;
/**
* @internal
*/
const ACCESS_TYPE_MAGIC = 2; const ACCESS_TYPE_MAGIC = 2;
/**
* @internal
*/
const ACCESS_TYPE_ADDER_AND_REMOVER = 3; const ACCESS_TYPE_ADDER_AND_REMOVER = 3;
/**
* @internal
*/
const ACCESS_TYPE_NOT_FOUND = 4; const ACCESS_TYPE_NOT_FOUND = 4;
/** /**
@ -62,6 +117,9 @@ class PropertyAccessor implements PropertyAccessorInterface
/** /**
* Should not be used by application code. Use * Should not be used by application code. Use
* {@link PropertyAccess::createPropertyAccessor()} instead. * {@link PropertyAccess::createPropertyAccessor()} instead.
*
* @param bool $magicCall
* @param bool $throwExceptionOnInvalidIndex
*/ */
public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false) public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false)
{ {
@ -365,7 +423,7 @@ class PropertyAccessor implements PropertyAccessorInterface
} }
} elseif (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property)) { } elseif (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property)) {
// Needed to support \stdClass instances. We need to explicitly // Needed to support \stdClass instances. We need to explicitly
// exclude $classHasProperty, otherwise if in the previous clause // exclude $access[self::ACCESS_HAS_PROPERTY], otherwise if
// a *protected* property was found on the class, property_exists() // a *protected* property was found on the class, property_exists()
// returns true, consequently the following line will result in a // returns true, consequently the following line will result in a
// fatal error. // fatal error.
@ -411,7 +469,6 @@ class PropertyAccessor implements PropertyAccessorInterface
$getsetter = lcfirst($camelProp); // jQuery style, e.g. read: last(), write: last($item) $getsetter = lcfirst($camelProp); // jQuery style, e.g. read: last(), write: last($item)
$isser = 'is'.$camelProp; $isser = 'is'.$camelProp;
$hasser = 'has'.$camelProp; $hasser = 'has'.$camelProp;
$classHasProperty = $reflClass->hasProperty($property);
if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) { if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) {
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
@ -429,13 +486,10 @@ class PropertyAccessor implements PropertyAccessorInterface
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
$access[self::ACCESS_NAME] = $property; $access[self::ACCESS_NAME] = $property;
$access[self::ACCESS_REF] = false; $access[self::ACCESS_REF] = false;
} elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) { } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) {
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
$access[self::ACCESS_NAME] = $property; $access[self::ACCESS_NAME] = $property;
$access[self::ACCESS_REF] = true; $access[self::ACCESS_REF] = true;
$result[self::VALUE] = &$object->$property;
$result[self::IS_REF] = true;
} elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) { } elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) {
// we call the getter and hope the __call do the job // we call the getter and hope the __call do the job
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC; $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC;
@ -506,7 +560,7 @@ class PropertyAccessor implements PropertyAccessorInterface
$this->writeCollection($object, $property, $value, $access[self::ACCESS_ADDER], $access[self::ACCESS_REMOVER]); $this->writeCollection($object, $property, $value, $access[self::ACCESS_ADDER], $access[self::ACCESS_REMOVER]);
} elseif (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property)) { } elseif (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property)) {
// Needed to support \stdClass instances. We need to explicitly // Needed to support \stdClass instances. We need to explicitly
// exclude $classHasProperty, otherwise if in the previous clause // exclude $access[self::ACCESS_HAS_PROPERTY], otherwise if
// a *protected* property was found on the class, property_exists() // a *protected* property was found on the class, property_exists()
// returns true, consequently the following line will result in a // returns true, consequently the following line will result in a
// fatal error. // fatal error.
@ -579,7 +633,6 @@ class PropertyAccessor implements PropertyAccessorInterface
private function getWriteAccessInfo($object, $property, $value) private function getWriteAccessInfo($object, $property, $value)
{ {
$key = get_class($object).'::'.$property; $key = get_class($object).'::'.$property;
$guessedAdders = '';
if (isset($this->writePropertyCache[$key])) { if (isset($this->writePropertyCache[$key])) {
$access = $this->writePropertyCache[$key]; $access = $this->writePropertyCache[$key];
@ -594,13 +647,7 @@ class PropertyAccessor implements PropertyAccessorInterface
if (is_array($value) || $value instanceof \Traversable) { if (is_array($value) || $value instanceof \Traversable) {
$methods = $this->findAdderAndRemover($reflClass, $singulars); $methods = $this->findAdderAndRemover($reflClass, $singulars);
if (null === $methods) { if (null !== $methods) {
// It is sufficient to include only the adders in the error
// message. If the user implements the adder but not the remover,
// an exception will be thrown in findAdderAndRemover() that
// the remover has to be implemented as well.
$guessedAdders = '"add'.implode('()", "add', $singulars).'()", ';
} else {
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER; $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER;
$access[self::ACCESS_ADDER] = $methods[0]; $access[self::ACCESS_ADDER] = $methods[0];
$access[self::ACCESS_REMOVER] = $methods[1]; $access[self::ACCESS_REMOVER] = $methods[1];
@ -608,11 +655,9 @@ class PropertyAccessor implements PropertyAccessorInterface
} }
if (!isset($access[self::ACCESS_TYPE])) { if (!isset($access[self::ACCESS_TYPE])) {
$setter = 'set'.$this->camelize($property); $setter = 'set'.$camelized;
$getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item) $getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item)
$classHasProperty = $reflClass->hasProperty($property);
if ($this->isMethodAccessible($reflClass, $setter, 1)) { if ($this->isMethodAccessible($reflClass, $setter, 1)) {
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
$access[self::ACCESS_NAME] = $setter; $access[self::ACCESS_NAME] = $setter;
@ -622,7 +667,7 @@ class PropertyAccessor implements PropertyAccessorInterface
} elseif ($this->isMethodAccessible($reflClass, '__set', 2)) { } elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
$access[self::ACCESS_NAME] = $property; $access[self::ACCESS_NAME] = $property;
} elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) { } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) {
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
$access[self::ACCESS_NAME] = $property; $access[self::ACCESS_NAME] = $property;
} elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) { } elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) {

View File

@ -62,9 +62,13 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
$propertyName = $this->getPropertyName($reflectionMethod->name); $propertyName = $this->getPropertyName($reflectionMethod->name);
if ($propertyName) { if (!$propertyName || isset($properties[$propertyName])) {
$properties[$propertyName] = true; continue;
} }
if (!preg_match('/^[A-Z]{2,}/', $propertyName)) {
$propertyName = lcfirst($propertyName);
}
$properties[$propertyName] = true;
} }
return array_keys($properties); return array_keys($properties);

View File

@ -36,18 +36,19 @@ class ReflectionExtractorTest extends \PHPUnit_Framework_TestCase
'bal', 'bal',
'parent', 'parent',
'collection', 'collection',
'B',
'foo', 'foo',
'foo2', 'foo2',
'foo3', 'foo3',
'foo4', 'foo4',
'foo5', 'foo5',
'files', 'files',
'A', 'a',
'B', 'DOB',
'C', 'c',
'D', 'd',
'E', 'e',
'F', 'f',
), ),
$this->extractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy') $this->extractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')
); );

View File

@ -46,6 +46,11 @@ class Dummy extends ParentDummy
*/ */
public $collection; public $collection;
/**
* @var ParentDummy
*/
public $B;
/** /**
* A. * A.
* *
@ -63,4 +68,13 @@ class Dummy extends ParentDummy
public function setB(ParentDummy $parent = null) public function setB(ParentDummy $parent = null)
{ {
} }
/**
* Date of Birth.
*
* @return \DateTime
*/
public function getDOB()
{
}
} }

View File

@ -9,7 +9,7 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
namespace Symfony\Component\PropertyInfo\PropertyInfo\Tests; namespace Symfony\Component\PropertyInfo\Tests;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor; use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyExtractor; use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyExtractor;

View File

@ -9,7 +9,7 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
namespace Symfony\Component\PropertyInfo\PropertyInfo\Tests; namespace Symfony\Component\PropertyInfo\Tests;
use Symfony\Component\PropertyInfo\Type; use Symfony\Component\PropertyInfo\Type;

View File

@ -83,6 +83,8 @@ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
* *
* @param mixed $credentials * @param mixed $credentials
* @param UserInterface $user * @param UserInterface $user
*
* @return bool
* *
* @throws AuthenticationException * @throws AuthenticationException
*/ */

View File

@ -32,7 +32,7 @@ interface AuthenticationEntryPointInterface
* *
* Examples: * Examples:
* A) For a form login, you might redirect to the login page * A) For a form login, you might redirect to the login page
* return new Response('/login'); * return new RedirectResponse('/login');
* B) For an API token authentication system, you return a 401 response * B) For an API token authentication system, you return a 401 response
* return new Response('Auth header required', 401); * return new Response('Auth header required', 401);
* *