Skip to content
Merged
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
16 changes: 16 additions & 0 deletions src/ModifiedTypeInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

namespace Microsoft\PhpParser;

/**
* Use the ModifiedTypeTrait for convenience in order to implement this interface.
*/
interface ModifiedTypeInterface {
public function hasModifier(int $targetModifier): bool;
public function isPublic(): bool;
public function isStatic(): bool;
}
46 changes: 46 additions & 0 deletions src/ModifiedTypeTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

namespace Microsoft\PhpParser;

trait ModifiedTypeTrait {
/** @var Token[] */
public $modifiers;

public function hasModifier(int $targetModifier): bool {
if ($this->modifiers === null) {
return false;
}

foreach ($this->modifiers as $modifier) {
if ($modifier->kind === $targetModifier) {
return true;
}
}

return false;
}

/**
* Convenience method to check for the existence of the "public" modifier.
* Does not necessarily need to be defined for that type.
*
* @return bool
*/
public function isPublic(): bool {
return $this->hasModifier(TokenKind::PublicKeyword);
}

/**
* Convenience method to check for the existence of the "static" modifier.
* Does not necessarily need to be defined for that type.
*
* @return bool
*/
public function isStatic(): bool {
return $this->hasModifier(TokenKind::StaticKeyword);
}
}
8 changes: 4 additions & 4 deletions src/Node/ClassConstDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

namespace Microsoft\PhpParser\Node;

use Microsoft\PhpParser\ModifiedTypeInterface;
use Microsoft\PhpParser\ModifiedTypeTrait;
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Token;

