mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
[skip ci] gen_stub: Generate useful methodsynopsises for new functions/methods (#12330)
Co-authored-by: Máté Kocsis <kocsismate@woohoolabs.com>
This commit is contained in:
committed by
GitHub
parent
2782577c4f
commit
5d98069456
@@ -1060,7 +1060,7 @@ class FunctionName implements FunctionOrMethodName {
|
||||
}
|
||||
|
||||
public function getMethodSynopsisFilename(): string {
|
||||
return implode('_', $this->name->getParts());
|
||||
return 'functions/' . implode('/', str_replace('_', '-', $this->name->getParts()));
|
||||
}
|
||||
|
||||
public function getNameForAttributes(): string {
|
||||
@@ -1105,8 +1105,11 @@ class MethodName implements FunctionOrMethodName {
|
||||
return "arginfo_class_{$this->getDeclarationClassName()}_{$this->methodName}";
|
||||
}
|
||||
|
||||
public function getMethodSynopsisFilename(): string {
|
||||
return $this->getDeclarationClassName() . "_{$this->methodName}";
|
||||
public function getMethodSynopsisFilename(): string
|
||||
{
|
||||
$parts = [...$this->className->getParts(), ltrim($this->methodName, '_')];
|
||||
/* File paths are in lowercase */
|
||||
return strtolower(implode('/', $parts));
|
||||
}
|
||||
|
||||
public function getNameForAttributes(): string {
|
||||
@@ -1480,25 +1483,447 @@ class FuncInfo {
|
||||
return $flags;
|
||||
}
|
||||
|
||||
private function generateRefSect1(DOMDocument $doc, string $role): DOMElement {
|
||||
$refSec = $doc->createElement('refsect1');
|
||||
$refSec->setAttribute('role', $role);
|
||||
$refSec->append(
|
||||
"\n ",
|
||||
$doc->createEntityReference('reftitle.' . $role),
|
||||
"\n "
|
||||
);
|
||||
return $refSec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, FuncInfo> $funcMap
|
||||
* @param array<string, FuncInfo> $aliasMap
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getMethodSynopsisDocument(array $funcMap, array $aliasMap): ?string {
|
||||
$REFSEC1_SEPERATOR = "\n\n ";
|
||||
|
||||
$doc = new DOMDocument();
|
||||
$doc = new DOMDocument("1.0", "utf-8");
|
||||
$doc->formatOutput = true;
|
||||
|
||||
$refentry = $doc->createElement('refentry');
|
||||
$doc->appendChild($refentry);
|
||||
|
||||
if ($this->isMethod()) {
|
||||
assert($this->name instanceof MethodName);
|
||||
/* Namespaces are seperated by '-', '_' must be converted to '-' too.
|
||||
* Trim away the __ for magic methods */
|
||||
$id = strtolower(
|
||||
str_replace('\\', '-', $this->name->className->__toString())
|
||||
. '.'
|
||||
. str_replace('_', '-', ltrim($this->name->methodName, '_'))
|
||||
);
|
||||
} else {
|
||||
$id = 'function.' . strtolower(str_replace('_', '-', $this->name->__toString()));
|
||||
}
|
||||
$refentry->setAttribute("xml:id", $id);
|
||||
/* We create an attribute for xmlns, as libxml otherwise force it to be the first one */
|
||||
//$refentry->setAttribute("xmlns", "http://docbook.org/ns/docbook");
|
||||
$namespace = $doc->createAttribute('xmlns');
|
||||
$namespace->value = "http://docbook.org/ns/docbook";
|
||||
$refentry->setAttributeNode($namespace);
|
||||
$refentry->setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
||||
$refentry->appendChild(new DOMText("\n "));
|
||||
|
||||
/* Creation of <refnamediv> */
|
||||
$refnamediv = $doc->createElement('refnamediv');
|
||||
$refnamediv->appendChild(new DOMText("\n "));
|
||||
$refname = $doc->createElement('refname', $this->name->__toString());
|
||||
$refnamediv->appendChild($refname);
|
||||
$refnamediv->appendChild(new DOMText("\n "));
|
||||
$refpurpose = $doc->createElement('refpurpose', 'Description');
|
||||
$refnamediv->appendChild($refpurpose);
|
||||
|
||||
$refnamediv->appendChild(new DOMText("\n "));
|
||||
$refentry->append($refnamediv, $REFSEC1_SEPERATOR);
|
||||
|
||||
/* Creation of <refsect1 role="description"> */
|
||||
$descriptionRefSec = $this->generateRefSect1($doc, 'description');
|
||||
|
||||
$methodSynopsis = $this->getMethodSynopsisElement($funcMap, $aliasMap, $doc);
|
||||
if (!$methodSynopsis) {
|
||||
return null;
|
||||
}
|
||||
$descriptionRefSec->appendChild($methodSynopsis);
|
||||
$descriptionRefSec->appendChild(new DOMText("\n "));
|
||||
$undocumentedEntity = $doc->createEntityReference('warn.undocumented.func');
|
||||
$descriptionRefSec->appendChild($undocumentedEntity);
|
||||
$descriptionRefSec->appendChild(new DOMText("\n "));
|
||||
$returnDescriptionPara = $doc->createElement('para');
|
||||
$returnDescriptionPara->appendChild(new DOMText("\n Description.\n "));
|
||||
$descriptionRefSec->appendChild($returnDescriptionPara);
|
||||
|
||||
$doc->appendChild($methodSynopsis);
|
||||
$descriptionRefSec->appendChild(new DOMText("\n "));
|
||||
$refentry->append($descriptionRefSec, $REFSEC1_SEPERATOR);
|
||||
|
||||
/* Creation of <refsect1 role="parameters"> */
|
||||
$parametersRefSec = $this->getParameterSection($doc);
|
||||
$refentry->append($parametersRefSec, $REFSEC1_SEPERATOR);
|
||||
|
||||
/* Creation of <refsect1 role="returnvalues"> */
|
||||
if (!$this->name->isConstructor() && !$this->name->isDestructor()) {
|
||||
$returnRefSec = $this->getReturnValueSection($doc);
|
||||
$refentry->append($returnRefSec, $REFSEC1_SEPERATOR);
|
||||
}
|
||||
|
||||
/* Creation of <refsect1 role="errors"> */
|
||||
$errorsRefSec = $this->generateRefSect1($doc, 'errors');
|
||||
$errorsDescriptionParaConstantTag = $doc->createElement('constant');
|
||||
$errorsDescriptionParaConstantTag->append('E_*');
|
||||
$errorsDescriptionParaExceptionTag = $doc->createElement('exceptionname');
|
||||
$errorsDescriptionParaExceptionTag->append('Exception');
|
||||
$errorsDescriptionPara = $doc->createElement('para');
|
||||
$errorsDescriptionPara->append(
|
||||
"\n When does this function issue ",
|
||||
$errorsDescriptionParaConstantTag,
|
||||
" level errors,\n and/or throw ",
|
||||
$errorsDescriptionParaExceptionTag,
|
||||
"s.\n "
|
||||
);
|
||||
$errorsRefSec->appendChild($errorsDescriptionPara);
|
||||
$errorsRefSec->appendChild(new DOMText("\n "));
|
||||
|
||||
$refentry->append($errorsRefSec, $REFSEC1_SEPERATOR);
|
||||
|
||||
/* Creation of <refsect1 role="changelog"> */
|
||||
$changelogRefSec = $this->getChangelogSection($doc);
|
||||
$refentry->append($changelogRefSec, $REFSEC1_SEPERATOR);
|
||||
|
||||
$exampleRefSec = $this->getExampleSection($doc, $id);
|
||||
$refentry->append($exampleRefSec, $REFSEC1_SEPERATOR);
|
||||
|
||||
/* Creation of <refsect1 role="notes"> */
|
||||
$notesRefSec = $this->generateRefSect1($doc, 'notes');
|
||||
|
||||
$noteTagSimara = $doc->createElement('simpara');
|
||||
$noteTagSimara->append(
|
||||
"\n Any notes that don't fit anywhere else should go here.\n "
|
||||
);
|
||||
$noteTag = $doc->createElement('note');
|
||||
$noteTag->append("\n ", $noteTagSimara, "\n ");
|
||||
$notesRefSec->append($noteTag, "\n ");
|
||||
|
||||
$refentry->append($notesRefSec, $REFSEC1_SEPERATOR);
|
||||
|
||||
/* Creation of <refsect1 role="seealso"> */
|
||||
$seeAlsoRefSec = $this->generateRefSect1($doc, 'seealso');
|
||||
|
||||
$seeAlsoMemberClassMethod = $doc->createElement('member');
|
||||
$seeAlsoMemberClassMethodTag = $doc->createElement('methodname');
|
||||
$seeAlsoMemberClassMethodTag->appendChild(new DOMText("ClassName::otherMethodName"));
|
||||
$seeAlsoMemberClassMethod->appendChild($seeAlsoMemberClassMethodTag);
|
||||
|
||||
$seeAlsoMemberFunction = $doc->createElement('member');
|
||||
$seeAlsoMemberFunctionTag = $doc->createElement('function');
|
||||
$seeAlsoMemberFunctionTag->appendChild(new DOMText("some_function"));
|
||||
$seeAlsoMemberFunction->appendChild($seeAlsoMemberFunctionTag);
|
||||
|
||||
$seeAlsoMemberLink = $doc->createElement('member');
|
||||
$seeAlsoMemberLinkTag = $doc->createElement('link');
|
||||
$seeAlsoMemberLinkTag->setAttribute('linkend', 'some.id.chunk.to.link');
|
||||
$seeAlsoMemberLinkTag->appendChild(new DOMText('something appendix'));
|
||||
$seeAlsoMemberLink->appendChild($seeAlsoMemberLinkTag);
|
||||
|
||||
$seeAlsoList = $doc->createElement('simplelist');
|
||||
$seeAlsoList->append(
|
||||
"\n ",
|
||||
$seeAlsoMemberClassMethod,
|
||||
"\n ",
|
||||
$seeAlsoMemberFunction,
|
||||
"\n ",
|
||||
$seeAlsoMemberLink,
|
||||
"\n "
|
||||
);
|
||||
|
||||
$seeAlsoRefSec->appendChild($seeAlsoList);
|
||||
$seeAlsoRefSec->appendChild(new DOMText("\n "));
|
||||
|
||||
$refentry->appendChild($seeAlsoRefSec);
|
||||
|
||||
$refentry->appendChild(new DOMText("\n\n"));
|
||||
|
||||
$doc->appendChild(new DOMComment(
|
||||
<<<ENDCOMMENT
|
||||
Keep this comment at the end of the file
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-omittag:t
|
||||
sgml-shorttag:t
|
||||
sgml-minimize-attributes:nil
|
||||
sgml-always-quote-attributes:t
|
||||
sgml-indent-step:1
|
||||
sgml-indent-data:t
|
||||
indent-tabs-mode:nil
|
||||
sgml-parent-document:nil
|
||||
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
||||
sgml-exposed-tags:nil
|
||||
sgml-local-catalogs:nil
|
||||
sgml-local-ecat-files:nil
|
||||
End:
|
||||
vim600: syn=xml fen fdm=syntax fdl=2 si
|
||||
vim: et tw=78 syn=sgml
|
||||
vi: ts=1 sw=1
|
||||
|
||||
ENDCOMMENT
|
||||
));
|
||||
return $doc->saveXML();
|
||||
}
|
||||
|
||||
private function getParameterSection(DOMDocument $doc): DOMElement {
|
||||
$parametersRefSec = $this->generateRefSect1($doc, 'parameters');
|
||||
if (empty($this->args)) {
|
||||
$noParamEntity = $doc->createEntityReference('no.function.parameters');
|
||||
$parametersRefSec->appendChild($noParamEntity);
|
||||
return $parametersRefSec;
|
||||
} else {
|
||||
$parametersPara = $doc->createElement('para');
|
||||
$parametersRefSec->appendChild($parametersPara);
|
||||
|
||||
$parametersPara->appendChild(new DOMText("\n "));
|
||||
$parametersList = $doc->createElement('variablelist');
|
||||
$parametersPara->appendChild($parametersList);
|
||||
|
||||
/*
|
||||
<varlistentry>
|
||||
<term><parameter>name</parameter></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Description.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
*/
|
||||
foreach ($this->args as $arg) {
|
||||
$parameter = $doc->createElement('parameter', $arg->name);
|
||||
$parameterTerm = $doc->createElement('term');
|
||||
$parameterTerm->appendChild($parameter);
|
||||
|
||||
$listItemPara = $doc->createElement('para');
|
||||
$listItemPara->append(
|
||||
"\n ",
|
||||
"Description.",
|
||||
"\n ",
|
||||
);
|
||||
|
||||
$parameterEntryListItem = $doc->createElement('listitem');
|
||||
$parameterEntryListItem->append(
|
||||
"\n ",
|
||||
$listItemPara,
|
||||
"\n ",
|
||||
);
|
||||
|
||||
$parameterEntry = $doc->createElement('varlistentry');
|
||||
$parameterEntry->append(
|
||||
"\n ",
|
||||
$parameterTerm,
|
||||
"\n ",
|
||||
$parameterEntryListItem,
|
||||
"\n ",
|
||||
);
|
||||
|
||||
$parametersList->appendChild(new DOMText("\n "));
|
||||
$parametersList->appendChild($parameterEntry);
|
||||
}
|
||||
$parametersList->appendChild(new DOMText("\n "));
|
||||
}
|
||||
$parametersPara->appendChild(new DOMText("\n "));
|
||||
$parametersRefSec->appendChild(new DOMText("\n "));
|
||||
return $parametersRefSec;
|
||||
}
|
||||
|
||||
private function getReturnValueSection(DOMDocument $doc): DOMElement {
|
||||
$returnRefSec = $this->generateRefSect1($doc, 'returnvalues');
|
||||
|
||||
$returnDescriptionPara = $doc->createElement('para');
|
||||
$returnDescriptionPara->appendChild(new DOMText("\n "));
|
||||
|
||||
$returnType = $this->return->getMethodSynopsisType();
|
||||
if ($returnType === null) {
|
||||
$returnDescriptionPara->appendChild(new DOMText("Description."));
|
||||
} else if (count($returnType->types) === 1) {
|
||||
$type = $returnType->types[0];
|
||||
$name = $type->name;
|
||||
|
||||
switch ($name) {
|
||||
case 'void':
|
||||
$descriptionNode = $doc->createEntityReference('return.void');
|
||||
break;
|
||||
case 'true':
|
||||
$descriptionNode = $doc->createEntityReference('return.true.always');
|
||||
break;
|
||||
case 'bool':
|
||||
$descriptionNode = $doc->createEntityReference('return.success');
|
||||
break;
|
||||
default:
|
||||
$descriptionNode = new DOMText("Description.");
|
||||
break;
|
||||
}
|
||||
$returnDescriptionPara->appendChild($descriptionNode);
|
||||
} else {
|
||||
$returnDescriptionPara->appendChild(new DOMText("Description."));
|
||||
}
|
||||
$returnDescriptionPara->appendChild(new DOMText("\n "));
|
||||
$returnRefSec->appendChild($returnDescriptionPara);
|
||||
$returnRefSec->appendChild(new DOMText("\n "));
|
||||
return $returnRefSec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<DOMNode> $headers [count($headers) === $columns]
|
||||
* @param array<array<DOMNode>> $rows [count($rows[$i]) === $columns]
|
||||
*/
|
||||
private function generateDocbookInformalTable(
|
||||
DOMDocument $doc,
|
||||
int $indent,
|
||||
int $columns,
|
||||
array $headers,
|
||||
array $rows
|
||||
): DOMElement {
|
||||
$strIndent = str_repeat(' ', $indent);
|
||||
|
||||
$headerRow = $doc->createElement('row');
|
||||
foreach ($headers as $header) {
|
||||
$headerEntry = $doc->createElement('entry');
|
||||
$headerEntry->appendChild($header);
|
||||
|
||||
$headerRow->append("\n$strIndent ", $headerEntry);
|
||||
}
|
||||
$headerRow->append("\n$strIndent ");
|
||||
|
||||
$thead = $doc->createElement('thead');
|
||||
$thead->append(
|
||||
"\n$strIndent ",
|
||||
$headerRow,
|
||||
"\n$strIndent ",
|
||||
);
|
||||
|
||||
$tbody = $doc->createElement('tbody');
|
||||
foreach ($rows as $row) {
|
||||
$bodyRow = $doc->createElement('row');
|
||||
foreach ($row as $cell) {
|
||||
$entry = $doc->createElement('entry');
|
||||
$entry->appendChild($cell);
|
||||
|
||||
$bodyRow->appendChild(new DOMText("\n$strIndent "));
|
||||
$bodyRow->appendChild($entry);
|
||||
}
|
||||
$bodyRow->appendChild(new DOMText("\n$strIndent "));
|
||||
|
||||
$tbody->append(
|
||||
"\n$strIndent ",
|
||||
$bodyRow,
|
||||
"\n$strIndent ",
|
||||
);
|
||||
}
|
||||
|
||||
$tgroup = $doc->createElement('tgroup');
|
||||
$tgroup->setAttribute('cols', (string) $columns);
|
||||
$tgroup->append(
|
||||
"\n$strIndent ",
|
||||
$thead,
|
||||
"\n$strIndent ",
|
||||
$tbody,
|
||||
"\n$strIndent ",
|
||||
);
|
||||
|
||||
$table = $doc->createElement('informaltable');
|
||||
$table->append(
|
||||
"\n$strIndent ",
|
||||
$tgroup,
|
||||
"\n$strIndent",
|
||||
);
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
private function getChangelogSection(DOMDocument $doc): DOMElement {
|
||||
$refSec = $this->generateRefSect1($doc, 'changelog');
|
||||
$headers = [
|
||||
$doc->createEntityReference('Version'),
|
||||
$doc->createEntityReference('Description'),
|
||||
];
|
||||
$rows = [[
|
||||
new DOMText('8.X.0'),
|
||||
new DOMText("\n Description\n "),
|
||||
]];
|
||||
$table = $this->generateDocbookInformalTable(
|
||||
$doc,
|
||||
/* indent: */ 2,
|
||||
/* columns: */ 2,
|
||||
/* headers: */ $headers,
|
||||
/* rows: */ $rows
|
||||
);
|
||||
$refSec->appendChild($table);
|
||||
|
||||
$refSec->appendChild(new DOMText("\n "));
|
||||
return $refSec;
|
||||
}
|
||||
|
||||
private function getExampleSection(DOMDocument $doc, string $id): DOMElement {
|
||||
$refSec = $this->generateRefSect1($doc, 'examples');
|
||||
|
||||
$example = $doc->createElement('example');
|
||||
$fnName = $this->name->__toString();
|
||||
$example->setAttribute('xml:id', $id . '.example.basic');
|
||||
|
||||
$title = $doc->createElement('title');
|
||||
$fn = $doc->createElement($this->isMethod() ? 'methodname' : 'function');
|
||||
$fn->append($fnName);
|
||||
$title->append($fn, ' example');
|
||||
|
||||
$example->append("\n ", $title);
|
||||
|
||||
$para = $doc->createElement('para');
|
||||
$para->append("\n ", "Description.", "\n ");
|
||||
$example->append("\n ", $para);
|
||||
|
||||
$prog = $doc->createElement('programlisting');
|
||||
$prog->setAttribute('role', 'php');
|
||||
$code = new DOMCdataSection(
|
||||
<<<CODE_EXAMPLE
|
||||
|
||||
<?php
|
||||
echo "Code example";
|
||||
?>
|
||||
|
||||
CODE_EXAMPLE
|
||||
);
|
||||
$prog->append("\n");
|
||||
$prog->appendChild($code);
|
||||
$prog->append("\n ");
|
||||
|
||||
$example->append("\n ", $prog);
|
||||
$example->append("\n ", $doc->createEntityReference('example.outputs'));
|
||||
|
||||
$output = new DOMCdataSection(
|
||||
<<<OUPUT_EXAMPLE
|
||||
|
||||
Code example
|
||||
|
||||
OUPUT_EXAMPLE
|
||||
);
|
||||
$screen = $doc->createElement('screen');
|
||||
$screen->append("\n");
|
||||
$screen->appendChild($output);
|
||||
$screen->append("\n ");
|
||||
|
||||
$example->append(
|
||||
"\n ",
|
||||
$screen,
|
||||
"\n ",
|
||||
);
|
||||
|
||||
$refSec->append(
|
||||
$example,
|
||||
"\n ",
|
||||
);
|
||||
return $refSec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, FuncInfo> $funcMap
|
||||
* @param array<string, FuncInfo> $aliasMap
|
||||
@@ -5155,6 +5580,9 @@ if ($replacePredefinedConstants && $locationCount < 2) {
|
||||
if ($replaceClassSynopses && $locationCount < 2) {
|
||||
die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --replace-classsynopses ./ ../doc-en/\n");
|
||||
}
|
||||
if ($generateMethodSynopses && $locationCount < 2) {
|
||||
die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --generate-methodsynopses ./ ../doc-en/\n");
|
||||
}
|
||||
if ($replaceMethodSynopses && $locationCount < 2) {
|
||||
die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --replace-methodsynopses ./ ../doc-en/\n");
|
||||
}
|
||||
@@ -5162,7 +5590,7 @@ if ($verifyManual && $locationCount < 2) {
|
||||
die("At least one source stub path and a target manual directory has to be provided:\n./build/gen_stub.php --verify-manual ./ ../doc-en/\n");
|
||||
}
|
||||
$manualTarget = null;
|
||||
if ($replacePredefinedConstants || $replaceClassSynopses || $replaceMethodSynopses || $verifyManual) {
|
||||
if ($replacePredefinedConstants || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses || $verifyManual) {
|
||||
$manualTarget = array_pop($locations);
|
||||
}
|
||||
if ($locations === []) {
|
||||
@@ -5361,16 +5789,14 @@ if ($replaceClassSynopses || $verifyManual) {
|
||||
}
|
||||
|
||||
if ($generateMethodSynopses) {
|
||||
$methodSynopsesDirectory = getcwd() . "/methodsynopses";
|
||||
|
||||
$methodSynopses = generateMethodSynopses($funcMap, $aliasMap);
|
||||
if (!empty($methodSynopses)) {
|
||||
if (!file_exists($methodSynopsesDirectory)) {
|
||||
mkdir($methodSynopsesDirectory);
|
||||
}
|
||||
if (!file_exists($manualTarget)) {
|
||||
mkdir($manualTarget);
|
||||
}
|
||||
|
||||
foreach ($methodSynopses as $filename => $content) {
|
||||
if (file_put_contents("$methodSynopsesDirectory/$filename", $content)) {
|
||||
foreach ($methodSynopses as $filename => $content) {
|
||||
if (!file_exists("$manualTarget/$filename")) {
|
||||
if (file_put_contents("$manualTarget/$filename", $content)) {
|
||||
echo "Saved $filename\n";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user