Skip to content

[Contracts/Service] add LazyString with new fromStringable(), isStringable() and resolve() methods #34190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"psr/container": "^1.0",
"psr/link": "^1.0",
"psr/log": "~1.0",
"symfony/contracts": "^1.1.7|^2",
"symfony/contracts": "^1.2|^2",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/polyfill-intl-idn": "^1.10",
Expand Down
1 change: 0 additions & 1 deletion src/Symfony/Component/DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ CHANGELOG
* made singly-implemented interfaces detection be scoped by file
* added ability to define a static priority method for tagged service
* added support for improved syntax to define method calls in Yaml
* added `LazyString` for lazy computation of string values injected into services
* made the `%env(base64:...)%` processor able to decode base64url

4.3.0
Expand Down
72 changes: 0 additions & 72 deletions src/Symfony/Component/DependencyInjection/Tests/LazyStringTest.php

This file was deleted.

3 changes: 1 addition & 2 deletions src/Symfony/Component/DependencyInjection/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@
"require": {
"php": "^7.1.3",
"psr/container": "^1.0",
"symfony/service-contracts": "^1.1.6|^2"
"symfony/service-contracts": "^1.2|^2"
},
"require-dev": {
"symfony/yaml": "^3.4|^4.0|^5.0",
"symfony/config": "^4.3|^5.0",
"symfony/error-handler": "^4.4|^5.0",
"symfony/expression-language": "^3.4|^4.0|^5.0"
},
"suggest": {
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/HttpClient/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"require": {
"php": "^7.1.3",
"psr/log": "^1.0",
"symfony/http-client-contracts": "^1.1.8|^2",
"symfony/http-client-contracts": "^1.2|^2",
"symfony/polyfill-php73": "^1.11"
},
"require-dev": {
Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Contracts/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

1.2.0
-----

* added `LazyString` for lazy computation of string values injected into services

1.1.0
-----

Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Contracts/Cache/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
"dev-master": "1.2-dev"
}
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Contracts/EventDispatcher/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
"dev-master": "1.2-dev"
}
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Contracts/HttpClient/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
"dev-master": "1.2-dev"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection;
namespace Symfony\Contracts\Service;

/**
* A string whose value is computed lazily by a callback.
Expand Down Expand Up @@ -49,6 +49,47 @@ public static function fromCallable($callback, ...$arguments): self
return $lazyString;
}

/**
* @param object|string|int|float|bool $value A scalar or an object that implements the __toString() magic method
*
* @return static
*/
public static function fromStringable($value): self
{
if (!self::isStringable($value)) {
throw new \TypeError(sprintf('Argument 1 passed to %s() must be a scalar or an object that implements the __toString() magic method, %s given.', __METHOD__, \is_object($value) ? \get_class($value) : \gettype($value)));
}

if (\is_object($value)) {
return static::fromCallable([$value, '__toString']);
}

$lazyString = new static();
$lazyString->value = (string) $value;

return $lazyString;
}

/**
* Tells whether the provided value can be cast to string.
*/
final public static function isStringable($value): bool
{
return \is_string($value) || $value instanceof self || (\is_object($value) ? \is_callable([$value, '__toString']) : is_scalar($value));
}

/**
* Casts scalars and stringable objects to strings.
*
* @param object|string|int|float|bool $value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should null be supported too ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so - if one wants to deal with null, one can always do resolve($foo ?? 'bar')

*
* @throws \TypeError When the provided value is not stringable
*/
final public static function resolve($value): string
{
return $value;
}

public function __toString()
{
if (\is_string($this->value)) {
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Contracts/Service/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
"dev-master": "1.2-dev"
}
}
}
112 changes: 112 additions & 0 deletions src/Symfony/Contracts/Tests/Service/LazyStringTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Contracts\Service\Tests;

use PHPUnit\Framework\TestCase;
use Symfony\Component\ErrorHandler\ErrorHandler;
use Symfony\Contracts\Service\LazyString;

class LazyStringTest extends TestCase
{
public function testLazyString()
{
$count = 0;
$s = LazyString::fromCallable(function () use (&$count) {
return ++$count;
});

$this->assertSame(0, $count);
$this->assertSame('1', (string) $s);
$this->assertSame(1, $count);
}

public function testLazyCallable()
{
$count = 0;
$s = LazyString::fromCallable([function () use (&$count) {
return new class($count) {
private $count;

public function __construct(int &$count)
{
$this->count = &$count;
}

public function __invoke()
{
return ++$this->count;
}
};
}]);

$this->assertSame(0, $count);
$this->assertSame('1', (string) $s);
$this->assertSame(1, $count);
$this->assertSame('1', (string) $s); // ensure the value is memoized
$this->assertSame(1, $count);
}

/**
* @runInSeparateProcess
*/
public function testReturnTypeError()
{
ErrorHandler::register();

$s = LazyString::fromCallable(function () { return []; });

$this->expectException(\TypeError::class);
$this->expectExceptionMessage('Return value of '.__NAMESPACE__.'\{closure}() passed to '.LazyString::class.'::fromCallable() must be of the type string, array returned.');

(string) $s;
}

public function testFromStringable()
{
$this->assertInstanceOf(LazyString::class, LazyString::fromStringable('abc'));
$this->assertSame('abc', (string) LazyString::fromStringable('abc'));
$this->assertSame('1', (string) LazyString::fromStringable(true));
$this->assertSame('', (string) LazyString::fromStringable(false));
$this->assertSame('123', (string) LazyString::fromStringable(123));
$this->assertSame('123.456', (string) LazyString::fromStringable(123.456));
$this->assertStringContainsString('hello', (string) LazyString::fromStringable(new \Exception('hello')));
}

public function testResolve()
{
$this->assertSame('abc', LazyString::resolve('abc'));
$this->assertSame('1', LazyString::resolve(true));
$this->assertSame('', LazyString::resolve(false));
$this->assertSame('123', LazyString::resolve(123));
$this->assertSame('123.456', LazyString::resolve(123.456));
$this->assertStringContainsString('hello', LazyString::resolve(new \Exception('hello')));
}

public function testIsStringable()
{
$this->assertTrue(LazyString::isStringable('abc'));
$this->assertTrue(LazyString::isStringable(true));
$this->assertTrue(LazyString::isStringable(false));
$this->assertTrue(LazyString::isStringable(123));
$this->assertTrue(LazyString::isStringable(123.456));
$this->assertTrue(LazyString::isStringable(new \Exception('hello')));
}

public function testIsNotStringable()
{
$this->assertFalse(LazyString::isStringable(null));
$this->assertFalse(LazyString::isStringable([]));
$this->assertFalse(LazyString::isStringable(STDIN));
$this->assertFalse(LazyString::isStringable(new \StdClass()));
$this->assertFalse(LazyString::isStringable(@eval('return new class() {private function __toString() {}};')));
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Contracts/Translation/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
"dev-master": "1.2-dev"
}
}
}
3 changes: 2 additions & 1 deletion src/Symfony/Contracts/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"psr/container": "^1.0"
},
"require-dev": {
"symfony/error-handler": "^4.4|^5.0",
"symfony/polyfill-intl-idn": "^1.10"
},
"replace": {
Expand All @@ -47,7 +48,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
"dev-master": "1.2-dev"
}
}
}