Skip to content

Started reworking expression strings into Expression objects #307

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

Draft
wants to merge 10 commits into
base: 5.x
Choose a base branch
from
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ static-code-analysis: vendor ## Runs a static code analysis with phpstan/phpstan
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/psalm

.PHONY: test
test: test-unit test-functional ## Runs all test suites with phpunit/phpunit
test: test-unit test-integration ## Runs all test suites with phpunit/phpunit
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit

.PHONY: test-unit
test-unit: ## Runs unit tests with phpunit/phpunit
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit --testsuite=unit

.PHONY: test-functional
test-functional: ## Runs unit tests with phpunit/phpunit
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit --testsuite=functional
.PHONY: test-integration
test-integration: ## Runs unit tests with phpunit/phpunit
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit --testsuite=integration

.PHONY: dependency-analysis
dependency-analysis: vendor ## Runs a dependency analysis with maglnet/composer-require-checker
Expand Down
60 changes: 60 additions & 0 deletions docs/expressions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
Expressions
===========

Starting with version 5.4, we now support parsing expressions and extracting types and references to elements from them.

.. info::

An expression is, for example, the default value for a property or argument, the definition of an enum case or a
constant value. These are called expressions and can contain more complex combinations of operators and values.

As this library revolves around reflecting Static information, most parts of an expression are considered irrelevant;
except for type information -such as type hints- and references to other elements, such as constants. As such, whenever
an expression is interpreted, it will result in a string containing placeholders and an array containing the reflected
parts -such as FQSENs-.

This means that the getters like ``getDefault()`` will return a string or when you provide the optional argument
$isString as being false, it will return an Expression object; which, when cast to string, will provide the same result.

.. warning::

Deprecation: In version 6, we will remove the optional argument and always return an Expression object. When the
result was used as a string nothing will change, but code that checks if the output is a string will no longer
function starting from that version.

This will allow consumers to be able to extract types and links to elements from expressions. This allows consumers to,
for example, interpret the default value for a constructor promoted properties when it directly instantiates an object.

Creating expressions
--------------------

.. hint::

The description below is only for internal usage and to understand how expressions work, this library deals with
this by default.

In this library, we use the ExpressionPrinter to convert a PHP-Parser node -or expression- into an expression
like this::

$printer = new ExpressionPrinter();
$expressionTemplate = $printer->prettyPrintExpr($phpParserNode);
$expression = new Expression($expressionTemplate, $printer->getParts());

In the example above we assume that there is a PHP-Parser node representing an expression; this node is passed to the
ExpressionPrinter -which is an adapted PrettyPrinter from PHP-Parser- which will render the expression as a readable
template string containing placeholders, and a list of parts that can slot into the placeholders.

Consuming expressions
---------------------

When using this library, you can consume these expression objects either by

1. Directly casting them to a string - this will replace all placeholders with the stringified version of the parts
2. Use the render function - this will do the same as the previous methods but you can specify one or more overrides
for the placeholders in the expression

The second method can be used to create your own string values from the given parts and render, for example, links in
these locations.

Another way to use these expressions is to interpret the parts array, and through that way know which elements and
types are referred to in that expression.
4 changes: 0 additions & 4 deletions docs/filtering.rst

This file was deleted.

4 changes: 0 additions & 4 deletions docs/incremental-updates.rst

This file was deleted.

3 changes: 1 addition & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ Reflection
==========

.. toctree::
:hidden:

expressions
meta-data
4 changes: 0 additions & 4 deletions docs/inspecting.rst

This file was deleted.

4 changes: 0 additions & 4 deletions docs/integrating-with-silex-and-cilex.rst

This file was deleted.

4 changes: 0 additions & 4 deletions docs/usage.rst

This file was deleted.

4 changes: 0 additions & 4 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ parameters:
- '#Parameter \#1 \$fqsen of class phpDocumentor\\Reflection\\Php\\(.*) constructor expects phpDocumentor\\Reflection\\Fqsen, mixed given\.#'
- '#Parameter \#1 \$fqsen of method phpDocumentor\\Reflection\\Php\\File::addNamespace\(\) expects phpDocumentor\\Reflection\\Fqsen, mixed given\.#'
#
# there is one test case that prevents changing PropertyIterator::getDefault() to just return Expr (this is set in PhpParser)
# src/phpDocumentor/Reflection/Php/Factory/Property.php
- '#Parameter \#1 \$node of method PhpParser\\PrettyPrinterAbstract::prettyPrintExpr\(\) expects PhpParser\\Node\\Expr, PhpParser\\Node\\Expr\|string given\.#'
#
# Type hint in php-parser is incorrect.
- '#Cannot cast PhpParser\\Node\\Expr\|string to string.#'

