Skip to content

Instantly share code, notes, and snippets.

@mpenick
Last active December 22, 2016 16:00
Show Gist options
  • Select an option

  • Save mpenick/75ca711a3085318ca93524d6c8495528 to your computer and use it in GitHub Desktop.

Select an option

Save mpenick/75ca711a3085318ca93524d6c8495528 to your computer and use it in GitHub Desktop.
<?php
function startsWith($haystack, $needle) {
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
class DocComment {
const UNINITIALIZE = 1;
const INSIDE = 2;
const DONE = 3;
const DOC_COMMENT_START = "/*";
const DOC_COMMENT_END = "*/";
const DOC_COMMENT_INSIDE = "*";
private $comment;
private $returnType;
private $parameters = array();
public function __construct($docComment) {
$this->clean($docComment);
}
public function clean($docComment) {
$lines = preg_split('/\R/', $docComment);
$state = self::UNINITIALIZE;
$comment = array();
foreach ($lines as $line) {
$line = ltrim($line);
if (startsWith($line, self::DOC_COMMENT_START)) {
if ($state != self::UNINITIALIZE) throw new Exception("Invalid comment");
$state = self::INSIDE;
$line = ltrim(substr($line, strlen(self::DOC_COMMENT_START)), "*");
if ($line == "") continue;
} else if (startsWith($line, self::DOC_COMMENT_END)) {
if ($state !=self::INSIDE) throw new Exception("Invalid comment");
$state = self::DONE;
continue;
} else if (startsWith($line, self::DOC_COMMENT_INSIDE)) {
if ($state != self::INSIDE) throw new Exception("Invalid comment");
$line = ltrim(substr($line, strlen(self::DOC_COMMENT_INSIDE)), "*");
}
if (substr($line, 0, 1) == " ") {
$line = substr($line, 1);
}
if (preg_match("/^@return\\s+([\\w_\\-\\\\|]+)\\s?(.*)/", ltrim($line), $matches)) {
if (count($matches) > 1) {
$returnType = $matches[1];
}
if (count($matches) > 2) {
$returnDocComment = $matches[2];
}
$this->returnType = array($returnType, $returnDocComment);
} else if (preg_match("/^@param\\s+([\\w_\\-\\\\|]+)\\s+\\$([\\w_\\-\\\\|]+)\\s*(.*)/", ltrim($line), $matches)) {
if (count($matches) > 1) {
$paramType = $matches[1];
}
if (count($matches) > 2) {
$paramName = $matches[2];
}
if (count($matches) > 3) {
$paramDocComment = $matches[3];
}
$this->parameters[$paramName] = array($paramType, $paramDocComment);
} else {
$comment []= $line;
}
}
if (count($comment) > 0 && $comment[count($comment) - 1] == "") {
array_pop($comment);
}
$this->comment = implode(PHP_EOL, $comment);
}
public function getComment() {
return $this->comment;
}
public function getReturnType() {
return $this->returnType;
}
public function getParameter($name) {
if (!isset($this->parameters[$name])) {
return null;
}
return $this->parameters[$name];
}
public function getParameters() {
return $this->parameters;
}
}
class ConstDocComments {
private $docComments = array();
public function __construct($class) {
$this->parse($class);
}
private function parse($class) {
$content = file_get_contents($class->getFileName());
$tokens = token_get_all($content);
$doc = null;
$isConst = false;
foreach($tokens as $token) {
if (count($token) <= 1) {
continue;
}
list($tokenType, $tokenValue) = $token;
switch ($tokenType) {
case T_WHITESPACE:
case T_COMMENT:
// ignored tokens
break;
case T_DOC_COMMENT:
$doc = $tokenValue;
break;
case T_CONST:
$isConst = true;
break;
case T_STRING:
if ($isConst) {
$docComment = new DocComment($doc);
$this->docComments[$tokenValue] = $docComment->getComment();
}
$doc = null;
$isConst = false;
break;
// all other tokens reset the parser
default:
$doc = null;
$isConst = false;
break;
}
}
}
public function getDocComments() {
return $this->docComments;
}
public function getDocComment($name) {
if (!isset($this->docComments)) {
return null;
}
return $this->docComments[$name];
}
}
if (count($argv) < 2) {
die("Usage: {$argv[0]} <directory>" . PHP_EOL);
}
$dir = $argv[1];
function doesParentHaveMethod($class, $method) {
$parent = $class->getParentClass();
if ($parent) {
if ($parent->hasMethod($method->getName())) {
return true;
}
return doesParentHaveMethod($parent, $method);
}
return false;
}
function writeDocYaml($yamlFileName, $overwrite, $class) {
$classDoc = array();
$classDocComment = new DocComment($class->getDocComment());
$classDoc["comment"] = $classDocComment->getComment();
$methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
if ($methods) {
$methodsDoc = array();
foreach($methods as $method) {
if (doesParentHaveMethod($class, $method) && ($method->isStatic() || $method->isFinal())) {
continue;
}
$methodDoc = array();
$docComment = new DocComment($method->getDocComment());
$methodDoc["comment"] = $docComment->getComment();
$parameters = $method->getParameters();
if ($parameters) {
$paramsDoc = array();
foreach ($parameters as $parameter) {
$paramDocComment = $docComment->getParameter($parameter->getName());
$paramDoc = array();
$paramDoc["comment"] = count($paramDocComment) > 1 ? $paramDocComment[1] : "";
$parameterType = $parameter->getType();
$parameterType = $parameterType ? $parameterType : $paramDocComment[0];
$paramDoc["type"] = $parameterType ? "$parameterType" : "mixed";
$paramsDoc[$parameter->getName()] = $paramDoc;
}
$methodDoc["params"] = $paramsDoc;
}
if (!$method->isConstructor() && !$method->isDestructor()) {
$returnDocComment = $docComment->getReturnType();
$returnDoc = array();
$returnDoc["comment"] = count($returnDocComment) > 1 ? $returnDocComment[1] : "";
$returnType = $returnDocComment[0];
$returnDoc["type"] = $returnType ? "$returnType" : "mixed";
$methodDoc["return"] = $returnDoc;
}
$methodsDoc[$method->getShortName()] = $methodDoc;
}
$classDoc["methods"] = $methodsDoc;
}
$constants = $class->getConstants();
if ($constants) {
$docComments = new ConstDocComments($class);
$constantsDoc = array();
foreach($constants as $name => $notused) {
$constantDoc = array();
$constantDoc["comment"] = $docComments->getDocComment($name);
$constantsDoc[$name] = $constantDoc;
}
$classDoc["constants"] = $constantsDoc;
}
if (!$overwrite && file_exists($yamlFileName)) {
logWarning("$yamlFileName already exists. Not overwriting.");
return;
}
$className = preg_replace("/^\\Cassandra/", "Dse", $class->getName());
yaml_emit_file($yamlFileName, array($className => $classDoc));
}
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
$regex = new RegexIterator($iterator, "/Cassandra.*\.php$/i", RecursiveRegexIterator::GET_MATCH);
//$regex = array("./Cassandra/Type/Scalar.php" => null);
foreach ($regex as $fileName => $notused) {
$fullClassName = preg_replace("/(.+)\.php$/", "$1", $fileName);
$path = dirname($fullClassName);
$className = basename($fullClassName);
$fullClassName = str_replace("/", "\\", ltrim($fullClassName, "."));
if ($fullClassName == "\Cassandra\Float") {
$fullClassName = "\Cassandra\Float_";
} else if ($fullClassName == "\Cassandra\Function") {
$fullClassName = "\Cassandra\Function_";
}
$outputPath = "../src/" . trim(substr($path, strlen("./Cassandra/") - 1), "/");
print("Loading $fileName ($fullClassName --> $outputPath/$className.yaml)...\n");
require_once($fileName);
$classDoc = array();
$class = new ReflectionClass($fullClassName);
writeDocYaml("$outputPath/$className.yaml", true, $class);
#foreach ($class->getMethods() as $method) {
# $docComment = new DocComment($method->getDocComment());
# print_r($method);
# printf("%s", $docComment->getComment());
# print_r($docComment->getReturnType());
# print_r($docComment->getParameters());
#}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment