Major refactoring cleanup: - Add new controller architecture (class-controller-*.php) - Add new settings-v2 UI (views/settings-v2/) - Add new CSS architecture (agentic-sidebar.css, tokens) - Add esbuild build pipeline (scripts/build.js, package.json) - Add composer dependencies (vendor/) - Add frontend src directory (assets/js/src/index.jsx) - Add documentation files - Remove old/obsolete files (class-settings.php, old CSS) This commits all pending changes from previous refactoring efforts.
351 lines
9.5 KiB
PHP
351 lines
9.5 KiB
PHP
<?php
|
|
/**
|
|
* A doc generator that outputs documentation in Markdown format.
|
|
*
|
|
* @author Stefano Kowalke <blueduck@gmx.net>
|
|
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
|
|
* @copyright 2014 Arroba IT
|
|
* @copyright 2024 PHPCSStandards and contributors
|
|
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/HEAD/licence.txt BSD Licence
|
|
*/
|
|
|
|
namespace PHP_CodeSniffer\Generators;
|
|
|
|
use DOMElement;
|
|
use DOMNode;
|
|
use PHP_CodeSniffer\Config;
|
|
|
|
class Markdown extends Generator
|
|
{
|
|
|
|
|
|
/**
|
|
* Generates the documentation for a standard.
|
|
*
|
|
* @return void
|
|
* @see processSniff()
|
|
*/
|
|
public function generate()
|
|
{
|
|
if (empty($this->docFiles) === true) {
|
|
return;
|
|
}
|
|
|
|
ob_start();
|
|
parent::generate();
|
|
|
|
$content = ob_get_contents();
|
|
ob_end_clean();
|
|
|
|
if (trim($content) !== '') {
|
|
echo $this->getFormattedHeader();
|
|
echo $content;
|
|
echo $this->getFormattedFooter();
|
|
}
|
|
|
|
}//end generate()
|
|
|
|
|
|
/**
|
|
* Print the markdown header.
|
|
*
|
|
* @deprecated 3.12.0 Use Markdown::getFormattedHeader() instead.
|
|
*
|
|
* @codeCoverageIgnore
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function printHeader()
|
|
{
|
|
trigger_error(
|
|
'The '.__METHOD__.'() method is deprecated. Use "echo '.__CLASS__.'::getFormattedHeader()" instead.',
|
|
E_USER_DEPRECATED
|
|
);
|
|
|
|
echo $this->getFormattedHeader();
|
|
|
|
}//end printHeader()
|
|
|
|
|
|
/**
|
|
* Format the markdown header.
|
|
*
|
|
* @since 3.12.0 Replaces the deprecated Markdown::printHeader() method.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getFormattedHeader()
|
|
{
|
|
$standard = $this->ruleset->name;
|
|
|
|
return "# $standard Coding Standard".PHP_EOL;
|
|
|
|
}//end getFormattedHeader()
|
|
|
|
|
|
/**
|
|
* Print the markdown footer.
|
|
*
|
|
* @deprecated 3.12.0 Use Markdown::getFormattedFooter() instead.
|
|
*
|
|
* @codeCoverageIgnore
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function printFooter()
|
|
{
|
|
trigger_error(
|
|
'The '.__METHOD__.'() method is deprecated. Use "echo '.__CLASS__.'::getFormattedFooter()" instead.',
|
|
E_USER_DEPRECATED
|
|
);
|
|
|
|
echo $this->getFormattedFooter();
|
|
|
|
}//end printFooter()
|
|
|
|
|
|
/**
|
|
* Format the markdown footer.
|
|
*
|
|
* @since 3.12.0 Replaces the deprecated Markdown::printFooter() method.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getFormattedFooter()
|
|
{
|
|
// Turn off errors so we don't get timezone warnings if people
|
|
// don't have their timezone set.
|
|
$errorLevel = error_reporting(0);
|
|
$output = PHP_EOL.'Documentation generated on '.date('r');
|
|
$output .= ' by [PHP_CodeSniffer '.Config::VERSION.'](https://github.com/PHPCSStandards/PHP_CodeSniffer)'.PHP_EOL;
|
|
error_reporting($errorLevel);
|
|
|
|
return $output;
|
|
|
|
}//end getFormattedFooter()
|
|
|
|
|
|
/**
|
|
* Process the documentation for a single sniff.
|
|
*
|
|
* @param \DOMNode $doc The DOMNode object for the sniff.
|
|
* It represents the "documentation" tag in the XML
|
|
* standard file.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function processSniff(DOMNode $doc)
|
|
{
|
|
$content = '';
|
|
foreach ($doc->childNodes as $node) {
|
|
if ($node->nodeName === 'standard') {
|
|
$content .= $this->getFormattedTextBlock($node);
|
|
} else if ($node->nodeName === 'code_comparison') {
|
|
$content .= $this->getFormattedCodeComparisonBlock($node);
|
|
}
|
|
}
|
|
|
|
if (trim($content) !== '') {
|
|
$title = $this->getTitle($doc);
|
|
echo PHP_EOL."## $title".PHP_EOL.PHP_EOL;
|
|
echo $content;
|
|
}
|
|
|
|
}//end processSniff()
|
|
|
|
|
|
/**
|
|
* Print a text block found in a standard.
|
|
*
|
|
* @param \DOMNode $node The DOMNode object for the text block.
|
|
*
|
|
* @deprecated 3.12.0 Use Markdown::getFormattedTextBlock() instead.
|
|
*
|
|
* @codeCoverageIgnore
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function printTextBlock(DOMNode $node)
|
|
{
|
|
trigger_error(
|
|
'The '.__METHOD__.'() method is deprecated. Use "echo '.__CLASS__.'::getFormattedTextBlock()" instead.',
|
|
E_USER_DEPRECATED
|
|
);
|
|
|
|
echo $this->getFormattedTextBlock($node);
|
|
|
|
}//end printTextBlock()
|
|
|
|
|
|
/**
|
|
* Format a text block found in a standard.
|
|
*
|
|
* @param \DOMNode $node The DOMNode object for the text block.
|
|
*
|
|
* @since 3.12.0 Replaces the deprecated Markdown::printTextBlock() method.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getFormattedTextBlock(DOMNode $node)
|
|
{
|
|
$content = $node->nodeValue;
|
|
if (empty($content) === true) {
|
|
return '';
|
|
}
|
|
|
|
$content = trim($content);
|
|
$content = htmlspecialchars($content, (ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401));
|
|
$content = str_replace('<em>', '*', $content);
|
|
$content = str_replace('</em>', '*', $content);
|
|
|
|
$nodeLines = explode("\n", $content);
|
|
$lineCount = count($nodeLines);
|
|
$lines = [];
|
|
|
|
for ($i = 0; $i < $lineCount; $i++) {
|
|
$currentLine = trim($nodeLines[$i]);
|
|
if ($currentLine === '') {
|
|
// The text contained a blank line. Respect this.
|
|
$lines[] = '';
|
|
continue;
|
|
}
|
|
|
|
// Check if the _next_ line is blank.
|
|
if (isset($nodeLines[($i + 1)]) === false
|
|
|| trim($nodeLines[($i + 1)]) === ''
|
|
) {
|
|
// Next line is blank, just add the line.
|
|
$lines[] = $currentLine;
|
|
} else {
|
|
// Ensure that line breaks are respected in markdown.
|
|
$lines[] = $currentLine.' ';
|
|
}
|
|
}
|
|
|
|
return implode(PHP_EOL, $lines).PHP_EOL;
|
|
|
|
}//end getFormattedTextBlock()
|
|
|
|
|
|
/**
|
|
* Print a code comparison block found in a standard.
|
|
*
|
|
* @param \DOMNode $node The DOMNode object for the code comparison block.
|
|
*
|
|
* @deprecated 3.12.0 Use Markdown::getFormattedCodeComparisonBlock() instead.
|
|
*
|
|
* @codeCoverageIgnore
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function printCodeComparisonBlock(DOMNode $node)
|
|
{
|
|
trigger_error(
|
|
'The '.__METHOD__.'() method is deprecated. Use "echo '.__CLASS__.'::getFormattedCodeComparisonBlock()" instead.',
|
|
E_USER_DEPRECATED
|
|
);
|
|
|
|
echo $this->getFormattedCodeComparisonBlock($node);
|
|
|
|
}//end printCodeComparisonBlock()
|
|
|
|
|
|
/**
|
|
* Format a code comparison block found in a standard.
|
|
*
|
|
* @param \DOMNode $node The DOMNode object for the code comparison block.
|
|
*
|
|
* @since 3.12.0 Replaces the deprecated Markdown::printCodeComparisonBlock() method.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getFormattedCodeComparisonBlock(DOMNode $node)
|
|
{
|
|
$codeBlocks = $node->getElementsByTagName('code');
|
|
$firstCodeElm = $codeBlocks->item(0);
|
|
$secondCodeElm = $codeBlocks->item(1);
|
|
|
|
if (isset($firstCodeElm, $secondCodeElm) === false) {
|
|
// Missing at least one code block.
|
|
return '';
|
|
}
|
|
|
|
$firstTitle = $this->formatCodeTitle($firstCodeElm);
|
|
$first = $this->formatCodeSample($firstCodeElm);
|
|
|
|
$secondTitle = $this->formatCodeTitle($secondCodeElm);
|
|
$second = $this->formatCodeSample($secondCodeElm);
|
|
|
|
$titleRow = '';
|
|
if ($firstTitle !== '' || $secondTitle !== '') {
|
|
$titleRow .= ' <tr>'.PHP_EOL;
|
|
$titleRow .= " <th>$firstTitle</th>".PHP_EOL;
|
|
$titleRow .= " <th>$secondTitle</th>".PHP_EOL;
|
|
$titleRow .= ' </tr>'.PHP_EOL;
|
|
}
|
|
|
|
$codeRow = '';
|
|
if ($first !== '' || $second !== '') {
|
|
$codeRow .= ' <tr>'.PHP_EOL;
|
|
$codeRow .= '<td>'.PHP_EOL.PHP_EOL;
|
|
$codeRow .= " $first".PHP_EOL.PHP_EOL;
|
|
$codeRow .= '</td>'.PHP_EOL;
|
|
$codeRow .= '<td>'.PHP_EOL.PHP_EOL;
|
|
$codeRow .= " $second".PHP_EOL.PHP_EOL;
|
|
$codeRow .= '</td>'.PHP_EOL;
|
|
$codeRow .= ' </tr>'.PHP_EOL;
|
|
}
|
|
|
|
$output = '';
|
|
if ($titleRow !== '' || $codeRow !== '') {
|
|
$output .= ' <table>'.PHP_EOL;
|
|
$output .= $titleRow;
|
|
$output .= $codeRow;
|
|
$output .= ' </table>'.PHP_EOL;
|
|
}
|
|
|
|
return $output;
|
|
|
|
}//end getFormattedCodeComparisonBlock()
|
|
|
|
|
|
/**
|
|
* Retrieve a code block title and prepare it for output as HTML.
|
|
*
|
|
* @param \DOMElement $codeElm The DOMElement object for a code block.
|
|
*
|
|
* @since 3.12.0
|
|
*
|
|
* @return string
|
|
*/
|
|
private function formatCodeTitle(DOMElement $codeElm)
|
|
{
|
|
$title = trim($codeElm->getAttribute('title'));
|
|
return str_replace(' ', ' ', $title);
|
|
|
|
}//end formatCodeTitle()
|
|
|
|
|
|
/**
|
|
* Retrieve a code block contents and prepare it for output as HTML.
|
|
*
|
|
* @param \DOMElement $codeElm The DOMElement object for a code block.
|
|
*
|
|
* @since 3.12.0
|
|
*
|
|
* @return string
|
|
*/
|
|
private function formatCodeSample(DOMElement $codeElm)
|
|
{
|
|
$code = (string) $codeElm->nodeValue;
|
|
$code = trim($code);
|
|
$code = str_replace("\n", PHP_EOL.' ', $code);
|
|
$code = str_replace(['<em>', '</em>'], '', $code);
|
|
|
|
return $code;
|
|
|
|
}//end formatCodeSample()
|
|
|
|
|
|
}//end class
|