Merge branch '2.8' into 3.0

This commit is contained in:
Tobias Schultze 2015-12-15 03:03:07 +01:00
commit 06dd574627
18 changed files with 169 additions and 54 deletions

View File

@ -19,15 +19,29 @@ namespace Symfony\Bridge\PhpUnit;
class DeprecationErrorHandler
{
const MODE_WEAK = 'weak';
const MODE_WEAK_VERBOSE = 'weak-verbose';
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) {
return;
}
if (self::MODE_WEAK !== $mode && (!isset($mode[0]) || '/' !== $mode[0])) {
$mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0;
}
$deprecations = array(
'unsilencedCount' => 0,
'remainingCount' => 0,
@ -147,7 +161,8 @@ class DeprecationErrorHandler
if (!empty($notices)) {
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);
}
});

View File

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

View File

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

View File

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

View File

@ -53,10 +53,12 @@
</div>
{% else %}
{# 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 %}
{% if log.context.level is defined and log.context.type is defined and log.context.type in [constant('E_DEPRECATED'), constant('E_USER_DEPRECATED')] %}
{% set deprecation_logs = deprecation_logs|merge([log]) %}
{% elseif log.context.scream is defined and log.context.scream == true %}
{% set silenced_logs = silenced_logs|merge([log]) %}
{% elseif log.priorityName == 'DEBUG' %}
{% set debug_logs = debug_logs|merge([log]) %}
{% else %}
@ -108,6 +110,21 @@
{% endif %}
</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>
{% endif %}
{% endblock %}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,19 +24,74 @@ use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
*/
class PropertyAccessor implements PropertyAccessorInterface
{
/**
* @internal
*/
const VALUE = 0;
/**
* @internal
*/
const IS_REF = 1;
/**
* @internal
*/
const IS_REF_CHAINED = 2;
/**
* @internal
*/
const ACCESS_HAS_PROPERTY = 0;
/**
* @internal
*/
const ACCESS_TYPE = 1;
/**
* @internal
*/
const ACCESS_NAME = 2;
/**
* @internal
*/
const ACCESS_REF = 3;
/**
* @internal
*/
const ACCESS_ADDER = 4;
/**
* @internal
*/
const ACCESS_REMOVER = 5;
/**
* @internal
*/
const ACCESS_TYPE_METHOD = 0;
/**
* @internal
*/
const ACCESS_TYPE_PROPERTY = 1;
/**
* @internal
*/
const ACCESS_TYPE_MAGIC = 2;
/**
* @internal
*/
const ACCESS_TYPE_ADDER_AND_REMOVER = 3;
/**
* @internal
*/
const ACCESS_TYPE_NOT_FOUND = 4;
/**
@ -62,6 +117,9 @@ class PropertyAccessor implements PropertyAccessorInterface
/**
* Should not be used by application code. Use
* {@link PropertyAccess::createPropertyAccessor()} instead.
*
* @param bool $magicCall
* @param bool $throwExceptionOnInvalidIndex
*/
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)) {
// 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()
// returns true, consequently the following line will result in a
// fatal error.
@ -411,7 +469,6 @@ class PropertyAccessor implements PropertyAccessorInterface
$getsetter = lcfirst($camelProp); // jQuery style, e.g. read: last(), write: last($item)
$isser = 'is'.$camelProp;
$hasser = 'has'.$camelProp;
$classHasProperty = $reflClass->hasProperty($property);
if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) {
$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_NAME] = $property;
$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_NAME] = $property;
$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()) {
// we call the getter and hope the __call do the job
$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]);
} elseif (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property)) {
// 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()
// returns true, consequently the following line will result in a
// fatal error.
@ -579,7 +633,6 @@ class PropertyAccessor implements PropertyAccessorInterface
private function getWriteAccessInfo($object, $property, $value)
{
$key = get_class($object).'::'.$property;
$guessedAdders = '';
if (isset($this->writePropertyCache[$key])) {
$access = $this->writePropertyCache[$key];
@ -594,13 +647,7 @@ class PropertyAccessor implements PropertyAccessorInterface
if (is_array($value) || $value instanceof \Traversable) {
$methods = $this->findAdderAndRemover($reflClass, $singulars);
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 {
if (null !== $methods) {
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER;
$access[self::ACCESS_ADDER] = $methods[0];
$access[self::ACCESS_REMOVER] = $methods[1];
@ -608,11 +655,9 @@ class PropertyAccessor implements PropertyAccessorInterface
}
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)
$classHasProperty = $reflClass->hasProperty($property);
if ($this->isMethodAccessible($reflClass, $setter, 1)) {
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
$access[self::ACCESS_NAME] = $setter;
@ -622,7 +667,7 @@ class PropertyAccessor implements PropertyAccessorInterface
} elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_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_NAME] = $property;
} 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) {
$propertyName = $this->getPropertyName($reflectionMethod->name);
if ($propertyName) {
$properties[$propertyName] = true;
if (!$propertyName || isset($properties[$propertyName])) {
continue;
}
if (!preg_match('/^[A-Z]{2,}/', $propertyName)) {
$propertyName = lcfirst($propertyName);
}
$properties[$propertyName] = true;
}
return array_keys($properties);

View File

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

View File

@ -46,6 +46,11 @@ class Dummy extends ParentDummy
*/
public $collection;
/**
* @var ParentDummy
*/
public $B;
/**
* A.
*
@ -63,4 +68,13 @@ class Dummy extends ParentDummy
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.
*/
namespace Symfony\Component\PropertyInfo\PropertyInfo\Tests;
namespace Symfony\Component\PropertyInfo\Tests;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyExtractor;

View File

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

View File

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

View File

@ -32,7 +32,7 @@ interface AuthenticationEntryPointInterface
*
* Examples:
* 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
* return new Response('Auth header required', 401);
*