Merge branch '5.1'

* 5.1:
  [PhpUnitBridge] Create a predictable symlink pointing to the local install
  [PropertyInfo] Backport support for typed properties (PHP 7.4)
  [PhpUnitBridge] Polyfill new phpunit 9.1 assertions
  [PhpUnitBridge] Move assertMatchesRegularExpression in PolyfillAssertTrait
  [PhpUnit] Add polyfill for assertMatchesRegularExpression()
  Update Notifier bridge readme
  [TwigBridge] Fix #37931: BC break where filter method `trans` did not allow null values for `$message` parameter anymore
  [PropertyAccess] Fix accessing dynamic properties
This commit is contained in:
Fabien Potencier 2020-08-28 18:19:10 +02:00
commit 5b3ebdc7e6
11 changed files with 114 additions and 5 deletions

View File

@ -13,6 +13,7 @@ namespace Symfony\Bridge\PhpUnit\Legacy;
use PHPUnit\Framework\Constraint\IsEqual;
use PHPUnit\Framework\Constraint\LogicalNot;
use PHPUnit\Framework\Constraint\RegularExpression;
use PHPUnit\Framework\Constraint\StringContains;
use PHPUnit\Framework\Constraint\TraversableContains;

View File

@ -284,6 +284,16 @@ EOPHP
chdir($oldPwd);
}
// Create a symlink with a predictable path pointing to the currently used version.
// This is useful for static analytics tools such as PHPStan having to load PHPUnit's classes
// and for other testing libraries such as Behat using PHPUnit's assertions.
chdir($PHPUNIT_DIR);
if (file_exists('phpunit')) {
@unlink('phpunit');
}
@symlink($PHPUNIT_VERSION_DIR, 'phpunit');
chdir($oldPwd);
if ($PHPUNIT_VERSION < 8.0) {
$argv = array_filter($argv, function ($v) use (&$argc) {
if ('--do-not-cache-result' !== $v) {

View File

@ -91,8 +91,12 @@ final class TranslationExtension extends AbstractExtension
return $this->translationNodeVisitor ?: $this->translationNodeVisitor = new TranslationNodeVisitor();
}
public function trans(string $message, array $arguments = [], string $domain = null, string $locale = null, int $count = null): string
public function trans(?string $message, array $arguments = [], string $domain = null, string $locale = null, int $count = null): string
{
if (null === $message || '' === $message) {
return '';
}
if (null !== $count) {
$arguments['%count%'] = $count;
}

View File

@ -118,6 +118,10 @@ class TranslationExtensionTest extends TestCase
['{{ "{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples"|trans(count=count) }}', 'There is 5 apples', ['count' => 5]],
['{{ text|trans(count=5, arguments={\'%name%\': \'Symfony\'}) }}', 'There is 5 apples (Symfony)', ['text' => '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%)']],
['{{ "{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples"|trans({}, "messages", "fr", count) }}', 'There is 5 apples', ['count' => 5]],
// trans filter with null message
['{{ null|trans }}', ''],
['{{ foo|trans }}', '', ['foo' => null]],
];
}

View File

@ -3,6 +3,18 @@ Firebase Notifier
Provides Firebase integration for Symfony Notifier.
DSN example
-----------
```
// .env file
FIREBASE_DSN=firebase://USERNAME:PASSWORD@default
```
where:
- `USERNAME` is your Firebase username
- `PASSWORD` is your Firebase password
Resources
---------

View File

@ -3,6 +3,18 @@ Mattermost Notifier
Provides Mattermost integration for Symfony Notifier.
DSN example
-----------
```
// .env file
MATTERMOST_DSN=mattermost://ACCESS_TOKEN@default?channel=CHANNEL
```
where:
- `ACCESS_TOKEN` is your Mattermost access token
- `CHANNEL` is your Mattermost channel
Resources
---------

View File

@ -3,6 +3,18 @@ RocketChat Notifier
Provides RocketChat integration for Symfony Notifier.
DSN example
-----------
```
// .env file
ROCKETCHAT_DSN=rocketchat://ACCESS_TOKEN@default?channel=CHANNEL
```
where:
- `ACCESS_TOKEN` is your RocketChat access token
- `CHANNEL` is your RocketChat channel
Resources
---------

View File

@ -3,6 +3,19 @@ Sinch Notifier
Provides Sinch integration for Symfony Notifier.
DSN example
-----------
```
// .env file
SINCH_DSN=sinch://SERVICE_PLAN_ID:AUTH_TOKEN@default?from=FROM
```
where:
- `SERVICE_PLAN_ID` is your Sinch service plan id
- `AUTH_TOKEN` is your Sinch auth token
- `FROM` is your sender
Resources
---------

View File

@ -450,10 +450,16 @@ class PropertyAccessor implements PropertyAccessorInterface
throw $e;
}
} elseif ($object instanceof \stdClass && property_exists($object, $property)) {
$result[self::VALUE] = $object->$property;
if (isset($zval[self::REF])) {
$result[self::REF] = &$object->$property;
} elseif (property_exists($object, $property)) {
try {
$result[self::VALUE] = $object->$property;
if (isset($zval[self::REF])) {
$result[self::REF] = &$object->$property;
}
} catch (\Error $e) {
if (!$ignoreInvalidProperty) {
throw new NoSuchPropertyException(sprintf('Can\'t read protected or private property "%s" in class "%s".', $property, $class), 0, $e);
}
}
} elseif (!$ignoreInvalidProperty) {
throw new NoSuchPropertyException(sprintf('Can\'t get a way to read the property "%s" in class "%s".', $property, $class));

View File

@ -0,0 +1,11 @@
<?php
namespace Symfony\Component\PropertyAccess\Tests\Fixtures;
class TestClassDynamicProperty
{
public function __construct($dynamicProperty)
{
$this->dynamicProperty = $dynamicProperty;
}
}

View File

@ -22,6 +22,7 @@ use Symfony\Component\PropertyAccess\Tests\Fixtures\ReturnTyped;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestAdderRemoverInvalidArgumentLength;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestAdderRemoverInvalidMethods;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClass;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassDynamicProperty;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassIsWritable;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicCall;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicGet;
@ -98,6 +99,29 @@ class PropertyAccessorTest extends TestCase
$this->assertSame($value, $this->propertyAccessor->getValue($objectOrArray, $path));
}
/**
* Test get dynamic value from object is other than \stdClass instance.
*/
public function testGetDynamicValue()
{
$value = 'dynamicPropertyValue';
$path = 'dynamicProperty';
$object = new TestClassDynamicProperty($value);
$this->assertSame($value, $this->propertyAccessor->getValue($object, $path));
}
/**
* Ensure exact exception with message was thrown on access to non-public property.
*/
public function testGetInaccessibleProperty()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
$this->expectExceptionMessage(sprintf('Can\'t read protected or private property "%s" in class "%s".', 'protectedProperty', TestClass::class));
$this->propertyAccessor->getValue(new TestClass('Bernhard'), 'protectedProperty');
}
/**
* @dataProvider getPathsWithMissingProperty
*/