Expand Down
7 changes: 5 additions & 2 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
<directory>./tests/integration</directory>
</testsuite>
</testsuites>
<php>
<ini name="error_reporting" value="E_ALL &amp; ~E_USER_DEPRECATED" />
</php>
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
Expand All @@ -35,8 +38,8 @@
</logging>
<listeners>
<listener
class="Mockery\Adapter\Phpunit\TestListener"
file="vendor/mockery/mockery/library/Mockery/Adapter/Phpunit/TestListener.php"
class="Mockery\Adapter\Phpunit\TestListener"
file="vendor/mockery/mockery/library/Mockery/Adapter/Phpunit/TestListener.php"
/>
</listeners>
</phpunit>
5 changes: 5 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
<projectFiles>
<directory name="src" />
<ignoreFiles>
<!--
ExpressionPrinter inherits all kinds of errors from PHP-Parser that I cannot fix, until a solution is found
we ignore this
-->
<file name="src/phpDocumentor/Reflection/Php/Expression/ExpressionPrinter.php"/>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
Expand Down
43 changes: 38 additions & 5 deletions src/phpDocumentor/Reflection/Php/Argument.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Mixed_;

use function is_string;
use function trigger_error;

use const E_USER_DEPRECATED;

/**
* Descriptor representing a single Argument of a method or function.
*/
Expand All @@ -27,8 +32,8 @@ final class Argument
/** @var Type a normalized type that should be in this Argument */
private Type $type;

/** @var string|null the default value for an argument or null if none is provided */
private ?string $default;
/** @var Expression|null the default value for an argument or null if none is provided */
private ?Expression $default;

/** @var bool whether the argument passes the parameter by reference instead of by value */
private bool $byReference;
Expand All @@ -38,22 +43,34 @@ final class Argument

/**
* Initializes the object.
*
* @param string|Expression|null $default
*/
public function __construct(
string $name,
?Type $type = null,
?string $default = null,
$default = null,
bool $byReference = false,
bool $isVariadic = false
) {
$this->name = $name;
$this->default = $default;
$this->byReference = $byReference;
$this->isVariadic = $isVariadic;
if ($type === null) {
$type = new Mixed_();
}

if (is_string($default)) {
trigger_error(
'Default values for arguments should be of type Expression, support for strings will be '
. 'removed in 6.x',
E_USER_DEPRECATED
);
$default = new Expression($default, []);
}

$this->default = $default;

$this->type = $type;
}

Expand All @@ -70,8 +87,24 @@ public function getType(): ?Type
return $this->type;
}

public function getDefault(): ?string
/**
* @return Expression|string|null
*/
public function getDefault(bool $asString = true)
{
if ($this->default === null) {
return null;
}

if ($asString) {
trigger_error(
'The Default value will become of type Expression by default',
E_USER_DEPRECATED
);

return (string) $this->default;
}

return $this->default;
}

Expand Down
43 changes: 38 additions & 5 deletions src/phpDocumentor/Reflection/Php/Constant.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
use phpDocumentor\Reflection\Location;
use phpDocumentor\Reflection\Metadata\MetaDataContainer as MetaDataContainerInterface;

use function is_string;
use function trigger_error;

use const E_USER_DEPRECATED;

/**
* Descriptor representing a constant
*/
Expand All @@ -30,7 +35,8 @@ final class Constant implements Element, MetaDataContainerInterface

private ?DocBlock $docBlock;

private ?string $value;
/** @var string|Expression|null */
private $value;

private Location $location;

Expand All @@ -42,30 +48,57 @@ final class Constant implements Element, MetaDataContainerInterface

/**
* Initializes the object.
*
* @param Expression|string|null $value
*/
public function __construct(
Fqsen $fqsen,
?DocBlock $docBlock = null,
?string $value = null,
$value = null,
?Location $location = null,
?Location $endLocation = null,
?Visibility $visibility = null,
bool $final = false
) {
$this->fqsen = $fqsen;
$this->docBlock = $docBlock;
$this->value = $value;
$this->location = $location ?: new Location(-1);
$this->endLocation = $endLocation ?: new Location(-1);
$this->visibility = $visibility ?: new Visibility(Visibility::PUBLIC_);
$this->final = $final;

if (is_string($value)) {
trigger_error(
'Constant values should be of type Expression, support for strings will be '
. 'removed in 6.x',
E_USER_DEPRECATED
);
$value = new Expression($value, []);
}

$this->value = $value;
}

/**
* Returns the value of this constant.
* Returns the expression value for this constant.
*
* @return Expression|string|null
*/
public function getValue(): ?string
public function getValue(bool $asString = true)
{
if ($this->value === null) {
return null;
}

if ($asString) {
trigger_error(
'The expression value will become of type Expression by default',
E_USER_DEPRECATED
);

return (string) $this->value;
}

return $this->value;
}

Expand Down
Loading