1: <?php declare(strict_types=1);
2:
3: namespace Salient\PHPDoc;
4:
5: use Salient\Contract\Core\DictionaryInterface;
6:
7: /**
8: * @extends DictionaryInterface<string>
9: */
10: interface PHPDocRegex extends DictionaryInterface
11: {
12: /**
13: * A valid PHP DocBlock, i.e. a DocComment containing a single PHPDoc
14: * structure
15: *
16: * @link https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md#3-definitions
17: */
18: public const PHP_DOCBLOCK = <<<'REGEX'
19: (?x)
20: /\*\* (?= \s )
21: (?<content> (?:
22: (?: (?! \*/ ) . )*+
23: (?: (?<line> (?: \r\n | \n | \r ) \h* ) (?! \*/ ) \* )?
24: )*+ (?&line)? )
25: \*/
26: REGEX;
27:
28: /**
29: * A valid PHPDoc tag
30: *
31: * @link https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md#53-tags
32: */
33: public const PHPDOC_TAG = <<<'REGEX'
34: (?x)
35: (?<= ^ | \r\n | \n | \r )
36: @ (?<tag> [[:alpha:]\\] [[:alnum:]\\_-]*+ ) (?= [\s(:] | $ )
37: REGEX;
38:
39: /**
40: * A valid PHPDoc type
41: *
42: * In some locales, `\s` matches non-breaking space (`\xA0`), so an
43: * alternative (`(?&sp)`) is used in contexts where the start of a PHP
44: * identifier would otherwise be parsed as whitespace.
45: */
46: public const PHPDOC_TYPE = <<<'REGEX'
47: (?xi)
48: (?(DEFINE)
49: (?<sp> [\f\n\r\t\x0b ] )
50: (?<lnum> [0-9]+ (?: _ [0-9]+ )* )
51: (?<dnum> (?: [0-9]* (?: _ [0-9]+ )* \. (?&lnum) ) | (?: (?&lnum) \. [0-9]* (?: _ [0-9]+ )* ) )
52: (?<exponent_dnum> (?: (?&lnum) | (?&dnum) ) e [+-]? (?&lnum) )
53: (?<php_identifier> [[:alpha:]_\x80-\xff] [[:alnum:]_\x80-\xff]* )
54: (?<php_type> \\ (?&php_identifier) (?: \\ (?&php_identifier) )* | (?&php_identifier) (?: \\ (?&php_identifier) )+ )
55: (?<phpdoc_type> (?&php_identifier) (?: - [[:alnum:]_\x80-\xff]+ )+ )
56: (?<variable> \$ (?&php_identifier) )
57: (?<variance> covariant | contravariant )
58: (?<param> \s*+ (?: & \s*+ )? (?: \.\.\. \s*+ )? (?: (?&variable) \s*+ )? =? )
59: (?<trailing> (?: \s*+ , (?: \s*+ \.\.\. (?: \s*+ , )? )? )? \s*+ )
60: )
61: (
62: (?:
63: \* |
64:
65: \$this |
66:
67: # String
68: ' (?: [^'\\]*+ | \\' | \\ )*+ ' |
69: " (?: [^"\\]*+ | \\" | \\ )*+ " |
70:
71: # Number
72: [+-]? (?:
73: (?&exponent_dnum) |
74: (?&dnum) |
75: 0x [0-9a-f]++ (?: _ [0-9a-f]++ )*+ |
76: 0b [01]++ (?: _ [01]++ )*+ |
77: 0o? [0-7]++ (?: _ [0-7]++ )*+ |
78: [1-9] [0-9]*+ (?: _ [0-9]++ )*+ |
79: 0
80: ) |
81:
82: # Closure with optional "<Type,...>", and optional parameter and
83: # return types
84: (?: callable | \\? Closure ) \s*+
85: (?: \s* < (?&sp)*+ (?: (?&variance) (?&sp)*+ )? (?! (?&variance) \b ) (?: (?&php_identifier) \s++ (?: of | as ) \b (?&sp)*+ )? (?-1)
86: (?: \s*+ , (?&sp)*+ (?: (?&variance) (?&sp)*+ )? (?! (?&variance) \b ) (?: (?&php_identifier) \s++ (?: of | as ) \b (?&sp)*+ )? (?-1) )*+
87: (?&trailing) > )?
88: \( (?&sp)*+ (?: (?-1) (?&param)
89: (?: \s*+ , (?&sp)*+ (?-1) (?&param) )*+
90: (?&trailing) | \.\.\. \s*+ )? \)
91: (?: \s* : (?&sp)*+ (?-1) )? |
92:
93: # Native or PHPDoc type, possibly nullable, with optional
94: # "::CONST_*", "<Type,...>", and/or "{0?:Type,...}"
95: (?: \? (?&sp)*+ )?
96: (?: (?&php_type) | (?&phpdoc_type) | (?&php_identifier) )
97: (?: :: [[:alpha:]_\x80-\xff*] [[:alnum:]_\x80-\xff*]*+ )?
98: (?: \s* < (?&sp)*+ (?: (?&variance) (?&sp)*+ )? (?! (?&variance) \b ) (?-1)
99: (?: \s*+ , (?&sp)*+ (?: (?&variance) (?&sp)*+ )? (?! (?&variance) \b ) (?-1) )*+
100: (?&trailing) > )?
101: (?: \s* \{ (?&sp)*+ (?:
102: (?: (?-1) \s*+ (?: \? \s*+ )? : (?&sp)*+ )? (?-1)
103: (?: \s*+ , (?&sp)*+ (?: (?-1) \s*+ (?: \? \s*+ )? : (?&sp)*+ )? (?-1) )*+
104: (?&trailing) | \.\.\. \s*+ )? \} )*+ |
105:
106: # Conditional return type
107: (?: (?&variable) | (?&php_identifier) ) \s+ is (?: \s++ not )? \b (?&sp)*+ (?-1)
108: \s*+ \? (?&sp)*+ (?-1)
109: \s*+ : (?&sp)*+ (?-1) |
110:
111: # Enclosing parentheses
112: (?: \? \s*+ )? \( (?&sp)*+ (?-1) \s*+ \)
113: )
114: (?: \s* \[ (?&sp)*+ (?: (?-1) \s*+ )? \] )*+
115: (?: \s* (?: \| | & ) (?&sp)* (?-1) )?
116: )
117: REGEX;
118: }
119: