This PR was merged into the 5.2-dev branch.
Discussion
----------
[PropertyAccess] fix the deprecation message
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | no
| Deprecations? | no
| Tickets |
| License | MIT
| Doc PR |
Commits
-------
a9cbac77eb fix the deprecation message
* 5.1:
Fix CS
[HttpClient][MockHttpClient][DX] Throw when the response factory callable does not return a valid response
[FrameworkBundle] Do not pass the base uri twice to scoped http clients
* 4.4:
Fix CS
[HttpClient][MockHttpClient][DX] Throw when the response factory callable does not return a valid response
[FrameworkBundle] Do not pass the base uri twice to scoped http clients
This PR was merged into the 5.2-dev branch.
Discussion
----------
[Workflow] Expose the Metadata Store in the DIC
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | no
| Deprecations? | no
| Tickets | Fix#37955
| License | MIT
| Doc PR |
I did not updated the CHANGELOG, because IMHO it not worth it
Commits
-------
aa7a444c86 [Workflow] Expose the Metadata Store in the DIC
This PR was squashed before being merged into the 5.2-dev branch.
Discussion
----------
[Translation] Add support for calling 'trans' with ICU formatted messages
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | maybe, see https://github.com/symfony/symfony/issues/37228
| New feature? | yes
| Deprecations? | no
| Tickets | Fix#37228
| License | MIT
| Doc PR | symfony/symfony-docs#13875
Motivation:
```
$apples = [0 => 'No apples', 1 => '1 apple', '# apples'];
echo _m($apples, ['count' => 0]); // Outputs 'No apples'
echo _m($apples, ['count' => 2]); // Outputs '2 apples'
```
where `_m` is a wrapper my application is using, but we obviously don't want to replicate many of the effort of the translation component, so it relies on `trans`.
This wrapper itself could be integrated into Symfony, if deemed appropriate.
See #37228
Commits
-------
d2ec41f4ef [Translation] Add support for calling 'trans' with ICU formatted messages
This PR was merged into the 4.4 branch.
Discussion
----------
[HttpClient][MockHttpClient][DX] Throw when the response factory callable does not return a valid response
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | no
| New feature? | no
| Deprecations? | no
| Tickets | -
| License | MIT
| Doc PR | -
The current message is `TypeError: Argument 4 passed to Symfony\Component\HttpClient\Response\MockResponse::fromRequest() must implement interface Symfony\Contracts\HttpClient\ResponseInterface, instance of Generator given`.
I lost some time with this because I was passing a callable that returns a \Generator instead of passing the resulting \Generator directly. We could support that case but I guess with the added exception message, it is clear we don't support it at all.
Commits
-------
564dce39f8 [HttpClient][MockHttpClient][DX] Throw when the response factory callable does not return a valid response
This PR was merged into the 4.4 branch.
Discussion
----------
[FrameworkBundle] Do not pass the base uri twice to scoped http clients
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | no
| New feature? | no
| Deprecations? | no
| Tickets | -
| License | MIT
| Doc PR | -
I looked at my dumped scoped http client PHP file and I noticed the base uri was passed two times. I thought there was a bug somewhere in my configuration. Actually, we only need to pass it as the method argument here.
Commits
-------
e2fce0d89f [FrameworkBundle] Do not pass the base uri twice to scoped http clients
This PR was squashed before being merged into the 5.2-dev branch.
Discussion
----------
[Translation] Translatable objects
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets | None
| License | MIT
| Doc PR | symfony/symfony-docs#... - Will be added if this gets approval
**Scenario**
When writing business logic, this code typically lives within services. With complex situations, there can be lots of business logic exceptions or messages being created. In order to have the best user experience, we try to display detailed messages to the end user so if it's an error with some data, they know what they need to do to correct the situation. Sometimes this is through a message on a page, an email, etc. In some cases a generic message might be displayed and we log the message instead or send the message in an email to a 3rd party.
The service shouldn't need to care how the message is going to be handled, but just needs to create it in a way that it can be translated if needed.
**Options**
Translating when the message is created. There are two issues with this approach. Autowiring the translator interface to services that will display an error isn't ideal as it will increase the dependencies the service has. The larger issue is that because we would be throwing an exception that contains a string or passing a message that's a string (what the translator returns), if we have generic twig templates that run the translator function on a message, we're doing a double translation and the debug toolbar will indicate that there is a missing message and turn red. This makes finding missing translations much more difficult as we have to determine if there are any false positives and turns the red indicator into white noise.
Translating when displaying on a template. This option is possible, but adds complexity to our templates. Each time we display a message, we need to have some kind of logic that checks to see if it should be ran through the translator as-is or we need to get the data from the object and pass into the translator.
**Proposed Solution**
Adding a Phrase object that we can create and pass around reduces the template complexity and adds a nice way to create objects that can be translated at any point. This something that will be very familiar with Magento developers and other platforms also have similar solutions.
**TODO**
- [x] Determine if overwriting the parameters should be allowed (currently implemented this way)
- [x] Determine the name of the translatable class, currently `Phrase` as `Message` will likely get confused with Messenger
- [x] Determine if there should be a global function to allow quickly creating these. Similar to `__()` in Magento
- [ ] Add docs
- [x] Add tests
Happy to get as much feedback as possible. I know a feature like this would be very welcomed by my team.
Commits
-------
dc6b3bf62d [Translation] Translatable objects
* 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
* 4.4:
[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()
This PR was merged into the 4.4 branch.
Discussion
----------
[PropertyInfo] Backport support for typed properties (PHP 7.4)
| Q | A
| ------------- | ---
| Branch? | 4.4 <!-- see below -->
| Bug fix? | yes
| New feature? | no <!-- please update src/**/CHANGELOG.md files -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tickets | N/A. <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead -->
| License | MIT
| Doc PR | https://github.com/symfony/symfony-docs/pull/13719 to be fixed
Backports #34557 as suggested.
One of my workmate hit this issue with the Serializer component and it was not obvious for him why it was not working, without hint.
Commits
-------
1b19f255a3 [PropertyInfo] Backport support for typed properties (PHP 7.4)
This PR was squashed before being merged into the 4.4 branch.
Discussion
----------
[PhpUnitBridge] Create a predictable symlink pointing to the local install
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | no
| New feature? | yes <!-- please update src/**/CHANGELOG.md files -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tickets | n/a
| License | MIT
| Doc PR | n/a
Static analysis tools such as PHPStan need to be able to autoload classes provided by to work. It's also useful to use the assertions provided by PHPUnit with other testing tools such as Behat.
When using `simple-phpunit`, PHPUnit isn't installed in the `vendor/` directory, consequently other tools cannot autoload its classes.
A workaround is to configure these tools to load the autoloader installed by `simple-phpunit`. Example with PHPstan:
```neon
parameters:
bootstrapFiles:
- vendor/bin/.phpunit/phpunit-9.2-0/vendor/autoload.php
```
However, the path of the autoloader isn't predictable: it depends of PHPUnit version.
This PR changes `simple-phpunit` to create a symlink with a predictable path (`vendor/.phpunit/phpunit`) pointing to the currently used version of PHPUnit, so it is possible to hardcode this value in config files.
The symlink is recreated before every run, so if a different version of PHPUnit must be used because the PHP version in use is different from the previous run (frequent when developing libraries compatible with multiple PHP versions), it still works.
Commits
-------
bf7654f245 [PhpUnitBridge] Create a predictable symlink pointing to the local install
This PR was merged into the 5.2-dev branch.
Discussion
----------
[Security] Also mark the authenticator security system experimental in 5.2
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | no
| Deprecations? | no
| Tickets | -
| License | MIT
| Doc PR | -
Commits
-------
176aef63d9 Also mark the authenticator security system experimental in 5.2
This PR was squashed before being merged into the 5.2-dev branch.
Discussion
----------
[Mailer] Implement additional mailer transport options
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets | Fix#37300
| License | MIT
| Doc PR | https://github.com/symfony/symfony-docs/pull/13911
This implements additional transport configuration options mentioned in #37300. It also adds a `command` option to be able to define the command used by the `sendmail` transport.
Examples:
```yml
framework:
mailer:
transports:
sendmail: sendmail://default?command=/usr/sbin/sendmail%%20-oi%%20-t
local_domain: smtps://smtp.example.com?local_domain=example.org
restart_threshold: smtps://smtp.example.com?restart_threshold=10&restart_threshold_sleep=1
ping_threshold: smtps://smtp.example.com?ping_threshold=200
```
Commits
-------
665d1cd3fa [Mailer] Implement additional mailer transport options
This PR was merged into the 5.2-dev branch.
Discussion
----------
Update Notifier bridge DSN in readme
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | no
| Deprecations? | no
| Tickets |
| License | MIT
| Doc PR |
Add missing DSN documentation in README for 5.2
Commits
-------
eedd72cd98 Update Notifier bridge DSN in readme
This PR was merged into the 4.4 branch.
Discussion
----------
[PhpUnitBridge] Move assertMatchesRegularExpression in PolyfillAssertTrait
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | no
| New feature? | no <!-- please update src/**/CHANGELOG.md files -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tickets | n/a
Move the polyfill method introduced in #37960 in the `Assert` trait. Sorry I noticed this trait later.
Commits
-------
0426113eda [PhpUnitBridge] Move assertMatchesRegularExpression in PolyfillAssertTrait
This PR was merged into the 5.2-dev branch.
Discussion
----------
[HttpClient] fix pausing AmpResponse
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Tickets | None
| License | MIT
| Doc PR | None
This fixes the pause handler while streaming bodies in `AmpHttpClient`.
Needs to be rebased onto the target branch and needs some test fixes. @nicolas-grekas FYI.
Commits
-------
bf2d1cf6e7 Improve pause handler
This PR was squashed before being merged into the 5.2-dev branch.
Discussion
----------
[HttpClient][DI] Add an option to use the MockClient in functional tests
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets |
| License | MIT
| Doc PR | TODO
Let's say you want to mock HTTP responses in your test or dev environment:
First, you create an invokable class responsible of generating the response:
```php
namespace App\Tests;
use Symfony\Contracts\HttpClient\ResponseInterface;
class MyAwesomeCallback
{
public function __invoke(string $method, string $url, array $options = []): ResponseInterface
{
// load a fixture file or generate dynamic data
}
}
```
Then configure it:
```yaml
# config/services_test.yaml
services:
# ...
App\Tests\MyAwesomeCallback: ~
# config/packages/test/framework.yaml
framework:
http_client:
mock_response_factory: App\Tests\MyAwesomeCallback
```
The HttpClient will now be using MockHttpClient in your functional test:
```php
$client = static::createClient();
// No live HTTP connection made
$client->request('GET', '/path-with-http-call');
```
Commits
-------
b53739c79d [HttpClient][DI] Add an option to use the MockClient in functional tests
This PR was merged into the 5.2-dev branch.
Discussion
----------
[Semaphore] Added the component
| Q | A
| ------------- | ---
| Branch? | yes
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets |
| License | MIT
| Doc PR |
---
[Semaphore] Added the component
Few years ago, we have introduced the Lock component. This is a very nice component, but sometime it is not enough. Sometime you need semaphore.
This is why I'm introducing this new component.
## What is a Semaphore ?
From wikipedia:
> In computer science, a semaphore is a variable or abstract data type used to control access to a common resource by multiple processes in a concurrent system such as a multitasking operating system. A semaphore is simply a variable. This variable is used to solve critical section problems and to achieve process synchronization in the multi processing environment. A trivial semaphore is a plain variable that is changed (for example, incremented or decremented, or toggled) depending on programmer-defined conditions.
This new component is more than a variable. This is an abstraction on top of different storage.
To make a quick comparison with a lock:
* A lock allows only 1 process to access a resource;
* A semaphore allow N process to access a resource.
Basically, a lock is a semaphore where `N = 1`.
### Possible confusion
PHP exposes some `sem_*` functions like [`sem_acquire`](http://php.net/sem_acquire). This module provides wrappers for the System V IPC family of functions. It includes semaphores, shared memory and inter-process messaging (IPC).
The Lock component has a storage that works with theses functions. It uses it with `N = 1`.
## What are the use-cases ?
Wikipedia has some [examples](https://en.wikipedia.org/wiki/Semaphore_(programming)#Examples)
But I can add one more commun use case.
If you are building an async system that process user data, you may want to priorise all jobs. You can achieve that by running at maximum N jobs per user at the same time. If the user has more resources, you give him more concurrent jobs (so a bigger `N`).
Thanks to semaphores, it's pretty easy to know if a new job can be run.
### Some concrete use-cases
I'm not saying the following services are using semaphore, but they may solve the previous problematic with semaphores. Here is some examples:
* services like testing platform where a user can test N projects concurrently (travis, circle, appveyor, insight, ...)
* services that ingest lots of data (newrelic, datadog, blackfire, segment.io, ...))
* services that send email in batch (campaign monitor, mailchimp, ...)
* etc...
## How to use it ?
To do so, since PHP is mono-threaded, you run M PHP workers. And in each worker, you look for for the next job. When you grab a job, you try to acquires a semaphore. If you got it, you process the job. If not you try another job.
FTR in other language, like Go, there are no need to run M workers, one is enough.
### With Symfony
```php
<?php
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\RedisStore as LockRedisStore;
use Symfony\Component\Semaphore\SemaphoreFactory;
use Symfony\Component\Semaphore\Store\RedisStore;
require __DIR__.'/vendor/autoload.php';
$redis = new Redis();
$redis->connect('172.17.0.2');
// Internally, Semaphore needs a lock
$lock = (new LockFactory(new LockRedisStore($redis)))->createLock('test:lock', 1);
// Create a semaphore:
// * name = test
// * limit = 3 (it means only 3 process are allowed)
// * ttl = 10 seconds : Maximum expected semaphore duration in seconds
$semaphore = (new SemaphoreFactory($lock, new RedisStore($redis)))->createSemaphore('test', 3, 10);
if (!$semaphore->acquire()) {
echo "Could not acquire the semaphore\n";
exit(1);
}
// The semaphore has been acquired
// Do the heavy job
for ($i = 0; $i < 100; ++$i) {
sleep(1);
// Before the expiration, refresh the semaphore if the job is not finished yet
if ($i % 9 === 0) {
$semaphore->refresh();
}
}
// Release it when finished
$semaphore->release();
```
## Prior art
I looked at [packagist](https://packagist.org/?query=semaphore) and:
* most of packages are using a semaphore storage for creating a lock. So there are not relevant here;
* some packages need an async framework to be used (amphp for example);
* the only packages really implementing a semaphore, has a really low code quality and some bugs.
## Current implementation
1. I initially copied the Lock component since the external API is quite similar;
1. I simplified it a lot for the current use case;
1. I implemented the RedisStorage according the [redis book](https://redislabs.com/ebook/part-2-core-concepts/chapter-6-application-components-in-redis/6-3-counting-semaphores/;)
1. I forced a TTL on the storage.
---
TODO:
* [ ] documentation
* [x] test
* [x] ~~move the lock requirements to the redis storage only ?~~ Not needed anymore
Commits
-------
891285475e [Semaphore] Added the component
This PR was merged into the 5.2-dev branch.
Discussion
----------
[Security] Lazily load the user during the check passport event
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| Deprecations? | yes
| Tickets | Fix#37436
| License | MIT
| Doc PR | tbd
**Before**
```php
class ApiKeyAuthenticator extends AbstractAuthenticator
{
// ...
public function authenticate(Request $request): PassportInterface
{
$email = $request->headers->get('X-USER-EMAIL');
if (false === strpos($email, '@')) {
throw new BadCredentialsException('Email is not a valid email address.');
}
$user = $this->userRepository->findOneBy(['email' => $email]);
if (null === $user) {
throw new UsernameNotFoundException();
}
return new SelfValidatingPassport($user);
}
}
```
**After**
```php
class ApiKeyAuthenticator extends AbstractAuthenticator
{
// ...
public function authenticate(Request $request): PassportInterface
{
$email = $request->headers->get('X-USER-EMAIL');
if (false === strpos($email, '@')) {
throw new BadCredentialsException('Email is not a valid email address.');
}
// a global ChainUserProvider (or firewall provider if explicitly configured) will be
// used to load the User with $email as username
return new SelfValidatingPassport($email);
// or a custom closure to load the user
return new SelfValidatingPassport(new UserBadge($email, function ($username) {
return $this->userRepository->findOneBy(['email' => $username]);
});
}
}
```
Doing it this way has a couple advantages (some of which are already mentioned in the issue):
* Some listeners on `CheckPassportEvent` need to execute *before* loading the user - to reduce resources (e.g. CSRF protection, if CSRF fails, no DB call should be made to load user - and also login throttling);
* Some listeners require knowing the username of the login action (e.g. login throttling on IP and username);
* The `UserProviderListener` allows to remove yet another centralized action in the authentication process from the authenticator class to the Symfony framework.
Automatic User Provider integration
---
Instead of passing the credentials and a closure to `UserBadge`, you can also just pass a (string) username. The user provider will then be used to load the user. This only works for `custom_authenticators` as of this moment.
* By default, a chain user provider with all configured `user_providers` will be used as the user provider;
* However, if you explicitly configure a `provider` for that firewall, that provider will be used (using a listener with higher priority).
Commits
-------
907ef311bf Lazily load the user during the check passport event