PHPDocumentationGenerator
đĻ DocGenerator
DocGenerator A tool to automatically generate HTML documentation from PHP docblocks
function __construct(string $outputPath = 'docs')
{
$this->outputPath = rtrim($outputPath, '/');
if (!is_dir($this->outputPath)) {
echo "Creating output directory: {$this->outputPath}\n";
if (!mkdir($this->outputPath, 0755, true)) {
throw new \Exception("Failed to create output directory: {$this->outputPath}");
}
đ§ createStyleSheet
Create CSS file for documentation styling
function createStyleSheet(): void
{
$css = <<<CSS
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
background-color: #f8f9fa;
}
h1 {
color: #2c3e50;
border-bottom: 3px solid #3498db;
padding-bottom: 10px;
margin-bottom: 30px;
font-size: 2.5em;
}
h2 {
color: #2980b9;
margin-top: 40px;
padding: 10px;
background-color: #ecf0f1;
border-radius: 5px;
font-size: 1.8em;
}
h3 {
color: #c0392b;
margin-top: 25px;
font-size: 1.4em;
border-left: 4px solid #e74c3c;
padding-left: 10px;
}
.function-description {
background-color: white;
padding: 15px;
border-left: 4px solid #2ecc71;
margin: 20px 0;
border-radius: 0 5px 5px 0;
}
.parameter-list {
list-style: none;
padding: 0;
}
.parameter-item {
background-color: white;
padding: 10px 15px;
margin: 5px 0;
border-radius: 5px;
border: 1px solid #ddd;
}
.parameter-name {
color: #2c3e50;
font-family: monospace;
font-weight: bold;
background-color: #f7f9fa;
padding: 2px 5px;
border-radius: 3px;
}
.parameter-type {
color: #e67e22;
font-family: monospace;
}
.return-info {
background-color: white;
padding: 15px;
border-radius: 5px;
border: 1px solid #ddd;
margin: 10px 0;
}
.throws-list {
list-style: none;
padding: 0;
}
.throws-item {
background-color: #fff5f5;
padding: 10px 15px;
margin: 5px 0;
border-radius: 5px;
border: 1px solid #fed7d7;
}
.throws-type {
color: #c53030;
font-weight: bold;
}
.author-list {
list-style: none;
padding: 0;
}
.author-item {
background-color: #f0fff4;
padding: 10px 15px;
margin: 5px 0;
border-radius: 5px;
border: 1px solid #c6f6d5;
}
.author-type {
color: #2f855a;
font-weight: bold;
}
.divider {
border: 0;
height: 2px;
background: linear-gradient(to right, transparent, #718096, transparent);
margin: 40px 0;
}
.navbar {
background-color: #2c3e50;
color: white;
padding: 10px 20px;
position: sticky;
top: 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.navbar a {
color: white;
text-decoration: none;
margin-right: 15px;
}
.navbar a:hover {
text-decoration: underline;
}
.method-visibility {
display: inline-block;
padding: 3px 6px;
border-radius: 3px;
margin-right: 8px;
font-size: 0.8em;
font-weight: bold;
}
.public {
background-color: #d4edda;
color: #155724;
}
.protected {
background-color: #fff3cd;
color: #856404;
}
.private {
background-color: #f8d7da;
color: #721c24;
}
#search-input {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ddd;
border-radius: 5px;
}
.search-highlight {
background-color: yellow;
}
.code-example {
background-color: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
font-family: monospace;
margin: 15px 0;
}
.deprecated {
text-decoration: line-through;
color: #6c757d;
}
.since-tag {
font-size: 0.8em;
color: #6c757d;
margin-left: 10px;
}
.file-group {
background-color: #f8f9fa;
padding: 10px;
margin-bottom: 15px;
border-radius: 5px;
border-left: 4px solid #3498db;
}
.file-count {
font-weight: bold;
color: #2c3e50;
margin-left: 5px;
}
CSS;
// Create the CSS file in the output directory
file_put_contents("{$this->outputPath}
đ§ createStyleSheet
Create CSS file for documentation styling
function createStyleSheet(): void
{
$css = <<<CSS
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
background-color: #f8f9fa;
}
h1 {
color: #2c3e50;
border-bottom: 3px solid #3498db;
padding-bottom: 10px;
margin-bottom: 30px;
font-size: 2.5em;
}
h2 {
color: #2980b9;
margin-top: 40px;
padding: 10px;
background-color: #ecf0f1;
border-radius: 5px;
font-size: 1.8em;
}
h3 {
color: #c0392b;
margin-top: 25px;
font-size: 1.4em;
border-left: 4px solid #e74c3c;
padding-left: 10px;
}
.function-description {
background-color: white;
padding: 15px;
border-left: 4px solid #2ecc71;
margin: 20px 0;
border-radius: 0 5px 5px 0;
}
.parameter-list {
list-style: none;
padding: 0;
}
.parameter-item {
background-color: white;
padding: 10px 15px;
margin: 5px 0;
border-radius: 5px;
border: 1px solid #ddd;
}
.parameter-name {
color: #2c3e50;
font-family: monospace;
font-weight: bold;
background-color: #f7f9fa;
padding: 2px 5px;
border-radius: 3px;
}
.parameter-type {
color: #e67e22;
font-family: monospace;
}
.return-info {
background-color: white;
padding: 15px;
border-radius: 5px;
border: 1px solid #ddd;
margin: 10px 0;
}
.throws-list {
list-style: none;
padding: 0;
}
.throws-item {
background-color: #fff5f5;
padding: 10px 15px;
margin: 5px 0;
border-radius: 5px;
border: 1px solid #fed7d7;
}
.throws-type {
color: #c53030;
font-weight: bold;
}
.author-list {
list-style: none;
padding: 0;
}
.author-item {
background-color: #f0fff4;
padding: 10px 15px;
margin: 5px 0;
border-radius: 5px;
border: 1px solid #c6f6d5;
}
.author-type {
color: #2f855a;
font-weight: bold;
}
.divider {
border: 0;
height: 2px;
background: linear-gradient(to right, transparent, #718096, transparent);
margin: 40px 0;
}
.navbar {
background-color: #2c3e50;
color: white;
padding: 10px 20px;
position: sticky;
top: 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.navbar a {
color: white;
text-decoration: none;
margin-right: 15px;
}
.navbar a:hover {
text-decoration: underline;
}
.method-visibility {
display: inline-block;
padding: 3px 6px;
border-radius: 3px;
margin-right: 8px;
font-size: 0.8em;
font-weight: bold;
}
.public {
background-color: #d4edda;
color: #155724;
}
.protected {
background-color: #fff3cd;
color: #856404;
}
.private {
background-color: #f8d7da;
color: #721c24;
}
#search-input {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ddd;
border-radius: 5px;
}
.search-highlight {
background-color: yellow;
}
.code-example {
background-color: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
font-family: monospace;
margin: 15px 0;
}
.deprecated {
text-decoration: line-through;
color: #6c757d;
}
.since-tag {
font-size: 0.8em;
color: #6c757d;
margin-left: 10px;
}
.file-group {
background-color: #f8f9fa;
padding: 10px;
margin-bottom: 15px;
border-radius: 5px;
border-left: 4px solid #3498db;
}
.file-count {
font-weight: bold;
color: #2c3e50;
margin-left: 5px;
}
CSS;
// Create the CSS file in the output directory
file_put_contents("{$this->outputPath}
đ§ generate
Generate documentation for a specific file or directory
function generate(string $path): void
{
try {
if (is_dir($path)) {
$this->processDirectory($path);
} else {
$this->processFile($path);
}
$this->generateIndex();
echo "Documentation generated successfully in {$this->outputPath}\n";
echo "Found and documented " . count($this->processedFiles) . " files\n";
// ... (truncated)
âī¸ Parameters
$path (string) File or directory path
âŠī¸ Returns
(void)
đ§ processDirectory
Process all PHP files in a directory
function processDirectory(string $dirPath): void
{
try {
if (!is_readable($dirPath)) {
throw new \Exception("Directory is not readable: {$dirPath}");
}
echo "Starting to process directory: {$dirPath}
âī¸ Parameters
$dirPath (string) Directory path
â ī¸ Throws
\Exception : If directory cannot be read
đ§ processFile
Process a single PHP file
function processFile(string $filePath): void
{
try {
echo "Reading file: " . basename($filePath) . "\n";
$content = file_get_contents($filePath);
if ($content === false) {
throw new \Exception("Could not read file {$filePath}");
}
$tokens = token_get_all($content);
$documentation = [];
$currentClass = null;
// ... (truncated)
âī¸ Parameters
$filePath (string) File path
â ī¸ Throws
\Exception : If file cannot be read or parsed
Extract a code snippet for an entity
function extractCodeSnippet(array $tokens, int $maxLines): string
{
$code = '';
$bracketCount = 0;
$lineCount = 0;
$started = false;
$functionFound = false;
foreach ($tokens as $token) {
// Check for function keyword
if (!$functionFound && is_array($token) && $token[0] === T_FUNCTION) {
$functionFound = true;
$code .= $token[1];
// ... (truncated)
âī¸ Parameters
$tokens (array) Token array after doc block
$maxLines (int) Maximum lines to extract
âŠī¸ Returns
(string) Formatted code snippet
đ§ parseDocBlock
Parse a doc block comment
function parseDocBlock(string $docBlock): array
{
$lines = explode("\n", $docBlock);
$parsed = [
'description' => '',
'params' => [],
'return' => null,
'throws' => [],
'author' => [],
'since' => null,
'deprecated' => false,
// ... (truncated)
âī¸ Parameters
$docBlock (string) Raw doc block comment
âŠī¸ Returns
(array) Parsed documentation
đ§ findEntityInfo
Find information about the entity following a doc block
function findEntityInfo(array $tokens): array
{
$name = null;
$type = null;
$visibility = 'public'; // Default visibility
$foundVisibility = false;
$foundFunction = false;
$i = 0;
// Skip any whitespace tokens
while ($i < count($tokens) && is_array($tokens[$i]) && $tokens[$i][0] === T_WHITESPACE) {
$i++;
}
// ... (truncated)
âī¸ Parameters
$tokens (array) Token array
âŠī¸ Returns
(array) Entity info [name, type, visibility]
đ§ saveDocumentation
Save documentation to HTML file
function saveDocumentation(string $sourcePath, array $documentation): void
{
$filename = basename($sourcePath, '.php');
$html = $this->getFileHeader($filename);
$html .= "<div class='navbar'>";
$html .= "<a href='index.html'>Home</a>";
$html .= "<a href='#top'>{$filename}
âī¸ Parameters
$sourcePath (string) Original file path
$documentation (array) Documentation data
Get HTML header with metadata and styling
function getFileHeader(string $title = 'PHP Documentation'): string
{
return <<<HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{$title}
âī¸ Parameters
$title (string) Page title
âŠī¸ Returns
(string) HTML header content
Get HTML header with metadata and styling
function getFileHeader(string $title = 'PHP Documentation'): string
{
return <<<HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{$title}
âī¸ Parameters
$title (string) Page title
âŠī¸ Returns
(string) HTML header content
đ§ generateIndex
Generate index file listing all documented files
function generateIndex(): void
{
$html = $this->getFileHeader('Documentation Index');
$html .= "<div class='navbar'>";
$html .= "<a href='index.html'>Home</a>";
$html .= "</div>";
$html .= "<h1>đ Documentation Index</h1>\n\n";
// Add search input
$html .= "<input type='text' id='search-input' placeholder='Search documentation...'>\n";
$html .= "<script>
document.getElementById('search-input').addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
const items = document.querySelectorAll('.parameter-item, .file-group');
items.forEach(function(item) {
const text = item.textContent.toLowerCase();
item.style.display = searchTerm === '' || text.includes(searchTerm) ? '' : 'none';
});
});
</script>\n\n";
// Group files by directory
$filesByDir = [];
foreach ($this->processedFiles as $file) {
// ... (truncated)