You can simply scan a string, a file or a full directory and you can see a simple data structure from your php code.
- Classes (PHPClass)
- Properties (PHPProperty)
- Constants (PHPConst)
- Methods (PHPMethod)
- Interfaces (PHPInterface)
- Traits (PHPTrait)
- Enums (PHPEnum)
- Functions (PHPFunction)
- Parameter (PHPParameter)
- Attributes (PHPAttribute)
This code is forked from JetBrains/phpstorm-stubs but you can't use the classes from "phpstorm-stubs" directly, because they are in a test namespace and the autoloader is "autoload-dev", so here is a extended version.
We will use:
- PHP >= 8.1
Runtime support for this library is PHP 8.1+, while the parser test suite validates analyzable source syntax from PHP 5.3 through PHP 8.5.
| Source Syntax Generation | Representative coverage |
|---|---|
| PHP 5.3 | namespaces, closures with use, array() syntax |
| PHP 5.4 | traits, callable, short arrays |
| PHP 5.5 | generators / yield, finally |
| PHP 5.6 | variadics, argument unpacking-ready syntax, constant arrays |
| PHP 7.0 | scalar parameter types, return types, anonymous classes |
| PHP 7.1 | nullable types, iterable, void |
| PHP 7.2 | object type |
| PHP 7.3 | trailing commas in calls |
| PHP 7.4 | typed properties, arrow functions |
| Feature | PHP Version | Supported |
|---|---|---|
| Attributes (class, method, property, parameter, constant) | 8.0+ | ✅ |
| Constructor property promotion | 8.0+ | ✅ |
| Union types | 8.0+ | ✅ |
| Named arguments | 8.0+ | ✅ |
| Match expressions | 8.0+ | ✅ |
| Nullsafe operator | 8.0+ | ✅ |
| Enums (unit, string-backed, int-backed) | 8.1+ | ✅ |
| Readonly properties | 8.1+ | ✅ |
| Intersection types | 8.1+ | ✅ |
never return type |
8.1+ | ✅ |
| First-class callable syntax | 8.1+ | ✅ |
| Readonly classes | 8.2+ | ✅ |
| DNF types | 8.2+ | ✅ |
Standalone true, false, null types |
8.2+ | ✅ |
| Trait constants | 8.2+ | ✅ |
| Typed class constants | 8.3+ | ✅ |
#[\Override] attribute detection |
8.3+ | ✅ |
| Property hooks / asymmetric visibility | 8.4+ | ✅ |
composer require voku/simple-php-code-parserParse a string:
$code = '
<?php
namespace voku\tests;
class SimpleClass {}
$obja = new class() {};
$objb = new class {};
class AnotherClass {}
';
$phpCode = \voku\SimplePhpParser\Parsers\PhpCodeParser::getFromString($code);
$phpClasses = $phpCode->getClasses();
var_dump($phpClasses['voku\tests\SimpleClass']); // "PHPClass"-objectFor new code, prefer the explicit input-mode entry points:
getFromString()for raw PHP source,getFromFile()for one existing file,getFromDirectory()for one existing directory.
getPhpFiles() remains available for backwards compatibility and keeps its legacy mixed-input behavior: existing files/directories are treated as paths, while every other string is interpreted as raw PHP source. A misspelled or missing path therefore does not fail fast when passed to getPhpFiles().
Parse one class:
$phpCode = \voku\SimplePhpParser\Parsers\PhpCodeParser::getFromClassName(Dummy::class);
$phpClasses = $phpCode->getClasses();
var_dump($phpClasses[Dummy::class]); // "PHPClass"-object
var_dump($phpClasses[Dummy::class]->methods); // "PHPMethod[]"-objects
var_dump($phpClasses[Dummy::class]->methods['withoutPhpDocParam']); // "PHPMethod"-object
var_dump($phpClasses[Dummy::class]->methods['withoutPhpDocParam']->parameters); // "PHPParameter[]"-objects
var_dump($phpClasses[Dummy::class]->methods['withoutPhpDocParam']->parameters['useRandInt']); // "PHPParameter"-object
var_dump($phpClasses[Dummy::class]->methods['withoutPhpDocParam']->parameters['useRandInt']->type); // "bool"Parse one file:
$phpCode = \voku\SimplePhpParser\Parsers\PhpCodeParser::getFromFile(__DIR__ . '/Dummy.php');
$phpClasses = $phpCode->getClasses();
var_dump($phpClasses[Dummy::class]); // "PHPClass"-object
var_dump($phpClasses[Dummy::class]->methods); // "PHPMethod[]"-objects
var_dump($phpClasses[Dummy::class]->methods['withoutPhpDocParam']); // "PHPMethod"-object
var_dump($phpClasses[Dummy::class]->methods['withoutPhpDocParam']->parameters); // "PHPParameter[]"-objects
var_dump($phpClasses[Dummy::class]->methods['withoutPhpDocParam']->parameters['useRandInt']); // "PHPParameter"-object
var_dump($phpClasses[Dummy::class]->methods['withoutPhpDocParam']->parameters['useRandInt']->type); // "bool"Parse many files:
$phpCode = \voku\SimplePhpParser\Parsers\PhpCodeParser::getFromDirectory(__DIR__ . '/src');
$phpClasses = $phpCode->getClasses();
var_dump($phpClasses[Dummy::class]); // "PHPClass"-objectFor larger AST-only directory scans on CLI, bounded process parallelism is opt-in:
use voku\SimplePhpParser\Parsers\Helper\ParserOptions;
use voku\SimplePhpParser\Parsers\PhpCodeParser;
$phpCode = PhpCodeParser::getPhpFiles(
__DIR__ . '/src',
options: ParserOptions::astOnlyParallel()
);The parallel path is only used when at least 16 files are parsed, pcntl is available, and more than one CPU core is detected. Otherwise the parser uses the same sequential AST-only path. Reflection-enriched parsing is never forked.
Unified metadata API:
$phpCode = \voku\SimplePhpParser\Parsers\PhpCodeParser::getFromDirectory(__DIR__ . '/src');
$phpClasses = $phpCode->getClasses();
$phpInterfaces = $phpCode->getInterfaces();
$phpTraits = $phpCode->getTraits();
$phpEnums = $phpCode->getEnums();
$phpFunctions = $phpCode->getFunctions();
$phpConstants = $phpCode->getConstants();
$functionInfo = $phpCode->getFunctionsInfo();
$methodInfo = $phpClasses[MyService::class]->getMethodsInfo();
$propertyInfo = $phpClasses[MyService::class]->getPropertiesInfo();Every parsed model element exposes its inclusive source range. This lets an
indexer store a compact symbol map and later read exactly one declaration with
sed (or a byte-range read), instead of sending a whole file to a coding
agent.
$phpCode = \voku\SimplePhpParser\Parsers\PhpCodeParser::getPhpFiles(__DIR__ . '/src/MyService.php');
$method = $phpCode->getClasses()[MyService::class]->methods['handle'];
// 1-based, inclusive line range for: sed -n "{$method->line},{$method->endLine}p" …
echo $method->line;
echo $method->endLine;
// 0-based, inclusive byte range in the parsed source file.
echo $method->startFilePos;
echo $method->endFilePos;line, endLine, startFilePos and endFilePos are the range of the php-parser
node: it includes PHP attributes but not the PHPDoc comment in front of the
declaration. Consumers that edit or delete a declaration use the owned range instead:
// The declaration plus its PHPDoc comment (and attributes), e.g. for deleting it.
echo $method->sourceStartLine;
echo $method->sourceEndLine;
echo $method->sourceStartFilePos;
echo $method->sourceEndFilePos;Consumers that work on the raw php-parser AST get the same range from
\voku\SimplePhpParser\Parsers\Helper\AstNodeInspector::ownedRange($node), which
returns startLine, endLine, startFilePos and endFilePos, or null when the node
carries no source positions.
The owned range of a model is null for reflection-backed models and for items that do not
exclusively own a declaration: const A = 1, B = 2;, public $a, $b; and define()
calls. Whether removing a declaration is semantically safe is not decided here.
Class-like models also expose directly composed traits as fully-qualified
names in $traitUses, plus alias and insteadof rules in
$traitAdaptations. Interfaces retain every declared parent interface.
For syntax that deliberately remains outside the compact Model API, use the same names-resolved php-parser AST that builds it:
$ast = \voku\SimplePhpParser\Parsers\PhpCodeParser::getAstFromFile(__DIR__ . '/src/MyService.php');
// or: PhpCodeParser::getAstFromString($source);The returned nodes have source-location attributes and parent references.
Resolvable names are fully-qualified; their original aliases remain available
through php-parser's originalName node attribute. Parent references make the
AST cyclic, so it should be inspected rather than serialized directly.
For an even smaller file-header summary, use PHPFileInfo:
$fileInfo = \voku\SimplePhpParser\Parsers\PhpCodeParser::getFileInfoFromFile(__DIR__ . '/src/MyService.php');
// Each namespace scope contains imports (class/function/const aliases) and
// declare values such as strict_types, with their source lines.
foreach ($fileInfo->namespaces as $namespace) {
var_dump($namespace['name'], $namespace['imports'], $namespace['declares']);
}Import-aware PHPDoc fields preserve the old rendered fields and add precise
typeFromPhpDocResolved / returnTypeFromPhpDocResolved values. Thus
use Vendor\Payload as Message; can resolve @param Message $message to
\Vendor\Payload without changing legacy output. Function and method models
also expose documented exception types in $throws, is_returned_by_ref, and
(for methods) is_abstract; parameters expose is_promoted. PHPEnum keeps
its legacy $cases value map and additionally exposes source-aware,
attribute-aware case objects in $caseDetails.
The library is meant to be the simple integration layer that other tools can call instead of wiring together nikic/php-parser, phpstan/phpdoc-parser, phpDocumentor, and native reflection themselves. The test suite validates analyzable PHP source from PHP 5.3 through PHP 8.5, including legacy syntax generations and modern type declarations / metadata such as attributes, enums, readonly constructs, typed constants, and property hooks.
Access enums:
$phpCode = \voku\SimplePhpParser\Parsers\PhpCodeParser::getFromDirectory(__DIR__ . '/src');
$phpEnums = $phpCode->getEnums();
// PHPEnum objects with scalarType, cases, methods, constants, attributesAccess attributes:
$phpCode = \voku\SimplePhpParser\Parsers\PhpCodeParser::getFromDirectory(__DIR__ . '/src');
$phpClasses = $phpCode->getClasses();
$class = $phpClasses['MyClass'];
// Class-level attributes
foreach ($class->attributes as $attr) {
echo $attr->name; // e.g. "MyAttribute"
print_r($attr->arguments); // constructor arguments (array)
}
// Method/property/parameter attributes work the same way
foreach ($class->methods['myMethod']->attributes as $attr) { ... }
foreach ($class->properties['myProp']->attributes as $attr) { ... }
foreach ($class->methods['myMethod']->parameters['param']->attributes as $attr) { ... }For support and donations please visit GitHub | Issues | PayPal | Patreon.
For status updates and release announcements please visit Releases | Patreon.
For professional support please contact me.
- Thanks to GitHub (Microsoft) for hosting the code and a good infrastructure including Issues-Management, etc.
- Thanks to IntelliJ as they make the best IDEs for PHP and they gave me an open source license for PhpStorm!
- Thanks to PHPStan && Psalm for really great Static analysis tools and for discover bugs in the code!