class ClassConstDeclaration extends Node {

/** @var Token[] */
public $modifiers;
class ClassConstDeclaration extends Node implements ModifiedTypeInterface {
use ModifiedTypeTrait;

/** @var Token */
public $constKeyword;
Expand Down
92 changes: 70 additions & 22 deletions src/Node/MethodDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@
use Microsoft\PhpParser\DiagnosticKind;
use Microsoft\PhpParser\DiagnosticsProvider;
use Microsoft\PhpParser\FunctionLike;
use Microsoft\PhpParser\ModifiedTypeInterface;
use Microsoft\PhpParser\ModifiedTypeTrait;
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Token;
use Microsoft\PhpParser\TokenKind;

class MethodDeclaration extends Node implements FunctionLike {
/** @var Token[] */
public $modifiers;

use FunctionHeader, FunctionReturnType, FunctionBody;
class MethodDeclaration extends Node implements FunctionLike, ModifiedTypeInterface {
use FunctionHeader, FunctionReturnType, FunctionBody, ModifiedTypeTrait;

const CHILD_NAMES = [
'modifiers',
Expand All @@ -41,23 +40,12 @@ class MethodDeclaration extends Node implements FunctionLike {
'compoundStatementOrSemicolon'
];

public function hasModifier(int $targetModifier) : bool {
if ($this->modifiers === null) {
return false;
}
foreach ($this->modifiers as $modifier) {
if ($modifier->kind === $targetModifier) {
return true;
}
}
return false;
}

public function isStatic() : bool {
return $this->hasModifier(TokenKind::StaticKeyword);
}

public function getName() {
/**
* Returns the name of the method.
*
* @return string
*/
public function getName(): string {
return $this->name->getText($this->getFileContents());
}

Expand All @@ -79,4 +67,64 @@ public function getDiagnosticForNode() {
}
return null;
}

/**
* Returns the signature parts as an array. Use $this::getSignatureFormatted for a user-friendly string version.
*
* @return array
*/
private function getSignatureParts(): array {
$parts = [];

foreach ($this->getChildNodesAndTokens() as $i => $child) {
if ($i === "compoundStatementOrSemicolon") {
return $parts;
}

$parts[] = $child instanceof Token
? $child->getText($this->getFileContents())
: $child->getText();
};

return $parts;
}

/**
* Returns the signature of the method as a formatted string.
*
* @return string
*/
public function getSignatureFormatted(): string {
$signature = implode(" ", $this->getSignatureParts());
return $signature;
}

/**
* Returns the description part of the doc string.
*
* @return string
*/
public function getDescriptionFormatted(): string {
$comment = trim($this->getLeadingCommentAndWhitespaceText(), "\r\n");
$commentParts = explode("\n", $comment);

$description = [];

foreach ($commentParts as $i => $part) {
$part = trim($part, "*\r\t /");

if (strlen($part) <= 0) {
continue;
}

if ($part[0] === "@") {
break;
}

$description[] = $part;
}

$descriptionFormatted = implode(" ", $description);
return $descriptionFormatted;
}
}
8 changes: 4 additions & 4 deletions src/Node/MissingMemberDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

namespace Microsoft\PhpParser\Node;

use Microsoft\PhpParser\ModifiedTypeInterface;
use Microsoft\PhpParser\ModifiedTypeTrait;
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Token;

class MissingMemberDeclaration extends Node {

/** @var Token[] */
public $modifiers;
class MissingMemberDeclaration extends Node implements ModifiedTypeInterface {
use ModifiedTypeTrait;

/** @var Token|null needed along with typeDeclaration for what looked like typed property declarations but was missing VariableName */
public $questionToken;
Expand Down
21 changes: 4 additions & 17 deletions src/Node/PropertyDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@

namespace Microsoft\PhpParser\Node;

use Microsoft\PhpParser\ModifiedTypeInterface;
use Microsoft\PhpParser\ModifiedTypeTrait;
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Token;
use Microsoft\PhpParser\TokenKind;

class PropertyDeclaration extends Node {

/** @var Token[] */
public $modifiers;
class PropertyDeclaration extends Node implements ModifiedTypeInterface {
use ModifiedTypeTrait;

/** @var Token|null question token for PHP 7.4 type declaration */
public $questionToken;
Expand Down Expand Up @@ -41,16 +40,4 @@ class PropertyDeclaration extends Node {
'propertyElements',
'semicolon'
];

public function isStatic() : bool {
if ($this->modifiers === null) {
return false;
}
foreach ($this->modifiers as $modifier) {
if ($modifier->kind === TokenKind::StaticKeyword) {
return true;
}
}
return false;
}
}
9 changes: 5 additions & 4 deletions src/Node/TraitSelectOrAliasClause.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@

namespace Microsoft\PhpParser\Node;

use Microsoft\PhpParser\ModifiedTypeInterface;
use Microsoft\PhpParser\ModifiedTypeTrait;
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Token;

class TraitSelectOrAliasClause extends Node {
class TraitSelectOrAliasClause extends Node implements ModifiedTypeInterface {
use ModifiedTypeTrait;

/** @var QualifiedName|Node\Expression\ScopedPropertyAccessExpression */
public $name;

/** @var Token */
public $asOrInsteadOfKeyword;

/** @var Token[] */
public $modifiers;

/** @var QualifiedName|Node\Expression\ScopedPropertyAccessExpression */
public $targetName;

Expand Down
56 changes: 14 additions & 42 deletions tools/PrintApiDocumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@
*--------------------------------------------------------------------------------------------*/

use Microsoft\PhpParser\Node\MethodDeclaration;
use Microsoft\PhpParser\Node\PropertyDeclaration;
use Microsoft\PhpParser\Node\Statement\ClassDeclaration;
use Microsoft\PhpParser\Parser;
use Microsoft\PhpParser\Token;
use Microsoft\PhpParser\TokenKind;

require_once __DIR__ . "/../src/bootstrap.php";

Expand All @@ -32,41 +29,30 @@

foreach ($files as $file) {
$ast = $parser->parseSourceFile(file_get_contents($file));

foreach ($ast->getDescendantNodes() as $descendant) {
if ($descendant instanceof ClassDeclaration) {
$className = $descendant->name->getText($descendant->getFileContents());
echo "## " . $className . PHP_EOL;

// TODO consider not having a separate classMemberDeclarations node
foreach ($descendant->classMembers->classMemberDeclarations as $member) {
// TODO: Maybe ask a class directly for all its method declarations
if ($member instanceof MethodDeclaration) {
// TODO this should be a helper function on any modified types
foreach ($member->modifiers as $modifier) {
if ($modifier->kind === TokenKind::PublicKeyword) {
$fileContents = $member->getFileContents();
$signature = implode(" ", getSignatureParts($member));
$comment = trim($member->getLeadingCommentAndWhitespaceText(), "\r\n");
if (!$member->isPublic()) {
continue;
}

$commentParts = explode("\n", $comment);
$description = [];
foreach ($commentParts as $i=>$part) {
$part = trim($part, "*\r\t /");
if (isset($part[0])) {
if ($part[0] === "@") {
break;
}
$description[] = $part;
}
}
$comment = implode(" ", $description);
if (strlen(trim($comment, " \t")) === 0) {
$comment = "> TODO: add doc comment\n";
}
echo "### " . $className . "::" . $member->name->getText($member->getFileContents()) . PHP_EOL;
echo $comment . PHP_EOL;
echo "```php\n$signature\n```" . PHP_EOL;
}
$signature = $member->getSignatureFormatted();

$description = $member->getDescriptionFormatted();
if (strlen($description) <= 0) {
$description = "> TODO: add doc comment\n";
}

echo "### " . $className . "::" . $member->getName() . PHP_EOL;
echo $description . PHP_EOL;
echo "```php\n$signature\n```" . PHP_EOL;
}
}
}
Expand All @@ -76,17 +62,3 @@
echo "## Node types
> TODO: complete documentation - in addition to the helper methods on the Node base class,
every Node object has properties specific to the Node type. Browse `src/Node/` to explore these properties.";

function getSignatureParts(MethodDeclaration $methodDeclaration) : array {
// TODO - something like this in API?
$parts = [];
foreach ($methodDeclaration->getChildNodesAndTokens() as $i=>$child) {
if ($i === "compoundStatementOrSemicolon") {
return $parts;
}
$parts[] = $child instanceof Token
? $child->getText($methodDeclaration->getFileContents())
: $child->getText();
};
return $parts;
}