|
5 | 5 | namespace phpDocumentor\Reflection\Php;
|
6 | 6 |
|
7 | 7 | use phpDocumentor\Reflection\Fqsen;
|
| 8 | +use phpDocumentor\Reflection\Php\Expression\ExpressionPrinter; |
8 | 9 | use phpDocumentor\Reflection\Type;
|
| 10 | +use Webmozart\Assert\Assert; |
9 | 11 |
|
10 | 12 | use function array_keys;
|
11 |
| -use function array_map; |
| 13 | +use function md5; |
12 | 14 | use function str_replace;
|
13 | 15 |
|
| 16 | +/** |
| 17 | + * Represents an expression with a define statement, constant, property, enum case and any other location. |
| 18 | + * |
| 19 | + * Certain expressions contain useful references to other elements or types. Examples of these are: |
| 20 | + * |
| 21 | + * - Define statements that use an expression to refer to a class or function |
| 22 | + * - Properties whose default value refers to a constant |
| 23 | + * - Arguments whose default value initialize an object |
| 24 | + * - Enum Cases that refer to a function or constant |
| 25 | + * |
| 26 | + * This class represents every location where an expression is used and contains 2 pieces of information: |
| 27 | + * |
| 28 | + * - The expression string containing placeholders linking to useful information |
| 29 | + * - An array of 'parts' whose keys equal the placeholders in the expression string and whose values is the extracted |
| 30 | + * information, such as an {@see FQSEN} or {@see Type}. |
| 31 | + * |
| 32 | + * In a way, the expression string is similar in nature to a URI Template (see links) where you have a string containing |
| 33 | + * variables that can be replaced. These variables are delimited by `{{` and `}}`, and are build up of the prefix PHPDOC |
| 34 | + * and then an MD5 hash of the name of the extracted information. |
| 35 | + * |
| 36 | + * It is not necessary for a consumer to interpret the information when they do not need it, a {@see self::__toString()} |
| 37 | + * magic method is provided that will replace the placeholders with the `toString()` output of each part. |
| 38 | + * |
| 39 | + * @link https://github.com/php/php-langspec/blob/master/spec/10-expressions.md |
| 40 | + * for the definition of expressions in PHP. |
| 41 | + * @link https://www.rfc-editor.org/rfc/rfc6570 for more information on URI Templates. |
| 42 | + * @see ExpressionPrinter how an expression coming from PHP-Parser is transformed into an expression. |
| 43 | + */ |
14 | 44 | final class Expression
|
15 | 45 | {
|
| 46 | + /** @var string The expression string containing placeholders for any extracted Types or FQSENs. */ |
16 | 47 | private string $expression;
|
17 | 48 |
|
18 |
| - /** @var array<string, Fqsen|Type> */ |
| 49 | + /** |
| 50 | + * The collection of placeholders with the value that their holding. |
| 51 | + * |
| 52 | + * In the expression string there can be several placeholders, this array contains a placeholder => value pair |
| 53 | + * that can be used by consumers to map the data to another formatting, adding links for example, and then render |
| 54 | + * the expression. |
| 55 | + * |
| 56 | + * @var array<string, Fqsen|Type> |
| 57 | + */ |
19 | 58 | private array $parts;
|
20 | 59 |
|
| 60 | + /** |
| 61 | + * Returns the recommended placeholder string format given a name. |
| 62 | + * |
| 63 | + * Consumers can use their own formats when needed, the placeholders are all keys in the {@see self::$parts} array |
| 64 | + * and not interpreted by this class. However, to prevent collisions it is recommended to use this method to |
| 65 | + * generate a placeholder. |
| 66 | + * |
| 67 | + * @param string $name a string identifying the element for which the placeholder is generated. |
| 68 | + */ |
| 69 | + public static function generatePlaceholder(string $name): string |
| 70 | + { |
| 71 | + Assert::notEmpty($name); |
| 72 | + |
| 73 | + return '{{ PHPDOC' . md5($name) . ' }}'; |
| 74 | + } |
| 75 | + |
21 | 76 | /**
|
22 | 77 | * @param array<string, Fqsen|Type> $parts
|
23 | 78 | */
|
24 | 79 | public function __construct(string $expression, array $parts)
|
25 | 80 | {
|
| 81 | + Assert::notEmpty($expression); |
| 82 | + Assert::allIsInstanceOfAny($parts, [Fqsen::class, Type::class]); |
| 83 | + |
26 | 84 | $this->expression = $expression;
|
27 | 85 | $this->parts = $parts;
|
28 | 86 | }
|
29 | 87 |
|
| 88 | + /** |
| 89 | + * The raw expression string containing placeholders for any extracted Types or FQSENs. |
| 90 | + * |
| 91 | + * @see self::render() to render a human-readable expression and to replace some parts with custom values. |
| 92 | + * @see self::__toString() to render a human-readable expression with the previously extracted parts. |
| 93 | + */ |
30 | 94 | public function getExpression(): string
|
31 | 95 | {
|
32 | 96 | return $this->expression;
|
33 | 97 | }
|
34 | 98 |
|
35 | 99 | /**
|
| 100 | + * A list of extracted parts for which placeholders exist in the expression string. |
| 101 | + * |
| 102 | + * The returned array will have the placeholders of the expression string as keys, and the related FQSEN or Type as |
| 103 | + * value. This can be used as a basis for doing your own transformations to {@see self::render()} the expression |
| 104 | + * in a custom way; or to extract type information from an expression and use that elsewhere in your application. |
| 105 | + * |
| 106 | + * @see ExpressionPrinter to transform a PHP-Parser expression into an expression string and list of parts. |
| 107 | + * |
36 | 108 | * @return array<string, Fqsen|Type>
|
37 | 109 | */
|
38 | 110 | public function getParts(): array
|
39 | 111 | {
|
40 | 112 | return $this->parts;
|
41 | 113 | }
|
42 | 114 |
|
43 |
| - public function __toString(): string |
| 115 | + /** |
| 116 | + * Renders the expression as a string and replaces all placeholders with either a provided value, or the |
| 117 | + * stringified value from the parts in this expression. |
| 118 | + * |
| 119 | + * The keys of the replacement parts should match those of {@see self::getParts()}, any unrecognized key is not |
| 120 | + * handled. |
| 121 | + * |
| 122 | + * @param array<string, string> $replacementParts |
| 123 | + */ |
| 124 | + public function render(array $replacementParts = []): string |
44 | 125 | {
|
45 |
| - $valuesAsStrings = array_map( |
46 |
| - static fn (object $part): string => (string) $part, |
47 |
| - $this->parts |
48 |
| - ); |
| 126 | + Assert::allStringNotEmpty($replacementParts); |
| 127 | + |
| 128 | + $valuesAsStrings = []; |
| 129 | + foreach ($this->parts as $placeholder => $part) { |
| 130 | + $valuesAsStrings[$placeholder] = $replacementParts[$placeholder] ?? (string) $part; |
| 131 | + } |
49 | 132 |
|
50 | 133 | return str_replace(array_keys($this->parts), $valuesAsStrings, $this->expression);
|
51 | 134 | }
|
| 135 | + |
| 136 | + /** |
| 137 | + * Returns a rendered version of the expression string where all placeholders are replaced by the stringified |
| 138 | + * versions of the included parts. |
| 139 | + * |
| 140 | + * @see self::$parts for the list of parts used in rendering |
| 141 | + * @see self::render() to influence rendering of the expression. |
| 142 | + */ |
| 143 | + public function __toString(): string |
| 144 | + { |
| 145 | + return $this->render(); |
| 146 | + } |
52 | 147 | }
|
0 commit comments