├── tests ├── bootstrap.php ├── Functional │ ├── expected │ │ ├── classes │ │ │ └── PhpDocumentorMarkdown │ │ │ │ └── Example │ │ │ │ ├── Arrayable.md │ │ │ │ ├── AbstractProduct.md │ │ │ │ ├── ReviewableTrait.md │ │ │ │ ├── Pizza │ │ │ │ ├── Sauce.md │ │ │ │ └── Base.md │ │ │ │ ├── ProductInterface.md │ │ │ │ ├── ManyInterfaces.md │ │ │ │ └── Pizza.md │ │ ├── functions │ │ │ ├── getDatabaseConfig.md │ │ │ └── mockFunctionWithParameters.md │ │ └── Home.md │ ├── FunctionalTestCase.php │ ├── PhpdocOutputTest.php │ └── Service │ │ └── MarkdownGeneratorService.php └── Unit │ └── Twig │ ├── templates │ └── macros.test.twig │ ├── UnitTestCase.php │ └── Macro │ ├── MacroTestCase.php │ └── MacroTest.php ├── themes └── markdown │ ├── partials │ ├── footer.md.twig │ ├── toc.md.twig │ ├── header.md.twig │ ├── description.md.twig │ ├── tags.md.twig │ └── inheritance.md.twig │ ├── template.xml │ ├── property.md.twig │ ├── function.md.twig │ ├── trait.md.twig │ ├── interface.md.twig │ ├── method.md.twig │ ├── class.md.twig │ ├── index.md.twig │ └── include │ └── macros.twig ├── .gitignore ├── src └── Example │ ├── Arrayable.php │ ├── ManyInterfaces.php │ ├── AbstractProduct.php │ ├── ReviewableTrait.php │ ├── Pizza │ ├── Sauce.php │ └── Base.php │ ├── ProductInterface.php │ ├── functions.php │ └── Pizza.php ├── .wiki ├── classes │ └── PhpDocumentorMarkdown │ │ └── Example │ │ ├── Arrayable.md │ │ ├── AbstractProduct.md │ │ ├── ReviewableTrait.md │ │ ├── Pizza │ │ ├── Sauce.md │ │ └── Base.md │ │ ├── ProductInterface.md │ │ ├── ManyInterfaces.md │ │ └── Pizza.md ├── functions │ ├── getDatabaseConfig.md │ └── mockFunctionWithParameters.md └── Home.md ├── patches └── phpdocumentor-resource-path.patch ├── .github └── workflows │ ├── docs.yml │ └── test.yml ├── phpunit.dist.xml ├── phpdoc.dist.xml ├── LICENSE ├── composer.json ├── CHANGELOG.md └── README.md /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | Automatically generated on {{ "now"|date("Y-m-d") }} 3 | -------------------------------------------------------------------------------- /themes/markdown/partials/toc.md.twig: -------------------------------------------------------------------------------- 1 | {# GitLab supports automatic table of contents generation #} 2 | {% if parameter.markdownFlavour == 'gitlab' %} 3 | 4 | ## Table of contents 5 | 6 | [[_TOC_]] 7 | {% endif %} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # PhpDocumentor 2 | .phpdoc/ 3 | phpdoc.xml 4 | ast.dump 5 | var/ 6 | .tests 7 | 8 | # PHPUnit 9 | phpunit.xml 10 | .phpunit* 11 | 12 | # IDE 13 | .idea 14 | 15 | # Composer 16 | /vendor/ 17 | 18 | # Misc 19 | tmp/ 20 | -------------------------------------------------------------------------------- /src/Example/Arrayable.php: -------------------------------------------------------------------------------- 1 | name = $name; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Generate docs 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | generate-docs: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v2 16 | 17 | - name: Set up environment 18 | run: | 19 | cp phpdoc.dist.xml phpdoc.xml 20 | wget https://phpdoc.org/phpDocumentor.phar 21 | chmod +x phpDocumentor.phar 22 | 23 | - name: Generate documentation 24 | run: ./phpDocumentor.phar 25 | -------------------------------------------------------------------------------- /themes/markdown/partials/tags.md.twig: -------------------------------------------------------------------------------- 1 | {% set sees = tags.see | default([]) | filter(see => see.reference | trim) %} 2 | {% set links = tags.link | default([]) | filter(link => link.link | trim) %} 3 | {% if sees is not empty or links is not empty %} 4 | 5 | **See Also:** 6 | 7 | {% for see in sees %} 8 | * {{ see.reference }}{% if see.description | trim %} - {{ see.description | raw }}{% endif %}{{- '\n' -}} 9 | {% endfor %} 10 | {% for link in links %} 11 | * {{ link.link }}{% if link.description | trim and link.description != link.link %} - {{ link.description | raw }}{% endif %}{{- '\n' -}} 12 | {% endfor %} 13 | {% endif %} -------------------------------------------------------------------------------- /themes/markdown/partials/inheritance.md.twig: -------------------------------------------------------------------------------- 1 | {% import 'include/macros.twig' as macros %} 2 | {%- for otherNode in others -%} 3 | {%- if loop.index0 > 0 -%} 4 | {{- ',\n ' -}} 5 | {%- else -%} 6 | {{- ' ' -}} 7 | {%- endif -%} 8 | {%- if '0' == macros.mdNestingLevel(otherNode.FullyQualifiedStructuralElementName) -%} 9 | {{- '`' ~ otherNode.FullyQualifiedStructuralElementName | default(otherNode.name) ~ '`' -}} 10 | {%- else -%} 11 | {{- macros.mdLink(otherNode, macros.mdClassPath(node), otherNode.FullyQualifiedStructuralElementName, 'class') -}} 12 | {%- endif -%} 13 | {%- endfor -%} 14 | -------------------------------------------------------------------------------- /phpunit.dist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ./tests/Unit 5 | 6 | 7 | ./tests/Functional 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.wiki/classes/PhpDocumentorMarkdown/Example/ReviewableTrait.md: -------------------------------------------------------------------------------- 1 | # ReviewableTrait 2 | 3 | A trait for reviewable objects. 4 | 5 | *** 6 | 7 | * Full name: `\PhpDocumentorMarkdown\Example\ReviewableTrait` 8 | 9 | ## Constants 10 | 11 | | Constant | Visibility | Type | Value | 12 | |--------------|------------|------|-------| 13 | | `REVIEWABLE` | public | | true | 14 | 15 | ## Properties 16 | 17 | ### reviews 18 | 19 | ```php 20 | public static array $reviews 21 | ``` 22 | 23 | * This property is **static**. 24 | 25 | *** 26 | 27 | ## Methods 28 | 29 | ### isReviewed 30 | 31 | Whether the object has been reviewed. 32 | 33 | ```php 34 | public isReviewed(): bool 35 | ``` 36 | 37 | *** 38 | -------------------------------------------------------------------------------- /tests/Functional/expected/classes/PhpDocumentorMarkdown/Example/ReviewableTrait.md: -------------------------------------------------------------------------------- 1 | # ReviewableTrait 2 | 3 | A trait for reviewable objects. 4 | 5 | *** 6 | 7 | * Full name: `\PhpDocumentorMarkdown\Example\ReviewableTrait` 8 | 9 | ## Constants 10 | 11 | | Constant | Visibility | Type | Value | 12 | |--------------|------------|------|-------| 13 | | `REVIEWABLE` | public | | true | 14 | 15 | ## Properties 16 | 17 | ### reviews 18 | 19 | ```php 20 | public static array $reviews 21 | ``` 22 | 23 | * This property is **static**. 24 | 25 | *** 26 | 27 | ## Methods 28 | 29 | ### isReviewed 30 | 31 | Whether the object has been reviewed. 32 | 33 | ```php 34 | public isReviewed(): bool 35 | ``` 36 | 37 | *** 38 | -------------------------------------------------------------------------------- /.wiki/classes/PhpDocumentorMarkdown/Example/Pizza/Sauce.md: -------------------------------------------------------------------------------- 1 | # Sauce 2 | 3 | Pizza sauce class. 4 | 5 | *** 6 | 7 | * Full name: `\PhpDocumentorMarkdown\Example\Pizza\Sauce` 8 | 9 | ## Properties 10 | 11 | ### name 12 | 13 | Sauce name. 14 | 15 | ```php 16 | protected string $name 17 | ``` 18 | 19 | *** 20 | 21 | ### tomatoSupplier 22 | 23 | Tomato supplier. 24 | 25 | ```php 26 | public static string $tomatoSupplier 27 | ``` 28 | 29 | * This property is **static**. 30 | 31 | *** 32 | 33 | ## Methods 34 | 35 | ### __construct 36 | 37 | ```php 38 | public __construct(string $name): mixed 39 | ``` 40 | 41 | **Parameters:** 42 | 43 | | Parameter | Type | Description | 44 | |-----------|------------|-------------| 45 | | `$name` | **string** | | 46 | 47 | *** 48 | -------------------------------------------------------------------------------- /tests/Functional/expected/classes/PhpDocumentorMarkdown/Example/Pizza/Sauce.md: -------------------------------------------------------------------------------- 1 | # Sauce 2 | 3 | Pizza sauce class. 4 | 5 | *** 6 | 7 | * Full name: `\PhpDocumentorMarkdown\Example\Pizza\Sauce` 8 | 9 | ## Properties 10 | 11 | ### name 12 | 13 | Sauce name. 14 | 15 | ```php 16 | protected string $name 17 | ``` 18 | 19 | *** 20 | 21 | ### tomatoSupplier 22 | 23 | Tomato supplier. 24 | 25 | ```php 26 | public static string $tomatoSupplier 27 | ``` 28 | 29 | * This property is **static**. 30 | 31 | *** 32 | 33 | ## Methods 34 | 35 | ### __construct 36 | 37 | ```php 38 | public __construct(string $name): mixed 39 | ``` 40 | 41 | **Parameters:** 42 | 43 | | Parameter | Type | Description | 44 | |-----------|------------|-------------| 45 | | `$name` | **string** | | 46 | 47 | *** 48 | -------------------------------------------------------------------------------- /src/Example/ProductInterface.php: -------------------------------------------------------------------------------- 1 | getenv('MYSQL_DATABASE') ?: 'pizza', 27 | 'DB_USER' => getenv('MYSQL_USER'), 28 | 'DB_PASSWORD' => getenv('MYSQL_PASSWORD'), 29 | 'DB_HOST' => getenv('MYSQL_HOST'), 30 | ]; 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | test: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Set up PHP with Xdebug 21 | uses: shivammathur/setup-php@v2 22 | with: 23 | php-version: '8.2' 24 | coverage: xdebug 25 | 26 | - name: Validate composer.json and composer.lock 27 | run: composer validate --strict 28 | 29 | - name: Cache Composer packages 30 | id: composer-cache 31 | uses: actions/cache@v3 32 | with: 33 | path: vendor 34 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 35 | restore-keys: | 36 | ${{ runner.os }}-php- 37 | 38 | - name: Install dependencies 39 | run: composer install --prefer-dist --no-progress 40 | 41 | - name: Run test suite 42 | run: vendor/bin/phpunit 43 | -------------------------------------------------------------------------------- /themes/markdown/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | -------------------------------------------------------------------------------- /tests/Unit/Twig/templates/macros.test.twig: -------------------------------------------------------------------------------- 1 | {% import (relativeIncludePath ~ '/macros.twig') as macros %} 2 | {% if key == 'mdEsc' %} 3 | {{- macros.mdEsc(args[0]) -}} 4 | {% endif %} 5 | {% if key == 'mdGetRelativePath' %} 6 | {{- macros.mdGetRelativePath(args[0], args[1]) -}} 7 | {% endif %} 8 | {% if key == 'mdNodePath' %} 9 | {{- macros.mdNodePath(args[0]) -}} 10 | {% endif %} 11 | {% if key == 'mdClassPath' %} 12 | {{- macros.mdClassPath(args[0]) -}} 13 | {% endif %} 14 | {% if key == 'mdFunctionPath' %} 15 | {{- macros.mdFunctionPath(args[0]) -}} 16 | {% endif %} 17 | {% if key == 'mdLink' %} 18 | {{- macros.mdLink(args[0], args[1], args[2], args[3]) -}} 19 | {% endif %} 20 | {% if key == 'mdRepeat' %} 21 | {{- macros.mdRepeat(args[0], args[1]) -}} 22 | {% endif %} 23 | {% if key == 'mdPadRight' %} 24 | {{- macros.mdPadRight(args[0], args[1]) -}} 25 | {% endif %} 26 | {% if key == 'mdTable' %} 27 | {{- macros.mdTable(args[0], args[1]) -}} 28 | {% endif %} 29 | {% if key == 'mdNestingLevel' %} 30 | {{- macros.mdNestingLevel(args[0]) -}} 31 | {% endif %} 32 | -------------------------------------------------------------------------------- /phpdoc.dist.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | Pizza Place (Example documentation) 8 | 9 | .wiki 10 | 11 | 12 | 13 | 14 | src/Example 15 | 16 | 17 | 18 | 28 | -------------------------------------------------------------------------------- /themes/markdown/property.md.twig: -------------------------------------------------------------------------------- 1 | {% import 'include/macros.twig' as macros %} 2 | {% autoescape false %} 3 | ### {{ property.name }} 4 | {% if property.summary | trim %} 5 | 6 | {{ macros.mdEsc(property.summary) | raw }} 7 | {% endif %} 8 | 9 | ```php 10 | {{ property.visibility ~ ' ' }}{% if property.static %}{{ 'static' ~ ' ' }}{% endif %}{% if property.type and property.type is not empty %}{{ property.type ~ ' ' }}{% endif %}{{ '$' ~ property.name }} 11 | ``` 12 | {% include 'partials/description.md.twig' with { node: property } %} 13 | {% if property.static or property.deprecated %} 14 | 15 | {% if property.static %}* This property is **static**. 16 | {% endif %} 17 | {% if property.deprecated %}* **Warning:** this property is **deprecated**. This means that this property will likely be removed in a future version. 18 | {% endif %} 19 | {% endif %} 20 | {% if property.type and property.type.classes %} 21 | 22 | **Type:** {% for typeClass in property.type.classes %}{{ macros.mdLink(typeClass, path, null, 'class') }}{% if not loop.last %} | {% endif %}{% endfor %} 23 | {% endif %} 24 | {% include 'partials/tags.md.twig' with { tags: property.tags } only %} 25 | 26 | *** 27 | {% endautoescape %} -------------------------------------------------------------------------------- /tests/Unit/Twig/UnitTestCase.php: -------------------------------------------------------------------------------- 1 | testTemplatePath = __DIR__ . '/templates'; 24 | $this->relativeIncludePath = 'themes/markdown/include'; 25 | } 26 | 27 | /** 28 | * Get the path to test templates. 29 | * 30 | * @return string 31 | */ 32 | public function getTestTemplatePath(): string 33 | { 34 | return $this->testTemplatePath; 35 | } 36 | 37 | /** 38 | * Get the relative path to production template include dir. 39 | * 40 | * @return string 41 | */ 42 | public function getRelativeIncludePath(): string 43 | { 44 | return $this->relativeIncludePath; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright: (c) 2022-present Sakri Koskimies 4 | (c) 2020 Dejan Markic 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/Example/Pizza/Base.php: -------------------------------------------------------------------------------- 1 | sauce = $sauce; 37 | $this->yeast = $yeast; 38 | } 39 | 40 | /** 41 | * @return Sauce 42 | */ 43 | public function getSauce(): Sauce 44 | { 45 | return $this->sauce; 46 | } 47 | 48 | /** 49 | * @return int 50 | * @throws Exception 51 | */ 52 | public function getYeast(): int 53 | { 54 | return $this->yeast; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.wiki/classes/PhpDocumentorMarkdown/Example/ProductInterface.md: -------------------------------------------------------------------------------- 1 | # ProductInterface 2 | 3 | Interface for a product. 4 | 5 | *** 6 | 7 | * Full name: `\PhpDocumentorMarkdown\Example\ProductInterface` 8 | * Parent interfaces: 9 | [`\PhpDocumentorMarkdown\Example\Arrayable`](./Arrayable.md) 10 | 11 | ## Methods 12 | 13 | ### __construct 14 | 15 | ```php 16 | public __construct(string $name, float $price): mixed 17 | ``` 18 | 19 | **Parameters:** 20 | 21 | | Parameter | Type | Description | 22 | |-----------|------------|----------------| 23 | | `$name` | **string** | Product name. | 24 | | `$price` | **float** | Product price. | 25 | 26 | *** 27 | 28 | ### getName 29 | 30 | Get the name of the product. 31 | 32 | ```php 33 | public getName(): string 34 | ``` 35 | 36 | *** 37 | 38 | ### getPrice 39 | 40 | Get the price of the product. 41 | 42 | ```php 43 | public getPrice(): float 44 | ``` 45 | 46 | *** 47 | 48 | ### getTaxRate 49 | 50 | Get the tax rate for this product. 51 | 52 | ```php 53 | public getTaxRate(): float 54 | ``` 55 | 56 | *** 57 | 58 | ## Inherited methods 59 | 60 | ### toArray 61 | 62 | Get the instance as an array. 63 | 64 | ```php 65 | public toArray(): array 66 | ``` 67 | 68 | *** 69 | -------------------------------------------------------------------------------- /tests/Functional/expected/classes/PhpDocumentorMarkdown/Example/ProductInterface.md: -------------------------------------------------------------------------------- 1 | # ProductInterface 2 | 3 | Interface for a product. 4 | 5 | *** 6 | 7 | * Full name: `\PhpDocumentorMarkdown\Example\ProductInterface` 8 | * Parent interfaces: 9 | [`\PhpDocumentorMarkdown\Example\Arrayable`](./Arrayable) 10 | 11 | ## Methods 12 | 13 | ### __construct 14 | 15 | ```php 16 | public __construct(string $name, float $price): mixed 17 | ``` 18 | 19 | **Parameters:** 20 | 21 | | Parameter | Type | Description | 22 | |-----------|------------|----------------| 23 | | `$name` | **string** | Product name. | 24 | | `$price` | **float** | Product price. | 25 | 26 | *** 27 | 28 | ### getName 29 | 30 | Get the name of the product. 31 | 32 | ```php 33 | public getName(): string 34 | ``` 35 | 36 | *** 37 | 38 | ### getPrice 39 | 40 | Get the price of the product. 41 | 42 | ```php 43 | public getPrice(): float 44 | ``` 45 | 46 | *** 47 | 48 | ### getTaxRate 49 | 50 | Get the tax rate for this product. 51 | 52 | ```php 53 | public getTaxRate(): float 54 | ``` 55 | 56 | *** 57 | 58 | ## Inherited methods 59 | 60 | ### toArray 61 | 62 | Get the instance as an array. 63 | 64 | ```php 65 | public toArray(): array 66 | ``` 67 | 68 | *** 69 | -------------------------------------------------------------------------------- /.wiki/classes/PhpDocumentorMarkdown/Example/ManyInterfaces.md: -------------------------------------------------------------------------------- 1 | # ManyInterfaces 2 | 3 | A ManyInterfaces 4 | 5 | ManyInterfaces description 6 | 7 | - **See:** \PhpDocumentorMarkdown\Example\AbstractProduct 8 | 9 | *** 10 | 11 | * Full name: `\PhpDocumentorMarkdown\Example\ManyInterfaces` 12 | * Parent interfaces: 13 | [`\PhpDocumentorMarkdown\Example\ProductInterface`](./ProductInterface.md), 14 | `JsonSerializable` 15 | 16 | ## Inherited methods 17 | 18 | ### toArray 19 | 20 | Get the instance as an array. 21 | 22 | ```php 23 | public toArray(): array 24 | ``` 25 | 26 | *** 27 | 28 | ### __construct 29 | 30 | ```php 31 | public __construct(string $name, float $price): mixed 32 | ``` 33 | 34 | **Parameters:** 35 | 36 | | Parameter | Type | Description | 37 | |-----------|------------|----------------| 38 | | `$name` | **string** | Product name. | 39 | | `$price` | **float** | Product price. | 40 | 41 | *** 42 | 43 | ### getName 44 | 45 | Get the name of the product. 46 | 47 | ```php 48 | public getName(): string 49 | ``` 50 | 51 | *** 52 | 53 | ### getPrice 54 | 55 | Get the price of the product. 56 | 57 | ```php 58 | public getPrice(): float 59 | ``` 60 | 61 | *** 62 | 63 | ### getTaxRate 64 | 65 | Get the tax rate for this product. 66 | 67 | ```php 68 | public getTaxRate(): float 69 | ``` 70 | 71 | *** 72 | -------------------------------------------------------------------------------- /tests/Functional/expected/classes/PhpDocumentorMarkdown/Example/ManyInterfaces.md: -------------------------------------------------------------------------------- 1 | # ManyInterfaces 2 | 3 | A ManyInterfaces 4 | 5 | ManyInterfaces description 6 | 7 | - **See:** \PhpDocumentorMarkdown\Example\AbstractProduct 8 | 9 | *** 10 | 11 | * Full name: `\PhpDocumentorMarkdown\Example\ManyInterfaces` 12 | * Parent interfaces: 13 | [`\PhpDocumentorMarkdown\Example\ProductInterface`](./ProductInterface), 14 | `JsonSerializable` 15 | 16 | ## Inherited methods 17 | 18 | ### toArray 19 | 20 | Get the instance as an array. 21 | 22 | ```php 23 | public toArray(): array 24 | ``` 25 | 26 | *** 27 | 28 | ### __construct 29 | 30 | ```php 31 | public __construct(string $name, float $price): mixed 32 | ``` 33 | 34 | **Parameters:** 35 | 36 | | Parameter | Type | Description | 37 | |-----------|------------|----------------| 38 | | `$name` | **string** | Product name. | 39 | | `$price` | **float** | Product price. | 40 | 41 | *** 42 | 43 | ### getName 44 | 45 | Get the name of the product. 46 | 47 | ```php 48 | public getName(): string 49 | ``` 50 | 51 | *** 52 | 53 | ### getPrice 54 | 55 | Get the price of the product. 56 | 57 | ```php 58 | public getPrice(): float 59 | ``` 60 | 61 | *** 62 | 63 | ### getTaxRate 64 | 65 | Get the tax rate for this product. 66 | 67 | ```php 68 | public getTaxRate(): float 69 | ``` 70 | 71 | *** 72 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "saggre/phpdocumentor-markdown", 3 | "description": "Markdown template for phpDocumentor3", 4 | "license": "MIT", 5 | "type": "library", 6 | "authors": [ 7 | { 8 | "name": "Saggre", 9 | "email": "sakri.koskimies@hotmail.com" 10 | } 11 | ], 12 | "keywords": [ 13 | "phpdocumentor", 14 | "phpdoc", 15 | "markdown", 16 | "ci", 17 | "ai", 18 | "documentation" 19 | ], 20 | "require": { 21 | }, 22 | "require-dev": { 23 | "php": ">=8.1", 24 | "ext-json": "*", 25 | "cweagans/composer-patches": "^1.7", 26 | "phpdocumentor/phpdocumentor": "3.8.0", 27 | "phpunit/phpunit": "^10", 28 | "symfony/filesystem": "^6.4" 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "PhpDocumentorMarkdown\\": "src/" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "PhpDocumentorMarkdown\\Test\\": "tests/" 38 | } 39 | }, 40 | "scripts": { 41 | "test": "vendor/bin/phpunit", 42 | "create-docs": "phpdoc", 43 | "create-docs:debug": "phpdoc -vvv" 44 | }, 45 | "config": { 46 | "sort-packages": true, 47 | "allow-plugins": { 48 | "cweagans/composer-patches": true 49 | } 50 | }, 51 | "extra": { 52 | "patches": { 53 | "phpdocumentor/phpdocumentor": [ 54 | "./patches/phpdocumentor-resource-path.patch" 55 | ] 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /themes/markdown/function.md.twig: -------------------------------------------------------------------------------- 1 | {% import 'include/macros.twig' as macros %} 2 | {% autoescape false %} 3 | {% block content %} 4 | {% include 'partials/header.md.twig' with { 5 | node: node, 6 | titleSuffix: '()' 7 | } %} 8 | 9 | *** 10 | 11 | {% if node.deprecated %}* **Warning:** this function is **deprecated**. This means that this function will likely be removed in a future version.{% endif %} 12 | * Full name: `{{ node.name }}` 13 | * Defined in: `{{ node.file.path }}` 14 | {% include 'partials/tags.md.twig' with { tags: node.tags } only %} 15 | 16 | ## Parameters 17 | 18 | {% if node.arguments is not empty %} 19 | {% set rows = [] %} 20 | {% for argument in node.arguments %} 21 | {% set rows = rows | merge([{ 22 | parameter: '`$' ~ argument.name ~ '`', 23 | type: argument.type ? '**' ~ argument.type ~ '**' : '', 24 | description: argument.description 25 | }]) %} 26 | {% endfor %} 27 | {{ macros.mdTable(rows, [ 28 | { label: 'Parameter', key: 'parameter' }, 29 | { label: 'Type', key: 'type' }, 30 | { label: 'Description', key: 'description' } 31 | ]) -}} 32 | {% else %} 33 | This function has no parameters. 34 | {% endif %} 35 | 36 | ## Return Value 37 | {% if node.response %} 38 | {% if node.response.type | trim %} 39 | 40 | **{{ node.response.type }}** 41 | {% endif %} 42 | {% if node.response.description | trim %} 43 | 44 | {{ node.response.description | raw }} 45 | {% endif %} 46 | {% else %} 47 | This function does not return a value. 48 | 49 | {% endif %} 50 | {% if parameter.includeFooter == 'yes' %} 51 | {% include 'partials/footer.md.twig' %} 52 | {% endif %} 53 | {% endblock %} 54 | {% endautoescape %} -------------------------------------------------------------------------------- /.wiki/classes/PhpDocumentorMarkdown/Example/Pizza/Base.md: -------------------------------------------------------------------------------- 1 | # Base 2 | 3 | Represents a pizza base. 4 | 5 | *** 6 | 7 | * Full name: `\PhpDocumentorMarkdown\Example\Pizza\Base` 8 | 9 | ## Constants 10 | 11 | | Constant | Visibility | Type | Value | 12 | |---------------------------|------------|------|-------| 13 | | `YEAST_SOURDOUGH_STARTER` | private | | 0b1 | 14 | | `YEAST_FRESH` | public | | 0b10 | 15 | | `YEAST_ACTIVE_DRY` | public | | 0b11 | 16 | 17 | ## Properties 18 | 19 | ### sauce 20 | 21 | The sauce used. 22 | 23 | ```php 24 | protected \PhpDocumentorMarkdown\Example\Pizza\Sauce $sauce 25 | ``` 26 | 27 | *** 28 | 29 | ### yeast 30 | 31 | Type of yeast used. 32 | 33 | ```php 34 | protected int $yeast 35 | ``` 36 | 37 | *** 38 | 39 | ## Methods 40 | 41 | ### __construct 42 | 43 | ```php 44 | public __construct(\PhpDocumentorMarkdown\Example\Pizza\Sauce $sauce, int $yeast = self::YEAST_SOURDOUGH_STARTER): mixed 45 | ``` 46 | 47 | **Parameters:** 48 | 49 | | Parameter | Type | Description | 50 | |-----------|------------------------------------------------|-------------| 51 | | `$sauce` | **\PhpDocumentorMarkdown\Example\Pizza\Sauce** | | 52 | | `$yeast` | **int** | | 53 | 54 | *** 55 | 56 | ### getSauce 57 | 58 | ```php 59 | public getSauce(): \PhpDocumentorMarkdown\Example\Pizza\Sauce 60 | ``` 61 | 62 | *** 63 | 64 | ### getYeast 65 | 66 | ```php 67 | public getYeast(): int 68 | ``` 69 | 70 | **Throws:** 71 | 72 | - [`Exception`](../../../Exception.md) 73 | 74 | *** 75 | -------------------------------------------------------------------------------- /tests/Functional/expected/classes/PhpDocumentorMarkdown/Example/Pizza/Base.md: -------------------------------------------------------------------------------- 1 | # Base 2 | 3 | Represents a pizza base. 4 | 5 | *** 6 | 7 | * Full name: `\PhpDocumentorMarkdown\Example\Pizza\Base` 8 | 9 | ## Constants 10 | 11 | | Constant | Visibility | Type | Value | 12 | |---------------------------|------------|------|-------| 13 | | `YEAST_SOURDOUGH_STARTER` | private | | 0b1 | 14 | | `YEAST_FRESH` | public | | 0b10 | 15 | | `YEAST_ACTIVE_DRY` | public | | 0b11 | 16 | 17 | ## Properties 18 | 19 | ### sauce 20 | 21 | The sauce used. 22 | 23 | ```php 24 | protected \PhpDocumentorMarkdown\Example\Pizza\Sauce $sauce 25 | ``` 26 | 27 | *** 28 | 29 | ### yeast 30 | 31 | Type of yeast used. 32 | 33 | ```php 34 | protected int $yeast 35 | ``` 36 | 37 | *** 38 | 39 | ## Methods 40 | 41 | ### __construct 42 | 43 | ```php 44 | public __construct(\PhpDocumentorMarkdown\Example\Pizza\Sauce $sauce, int $yeast = self::YEAST_SOURDOUGH_STARTER): mixed 45 | ``` 46 | 47 | **Parameters:** 48 | 49 | | Parameter | Type | Description | 50 | |-----------|------------------------------------------------|-------------| 51 | | `$sauce` | **\PhpDocumentorMarkdown\Example\Pizza\Sauce** | | 52 | | `$yeast` | **int** | | 53 | 54 | *** 55 | 56 | ### getSauce 57 | 58 | ```php 59 | public getSauce(): \PhpDocumentorMarkdown\Example\Pizza\Sauce 60 | ``` 61 | 62 | *** 63 | 64 | ### getYeast 65 | 66 | ```php 67 | public getYeast(): int 68 | ``` 69 | 70 | **Throws:** 71 | 72 | - [`Exception`](../../../Exception) 73 | 74 | *** 75 | -------------------------------------------------------------------------------- /tests/Unit/Twig/Macro/MacroTestCase.php: -------------------------------------------------------------------------------- 1 | loadMacroTemplate(); 24 | } catch (Throwable $e) { 25 | self::fail($e->getMessage()); 26 | } 27 | } 28 | 29 | /** 30 | * Load the template. 31 | * 32 | * @return void 33 | * @throws RuntimeError 34 | * @throws LoaderError 35 | * 36 | * @throws SyntaxError 37 | */ 38 | protected function loadMacroTemplate(): void 39 | { 40 | // Allow Twig to load files from both /tests and /themes 41 | $loader = new FilesystemLoader(ROOT_DIR); 42 | $twig = new Environment($loader); 43 | 44 | $this->template = $twig->load('tests/Unit/Twig/templates/macros.test.twig'); 45 | } 46 | 47 | /** 48 | * @param string $key 49 | * @param array $args 50 | * 51 | * @return string Macro output 52 | */ 53 | protected function renderTemplate(string $key, array $args): string 54 | { 55 | $variables = [ 56 | 'relativeIncludePath' => $this->getRelativeIncludePath(), 57 | 'key' => $key, 58 | 'args' => $args, 59 | ]; 60 | 61 | return $this->template->render($variables); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/Functional/FunctionalTestCase.php: -------------------------------------------------------------------------------- 1 | runPhpDocWithDir(self::getProjectRootPath() . '/src/Example'); 22 | } 23 | 24 | public function getMarkdownGenerator(): MarkdownGeneratorService 25 | { 26 | return self::$markdownGenerator; 27 | } 28 | 29 | final public static function assertMarkdownFileEquals(string $expectedPath, string $actualPath, $message = ''): void 30 | { 31 | $expected = file_get_contents($expectedPath); 32 | $actual = self::$markdownGenerator->loadContents($actualPath); 33 | self::assertMarkdownEquals($expected, $actual, $message); 34 | } 35 | 36 | final public static function assertMarkdownEquals(string $expected, string $actual, $message = ''): void 37 | { 38 | self::assertEquals( 39 | $expected, 40 | $actual, 41 | $message ?: 'Markdown does not match expected output.' 42 | ); 43 | } 44 | 45 | public static function tearDownAfterClass(): void 46 | { 47 | parent::tearDownAfterClass(); 48 | self::$markdownGenerator->cleanup(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Functional/PhpdocOutputTest.php: -------------------------------------------------------------------------------- 1 | 'Home.md', 12 | ), 13 | array( 14 | 'path' => 'classes/PhpDocumentorMarkdown/Example/AbstractProduct.md', 15 | ), 16 | array( 17 | 'path' => 'classes/PhpDocumentorMarkdown/Example/Arrayable.md', 18 | ), 19 | array( 20 | 'path' => 'classes/PhpDocumentorMarkdown/Example/Pizza.md', 21 | ), 22 | array( 23 | 'path' => 'classes/PhpDocumentorMarkdown/Example/ProductInterface.md', 24 | ), 25 | array( 26 | 'path' => 'classes/PhpDocumentorMarkdown/Example/ReviewableTrait.md', 27 | ), 28 | array( 29 | 'path' => 'classes/PhpDocumentorMarkdown/Example/ManyInterfaces.md', 30 | ), 31 | array( 32 | 'path' => 'classes/PhpDocumentorMarkdown/Example/Pizza/Base.md', 33 | ), 34 | array( 35 | 'path' => 'classes/PhpDocumentorMarkdown/Example/Pizza/Sauce.md', 36 | ), 37 | array( 38 | 'path' => 'functions/getDatabaseConfig.md', 39 | ), 40 | array( 41 | 'path' => 'functions/mockFunctionWithParameters.md', 42 | ), 43 | ); 44 | } 45 | 46 | /** 47 | * @dataProvider dataProviderTestFiles 48 | */ 49 | public function testFiles(string $path) 50 | { 51 | self::assertMarkdownFileEquals( 52 | __DIR__ . "/expected/$path", 53 | $path, 54 | "Markdown at $path does not match expected output" 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Example/Pizza.php: -------------------------------------------------------------------------------- 1 | name = $name; 50 | $this->price = $price; 51 | $this->base = $base; 52 | } 53 | 54 | /** 55 | * Get the name of the product. 56 | * 57 | * @return string The name of the product. 58 | */ 59 | public function getName(): string 60 | { 61 | return $this->name; 62 | } 63 | 64 | /** 65 | * Get the price of the product. 66 | * 67 | * @return float 68 | */ 69 | public function getPrice(): float 70 | { 71 | return $this->price; 72 | } 73 | 74 | public function jsonSerialize() 75 | { 76 | // TODO: Implement jsonSerialize() method. 77 | } 78 | 79 | public function toArray(): array 80 | { 81 | // TODO: Implement toArray() method. 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /themes/markdown/trait.md.twig: -------------------------------------------------------------------------------- 1 | {% import 'include/macros.twig' as macros %} 2 | {% autoescape false %} 3 | {% block content %} 4 | {% include 'partials/header.md.twig' with { node: node } %} 5 | 6 | *** 7 | 8 | * Full name: `{{ node.FullyQualifiedStructuralElementName }}` 9 | {% if node.parent and node.parent is not empty %}* Parent trait: {{ macros.mdLink(node.parent, macros.mdClassPath(node), node.parent.FullyQualifiedStructuralElementName, 'class') }} 10 | {% endif %} 11 | {% if node.final %}* This trait is marked as **final** and can't be subclassed 12 | {% endif %} 13 | {% if node.deprecated %}* **Warning:** this trait is **deprecated**. This means that this class will likely be removed in a future version. 14 | {% endif %} 15 | {% if node.interfaces is not empty %}* This trait implements: 16 | {% include 'partials/inheritance.md.twig' with { node: node, others: node.interfaces } only %} 17 | 18 | {% endif %} 19 | {% include 'partials/tags.md.twig' with { tags: node.tags } only %} 20 | {% if node.constants | length > 0 %} 21 | 22 | ## Constants 23 | 24 | {% set constants = [] %} 25 | {% for constant in node.constants %} 26 | {% set constants = constants | merge([ 27 | { 28 | name: '`' ~ constant.name ~ '`', 29 | visibility: constant.visibility | default('*default*'), 30 | type: constant.type ? constant.type : '', 31 | value: constant.value 32 | } 33 | ]) %} 34 | {% endfor %} 35 | {{ macros.mdTable(constants, [ 36 | { label: 'Constant', key: 'name' }, 37 | { label: 'Visibility', key: 'visibility' }, 38 | { label: 'Type', key: 'type' }, 39 | { label: 'Value', key: 'value' }, 40 | ]) -}} 41 | {% endif %} 42 | {% if node.properties | length > 0 %} 43 | 44 | ## Properties 45 | 46 | {% for property in node.properties %} 47 | {% include 'property.md.twig' %} 48 | {% endfor %} 49 | {% endif %} 50 | {% if node.methods | length > 0 %} 51 | 52 | ## Methods 53 | 54 | {% for method in node.methods %} 55 | {% include 'method.md.twig' %} 56 | {% endfor %} 57 | {% endif %} 58 | {% if parameter.includeFooter == 'yes' %} 59 | {% include 'partials/footer.md.twig' %} 60 | {% endif %} 61 | {% endblock %} 62 | {% endautoescape %} -------------------------------------------------------------------------------- /themes/markdown/interface.md.twig: -------------------------------------------------------------------------------- 1 | {% import 'include/macros.twig' as macros %} 2 | {% autoescape false %} 3 | {% block content %} 4 | {% include 'partials/header.md.twig' with { node: node } %} 5 | 6 | *** 7 | 8 | * Full name: `{{ node.FullyQualifiedStructuralElementName }}` 9 | {% if node.parent and node.parent is not empty %}* Parent interfaces: 10 | {% include 'partials/inheritance.md.twig' with { node: node, others: node.parent } only %} 11 | 12 | {% endif %} 13 | {% if node.final %}* This interface is marked as **final** and can't be subclassed 14 | {% endif %} 15 | {% if node.deprecated %}* **Warning:** this interface is **deprecated**. This means that this interface will likely be removed in a future version. 16 | {% endif %} 17 | {% if node.interfaces is not empty %}* This interface implements: 18 | {% include 'partials/inheritance.md.twig' with { node: node, others: node.interfaces } only %} 19 | 20 | {% endif %} 21 | {% include 'partials/tags.md.twig' with { tags: node.tags } only %} 22 | {% if node.constants | length > 0 %} 23 | 24 | ## Constants 25 | 26 | {% set constants = [] %} 27 | {% for constant in node.constants %} 28 | {% set constants = constants | merge([ 29 | { 30 | name: '`' ~ constant.name ~ '`', 31 | visibility: constant.visibility | default('*default*'), 32 | type: constant.type ? macros.mdEsc(constant.type) : '', 33 | value: constant.value 34 | } 35 | ]) %} 36 | {% endfor %} 37 | 38 | {{ macros.mdTable(constants, [ 39 | { label: 'Constant', key: 'name' }, 40 | { label: 'Visibility', key: 'visibility' }, 41 | { label: 'Type', key: 'type' }, 42 | { label: 'Value', key: 'value' }, 43 | ]) -}} 44 | {%- endif %} 45 | {% if node.methods | length > 0 %} 46 | 47 | ## Methods 48 | 49 | {% for method in node.methods -%} 50 | {% if loop.index0 > 0 %}{{ '\n' }}{% endif %}{% include 'method.md.twig' %} 51 | {% endfor %} 52 | {% endif %} 53 | {% if node.InheritedMethods | length > 0 %} 54 | 55 | ## Inherited methods 56 | 57 | {% for method in node.InheritedMethods -%} 58 | {% if loop.index0 > 0 %}{{ '\n' }}{% endif %}{% include 'method.md.twig' %} 59 | {% endfor %} 60 | {% endif %} 61 | {% if parameter.includeFooter == 'yes' %} 62 | {% include 'partials/footer.md.twig' %} 63 | {% endif %} 64 | {% endblock %} 65 | {% endautoescape %} -------------------------------------------------------------------------------- /tests/Functional/Service/MarkdownGeneratorService.php: -------------------------------------------------------------------------------- 1 | projectRootPath = $projectRootPath; 20 | $this->filesystem = new Filesystem(); 21 | $this->workingDir = tempnam(sys_get_temp_dir(), 'phpdoc'); 22 | $this->filesystem->remove($this->workingDir); 23 | 24 | if (!mkdir($concurrentDirectory = $this->workingDir) && !is_dir($concurrentDirectory)) { 25 | throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory)); 26 | } 27 | } 28 | 29 | public function cleanup(): void 30 | { 31 | $this->filesystem->remove($this->workingDir); 32 | } 33 | 34 | protected function getProjectRootPath(): string 35 | { 36 | return $this->projectRootPath; 37 | } 38 | 39 | protected function getPhpDocBinaryPath(): string 40 | { 41 | return "{$this->getProjectRootPath()}/vendor/bin/phpdoc"; 42 | } 43 | 44 | public function runPhpDocWithDir(string $path, array $arguments = []): Process 45 | { 46 | if (!is_dir($path)) { 47 | throw new InvalidArgumentException(sprintf('The path "%s" is not a directory.', $path)); 48 | } 49 | 50 | $this->filesystem->mirror($path, "$this->workingDir/test"); 51 | 52 | $process = new Process( 53 | array_merge( 54 | [ 55 | PHP_BINARY, 56 | $this->getPhpDocBinaryPath(), 57 | '-vvv', 58 | "--config={$this->getProjectRootPath()}/phpdoc.dist.xml", 59 | '--force', 60 | "--target=$this->workingDir/output", 61 | ], 62 | $arguments 63 | ), 64 | $this->getProjectRootPath() 65 | ); 66 | 67 | return $process->mustRun(); 68 | } 69 | 70 | public function loadContents($filename): string 71 | { 72 | return file_get_contents($this->workingDir . '/output/' . $filename); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /themes/markdown/method.md.twig: -------------------------------------------------------------------------------- 1 | {% import 'include/macros.twig' as macros %} 2 | {% autoescape false %} 3 | ### {{ method.name }} 4 | {% if method.summary | trim %} 5 | 6 | {{ macros.mdEsc(method.summary) | raw }} 7 | {% endif %} 8 | 9 | {# Method signature #} 10 | ```php 11 | {% if method.final %}{{ 'final' ~ ' ' }}{% endif %}{{ method.visibility ~ ' ' }}{% if method.static %}{{ 'static' ~ ' ' }}{% endif %}{{ method.name }}({% for argument in method.arguments %} 12 | {{- argument.type }} 13 | {{- argument.byReference ? '&' }} $ 14 | {{- argument.name }}{{ argument.default ? ' = ' ~ argument.default | raw }} 15 | {%- if not loop.last %}, {% endif %} 16 | {%- endfor %}) 17 | {{- method.response.type ? ': ' ~ method.response.type }} 18 | ``` 19 | {% include 'partials/description.md.twig' with { node: method } %} 20 | {% if method.static or method.abstract or method.final or method.deprecated %} 21 | 22 | {% if method.static %}* This method is **static**.{% endif -%} 23 | {% if method.abstract %}* This method is **abstract**.{% endif -%} 24 | {% if method.final %}* This method is **final**.{% endif -%} 25 | {% if method.deprecated %}* **Warning:** this method is **deprecated**. This means that this method will likely be removed in a future version.{% endif -%} 26 | {% endif -%} 27 | {%- if method.arguments is not empty %} 28 | 29 | **Parameters:** 30 | 31 | {% set parameters = [] %} 32 | {% for argument in method.arguments %} 33 | {% set parameters = parameters | merge([ 34 | { 35 | name: '`$' ~ argument.name ~ '`', 36 | type: argument.type ? '**' ~ argument.type ~ '**' : '', 37 | description: argument.description | raw 38 | } 39 | ]) %} 40 | {% endfor -%} 41 | {{ macros.mdTable(parameters, [ 42 | { label: 'Parameter', key: 'name' }, 43 | { label: 'Type', key: 'type' }, 44 | { label: 'Description', key: 'description' }, 45 | ]) }} 46 | {%- endif -%} 47 | {% if method.response.description and method.response.description is not empty %} 48 | 49 | **Return Value:** 50 | {% include 'partials/description.md.twig' with { node: method.response } %} 51 | {% endif %} 52 | {% if method.tags.throws | length > 0 or method.tags.throw | length > 0 %} 53 | 54 | **Throws:** 55 | 56 | {% for exception in method.tags.throws %} 57 | {% if exception.description | trim %} 58 | {{ exception.description | raw }} 59 | {% endif %} 60 | - {{ macros.mdLink(exception.type | raw, macros.mdClassPath(node), exception.type | shortFQSEN, 'class' ) }} 61 | {% endfor %} 62 | {% endif -%} 63 | {% include 'partials/tags.md.twig' with { tags: method.tags } only %} 64 | 65 | *** 66 | {% endautoescape %} -------------------------------------------------------------------------------- /themes/markdown/class.md.twig: -------------------------------------------------------------------------------- 1 | {% import 'include/macros.twig' as macros %} 2 | {% autoescape false %} 3 | {% block content %} 4 | {% include 'partials/header.md.twig' with { node: node } %} 5 | 6 | *** 7 | 8 | * Full name: `{{ node.FullyQualifiedStructuralElementName }}` 9 | {% if node.parent and node.parent is not empty %}* Parent class: {{ macros.mdLink(node.parent, macros.mdClassPath(node), node.parent.FullyQualifiedStructuralElementName, 'class') }} 10 | {% endif %} 11 | {% if node.final %}* This class is marked as **final** and can't be subclassed 12 | {% endif %} 13 | {% if node.deprecated %}* **Warning:** this class is **deprecated**. This means that this class will likely be removed in a future version. 14 | {% endif %} 15 | {% if node.interfaces is not empty %}* This class implements: 16 | {% include 'partials/inheritance.md.twig' with { node: node, others: node.interfaces } only %} 17 | 18 | {% endif %} 19 | {% if node.abstract %}* This class is an **Abstract class** 20 | {% endif %} 21 | {% if node.final %}* This class is a **Final class** 22 | {% endif %} 23 | {% include 'partials/tags.md.twig' with { tags: node.tags } only %} 24 | {% if node.constants | length > 0 %} 25 | 26 | ## Constants 27 | 28 | {% set constants = [] %} 29 | {% for constant in node.constants %} 30 | {% set constants = constants | merge([ 31 | { 32 | name: '`' ~ constant.name ~ '`', 33 | visibility: constant.visibility | default('*default*'), 34 | type: constant.type ? constant.type : '', 35 | value: constant.value 36 | } 37 | ]) %} 38 | {% endfor %} 39 | {{ macros.mdTable(constants, [ 40 | { label: 'Constant', key: 'name' }, 41 | { label: 'Visibility', key: 'visibility' }, 42 | { label: 'Type', key: 'type' }, 43 | { label: 'Value', key: 'value' }, 44 | ]) -}} 45 | {% endif %} 46 | {% if node.properties | length > 0 %} 47 | 48 | ## Properties 49 | 50 | {% for property in node.properties %} 51 | {% if loop.index0 > 0 %}{{ '\n' }}{% endif %}{% include 'property.md.twig' %} 52 | {% endfor %} 53 | {% endif %} 54 | {% if node.methods | length > 0 %} 55 | 56 | ## Methods 57 | 58 | {% for method in node.methods %} 59 | {% if loop.index0 > 0 %}{{ '\n' }}{% endif %}{% include 'method.md.twig' %} 60 | {% endfor %} 61 | {% endif %} 62 | {% if node.InheritedMethods | length > 0 %} 63 | 64 | ## Inherited methods 65 | 66 | {% for method in node.InheritedMethods %} 67 | {% if loop.index0 > 0 %}{{ '\n' }}{% endif %}{% include 'method.md.twig' %} 68 | {% endfor %} 69 | {% endif %} 70 | {% if parameter.includeFooter == 'yes' %} 71 | {% include 'partials/footer.md.twig' %} 72 | {% endif %} 73 | {% endblock %} 74 | {% endautoescape %} -------------------------------------------------------------------------------- /.wiki/classes/PhpDocumentorMarkdown/Example/Pizza.md: -------------------------------------------------------------------------------- 1 | # Pizza 2 | 3 | A pizza \| pie's. 4 | 5 | *** 6 | 7 | * Full name: `\PhpDocumentorMarkdown\Example\Pizza` 8 | * Parent class: [`\PhpDocumentorMarkdown\Example\AbstractProduct`](./AbstractProduct.md) 9 | * This class implements: 10 | [`\PhpDocumentorMarkdown\Example\ProductInterface`](./ProductInterface.md), 11 | `JsonSerializable` 12 | 13 | ## Properties 14 | 15 | ### name 16 | 17 | Product name. 18 | 19 | ```php 20 | private string $name 21 | ``` 22 | 23 | *** 24 | 25 | ### price 26 | 27 | Product price. 28 | 29 | ```php 30 | protected float $price 31 | ``` 32 | 33 | *** 34 | 35 | ### base 36 | 37 | Pizza base. 38 | 39 | ```php 40 | protected \PhpDocumentorMarkdown\Example\Pizza\Base|null $base 41 | ``` 42 | 43 | Property base description 44 | 45 | - **See:** \PhpDocumentorMarkdown\Example\ManyInterfaces 46 | 47 | *** 48 | 49 | ## Methods 50 | 51 | ### __construct 52 | 53 | Constructor title 54 | 55 | ```php 56 | public __construct(string $name = '', float $price = 10.0, \PhpDocumentorMarkdown\Example\Pizza\Base|null $base = null): mixed 57 | ``` 58 | 59 | Constructor description 60 | 61 | - **See:** \PhpDocumentorMarkdown\Example\ManyInterfaces 62 | - **See:** https://example.com 63 | 64 | **Parameters:** 65 | 66 | | Parameter | Type | Description | 67 | |-----------|-----------------------------------------------------|----------------| 68 | | `$name` | **string** | Product name. | 69 | | `$price` | **float** | Product price. | 70 | | `$base` | **\PhpDocumentorMarkdown\Example\Pizza\Base\|null** | Pizza's base. | 71 | 72 | *** 73 | 74 | ### getName 75 | 76 | Get the name of the product. 77 | 78 | ```php 79 | public getName(): string 80 | ``` 81 | 82 | **Return Value:** 83 | 84 | The name of the product. 85 | 86 | *** 87 | 88 | ### getPrice 89 | 90 | Get the price of the product. 91 | 92 | ```php 93 | public getPrice(): float 94 | ``` 95 | 96 | *** 97 | 98 | ### jsonSerialize 99 | 100 | ```php 101 | public jsonSerialize(): mixed 102 | ``` 103 | 104 | *** 105 | 106 | ### toArray 107 | 108 | Get the instance as an array. 109 | 110 | ```php 111 | public toArray(): array 112 | ``` 113 | 114 | *** 115 | 116 | ## Inherited methods 117 | 118 | ### getTaxRate 119 | 120 | Get the tax rate for the product. 121 | 122 | ```php 123 | public getTaxRate(): float 124 | ``` 125 | 126 | *** 127 | 128 | ### isReviewed 129 | 130 | Whether the object has been reviewed. 131 | 132 | ```php 133 | public isReviewed(): bool 134 | ``` 135 | 136 | *** 137 | -------------------------------------------------------------------------------- /tests/Functional/expected/Home.md: -------------------------------------------------------------------------------- 1 | # Pizza Place (Example documentation) 2 | 3 | This is an automatically generated documentation for **Pizza Place (Example documentation)**. 4 | 5 | ## Namespaces 6 | 7 | ### \PhpDocumentorMarkdown\Example 8 | 9 | #### Classes 10 | 11 | | Class | Description | 12 | |------------------------------------------------------------------------------|------------------------------| 13 | | [`AbstractProduct`](./classes/PhpDocumentorMarkdown/Example/AbstractProduct) | Base class for all products. | 14 | | [`Pizza`](./classes/PhpDocumentorMarkdown/Example/Pizza) | A pizza \| pie's. | 15 | 16 | #### Traits 17 | 18 | | Trait | Description | 19 | |------------------------------------------------------------------------------|---------------------------------| 20 | | [`ReviewableTrait`](./classes/PhpDocumentorMarkdown/Example/ReviewableTrait) | A trait for reviewable objects. | 21 | 22 | #### Interfaces 23 | 24 | | Interface | Description | 25 | |--------------------------------------------------------------------------------|--------------------------| 26 | | [`Arrayable`](./classes/PhpDocumentorMarkdown/Example/Arrayable) | | 27 | | [`ManyInterfaces`](./classes/PhpDocumentorMarkdown/Example/ManyInterfaces) | A ManyInterfaces | 28 | | [`ProductInterface`](./classes/PhpDocumentorMarkdown/Example/ProductInterface) | Interface for a product. | 29 | 30 | ### \PhpDocumentorMarkdown\Example\Pizza 31 | 32 | #### Classes 33 | 34 | | Class | Description | 35 | |----------------------------------------------------------------|--------------------------| 36 | | [`Base`](./classes/PhpDocumentorMarkdown/Example/Pizza/Base) | Represents a pizza base. | 37 | | [`Sauce`](./classes/PhpDocumentorMarkdown/Example/Pizza/Sauce) | Pizza sauce class. | 38 | 39 | ### \PhpDocumentorMarkdown\Functions 40 | 41 | #### Functions 42 | 43 | | Function | Description | 44 | |--------------------------------------------------------------------------|--------------------------------------------------| 45 | | [`mockFunctionWithParameters()`](./functions/mockFunctionWithParameters) | Mock function to demonstrate parameter handling. | 46 | | [`getDatabaseConfig()`](./functions/getDatabaseConfig) | Get database configuration. | 47 | -------------------------------------------------------------------------------- /tests/Functional/expected/classes/PhpDocumentorMarkdown/Example/Pizza.md: -------------------------------------------------------------------------------- 1 | # Pizza 2 | 3 | A pizza \| pie's. 4 | 5 | *** 6 | 7 | * Full name: `\PhpDocumentorMarkdown\Example\Pizza` 8 | * Parent class: [`\PhpDocumentorMarkdown\Example\AbstractProduct`](./AbstractProduct) 9 | * This class implements: 10 | [`\PhpDocumentorMarkdown\Example\ProductInterface`](./ProductInterface), 11 | `JsonSerializable` 12 | 13 | ## Properties 14 | 15 | ### name 16 | 17 | Product name. 18 | 19 | ```php 20 | private string $name 21 | ``` 22 | 23 | *** 24 | 25 | ### price 26 | 27 | Product price. 28 | 29 | ```php 30 | protected float $price 31 | ``` 32 | 33 | *** 34 | 35 | ### base 36 | 37 | Pizza base. 38 | 39 | ```php 40 | protected \PhpDocumentorMarkdown\Example\Pizza\Base|null $base 41 | ``` 42 | 43 | Property base description 44 | 45 | - **See:** \PhpDocumentorMarkdown\Example\ManyInterfaces 46 | 47 | *** 48 | 49 | ## Methods 50 | 51 | ### __construct 52 | 53 | Constructor title 54 | 55 | ```php 56 | public __construct(string $name = '', float $price = 10.0, \PhpDocumentorMarkdown\Example\Pizza\Base|null $base = null): mixed 57 | ``` 58 | 59 | Constructor description 60 | 61 | - **See:** \PhpDocumentorMarkdown\Example\ManyInterfaces 62 | - **See:** https://example.com 63 | 64 | **Parameters:** 65 | 66 | | Parameter | Type | Description | 67 | |-----------|-----------------------------------------------------|----------------| 68 | | `$name` | **string** | Product name. | 69 | | `$price` | **float** | Product price. | 70 | | `$base` | **\PhpDocumentorMarkdown\Example\Pizza\Base\|null** | Pizza's base. | 71 | 72 | *** 73 | 74 | ### getName 75 | 76 | Get the name of the product. 77 | 78 | ```php 79 | public getName(): string 80 | ``` 81 | 82 | **Return Value:** 83 | 84 | The name of the product. 85 | 86 | *** 87 | 88 | ### getPrice 89 | 90 | Get the price of the product. 91 | 92 | ```php 93 | public getPrice(): float 94 | ``` 95 | 96 | *** 97 | 98 | ### jsonSerialize 99 | 100 | ```php 101 | public jsonSerialize(): mixed 102 | ``` 103 | 104 | *** 105 | 106 | ### toArray 107 | 108 | Get the instance as an array. 109 | 110 | ```php 111 | public toArray(): array 112 | ``` 113 | 114 | *** 115 | 116 | ## Inherited methods 117 | 118 | ### getTaxRate 119 | 120 | Get the tax rate for the product. 121 | 122 | ```php 123 | public getTaxRate(): float 124 | ``` 125 | 126 | *** 127 | 128 | ### isReviewed 129 | 130 | Whether the object has been reviewed. 131 | 132 | ```php 133 | public isReviewed(): bool 134 | ``` 135 | 136 | *** 137 | -------------------------------------------------------------------------------- /.wiki/Home.md: -------------------------------------------------------------------------------- 1 | # Pizza Place (Example documentation) 2 | 3 | This is an automatically generated documentation for **Pizza Place (Example documentation)**. 4 | 5 | ## Namespaces 6 | 7 | ### \PhpDocumentorMarkdown\Example 8 | 9 | #### Classes 10 | 11 | | Class | Description | 12 | |---------------------------------------------------------------------------------|------------------------------| 13 | | [`AbstractProduct`](./classes/PhpDocumentorMarkdown/Example/AbstractProduct.md) | Base class for all products. | 14 | | [`Pizza`](./classes/PhpDocumentorMarkdown/Example/Pizza.md) | A pizza \| pie's. | 15 | 16 | #### Traits 17 | 18 | | Trait | Description | 19 | |---------------------------------------------------------------------------------|---------------------------------| 20 | | [`ReviewableTrait`](./classes/PhpDocumentorMarkdown/Example/ReviewableTrait.md) | A trait for reviewable objects. | 21 | 22 | #### Interfaces 23 | 24 | | Interface | Description | 25 | |-----------------------------------------------------------------------------------|--------------------------| 26 | | [`Arrayable`](./classes/PhpDocumentorMarkdown/Example/Arrayable.md) | | 27 | | [`ManyInterfaces`](./classes/PhpDocumentorMarkdown/Example/ManyInterfaces.md) | A ManyInterfaces | 28 | | [`ProductInterface`](./classes/PhpDocumentorMarkdown/Example/ProductInterface.md) | Interface for a product. | 29 | 30 | ### \PhpDocumentorMarkdown\Example\Pizza 31 | 32 | #### Classes 33 | 34 | | Class | Description | 35 | |-------------------------------------------------------------------|--------------------------| 36 | | [`Base`](./classes/PhpDocumentorMarkdown/Example/Pizza/Base.md) | Represents a pizza base. | 37 | | [`Sauce`](./classes/PhpDocumentorMarkdown/Example/Pizza/Sauce.md) | Pizza sauce class. | 38 | 39 | ### \PhpDocumentorMarkdown\Functions 40 | 41 | #### Functions 42 | 43 | | Function | Description | 44 | |-----------------------------------------------------------------------------|--------------------------------------------------| 45 | | [`mockFunctionWithParameters()`](./functions/mockFunctionWithParameters.md) | Mock function to demonstrate parameter handling. | 46 | | [`getDatabaseConfig()`](./functions/getDatabaseConfig.md) | Get database configuration. | 47 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [1.0.0] - 2025-07-10 11 | 12 | ### Added 13 | 14 | - Option for removing page titles 15 | - Tags template 16 | - Table of contents template 17 | - Inheritance template 18 | - Functional tests 19 | - Unit tests for new macros 20 | 21 | ### Changed 22 | 23 | - Rewrote markdown templates for improved formatting 24 | - Updated GitHub Actions workflows and workflow names 25 | - Moved page titles to header partial template 26 | - Defaulted to using URLs without file extensions 27 | 28 | ### Fixed 29 | 30 | - Escaping issues in templates, tables and method signatures 31 | - Global parameters path resolution 32 | 33 | ### Removed 34 | 35 | - Links from global classes and interfaces 36 | 37 | ## [0.1.4] - 2023-11-24 38 | 39 | ### Added 40 | 41 | - Add wiki repo as git submodule 42 | - Composer helper scripts 43 | - Add throws section to output 44 | 45 | ### Changed 46 | 47 | - Set index Markdown file name to Home.md 48 | - Removed watermark from footer 49 | 50 | ### Fixed 51 | 52 | - Fixed issue with interface extends 53 | 54 | ## [0.1.3] - 2022-04-20 55 | 56 | ### Added 57 | 58 | - Option for leaving out .md file extensions from urls (used for GitLab wiki pages). 59 | 60 | ## [0.1.2] - 2022-04-17 61 | 62 | ### Added 63 | 64 | - Added links to implemented interface md files 65 | - Add documentation to Twig macros 66 | - Add tests for Twig macros 67 | - Add PHP wrapper for Twig macros 68 | - Run tests and generate docs (test) with GitHub Actions 69 | - Add properties to classes and traits documentation 70 | 71 | ### Changed 72 | 73 | - Removed method return section when return value is not documented 74 | 75 | ### Fixed 76 | 77 | - Fixed issue with empty table cells 78 | 79 | ## [0.1.1] - 2022-04-15 80 | 81 | ### Changed 82 | 83 | - Regenerated examples 84 | - Renamed header.twig to header.md.twig 85 | 86 | ### Fixed 87 | 88 | - Fixed Markdown horizontal separator breaking in GitHub 89 | 90 | ## [0.1.0] - 2022-04-15 91 | 92 | ### Added 93 | 94 | - Tests for Twig macros 95 | - Made the project a Composer package 96 | - Added example docs source dode 97 | 98 | ### Changed 99 | 100 | - Rewrote examples 101 | 102 | ### Fixed 103 | 104 | - Fixed malformed Markdown 105 | 106 | ## [0.0.2] - 2020-06-18 107 | 108 | ### Added 109 | 110 | - Constants 111 | - Interfaces 112 | - Traits 113 | - Inherited methods 114 | 115 | ## [0.0.1] - 2020-05-05 116 | 117 | ### Added 118 | 119 | - Initial version 120 | -------------------------------------------------------------------------------- /themes/markdown/index.md.twig: -------------------------------------------------------------------------------- 1 | {% import 'include/macros.twig' as macros %} 2 | {% autoescape false %} 3 | {% block content %} 4 | {% include 'partials/header.md.twig' with { node: project } %} 5 | 6 | This is an automatically generated documentation for **{{project.name}}**. 7 | 8 | ## Namespaces 9 | {% for namespace in project.indexes.namespaces | sort((a,b) => a.FullyQualifiedStructuralElementName <=> b.FullyQualifiedStructuralElementName) %} 10 | {% if (namespace.classes | length) > 0 or (namespace.traits | length) > 0 or (namespace.interfaces | length) > 0 or (namespace.functions | length) > 0 %} 11 | 12 | ### {{ namespace.FullyQualifiedStructuralElementName }} 13 | {% if namespace.classes | length > 0 %} 14 | 15 | #### Classes 16 | 17 | {% set parameters = [] %} 18 | {% for class in namespace.classes %} 19 | {% set parameters = parameters | merge([ 20 | { 21 | link: macros.mdLink(class.FullyQualifiedStructuralElementName, null, null, 'class'), 22 | description: class.summary | raw 23 | } 24 | ]) %} 25 | {% endfor -%} 26 | {{ macros.mdTable(parameters, [ 27 | { label: 'Class', key: 'link' }, 28 | { label: 'Description', key: 'description' }, 29 | ]) }} 30 | {%- endif %} 31 | {% if namespace.traits | length > 0 %} 32 | 33 | #### Traits 34 | 35 | {% set parameters = [] %} 36 | {% for trait in namespace.traits %} 37 | {% set parameters = parameters | merge([ 38 | { 39 | link: macros.mdLink(trait.FullyQualifiedStructuralElementName, null, null, 'class'), 40 | description: trait.summary | raw 41 | } 42 | ]) %} 43 | {% endfor -%} 44 | {{ macros.mdTable(parameters, [ 45 | { label: 'Trait', key: 'link' }, 46 | { label: 'Description', key: 'description' }, 47 | ]) }} 48 | {%- endif %} 49 | {% if namespace.interfaces | length > 0 %} 50 | 51 | #### Interfaces 52 | 53 | {% set parameters = [] %} 54 | {% for interface in namespace.interfaces %} 55 | {% set parameters = parameters | merge([ 56 | { 57 | link: macros.mdLink(interface.FullyQualifiedStructuralElementName, null, null, 'class'), 58 | description: interface.summary | raw 59 | } 60 | ]) %} 61 | {% endfor -%} 62 | {{ macros.mdTable(parameters, [ 63 | { label: 'Interface', key: 'link' }, 64 | { label: 'Description', key: 'description' }, 65 | ]) }} 66 | {%- endif %} 67 | {% if namespace.functions | length > 0 %} 68 | 69 | #### Functions 70 | 71 | {% set parameters = [] %} 72 | {% for function in namespace.functions %} 73 | {% set parameters = parameters | merge([ 74 | { 75 | link: macros.mdLink(function.name, null, function.name ~ '()', 'function'), 76 | description: function.summary | raw 77 | } 78 | ]) %} 79 | {% endfor -%} 80 | {{ macros.mdTable(parameters, [ 81 | { label: 'Function', key: 'link' }, 82 | { label: 'Description', key: 'description' }, 83 | ]) }} 84 | {%- endif %} 85 | {% endif %} 86 | {% endfor %} 87 | {% if parameter.includeFooter == 'yes' %} 88 | {% include 'partials/footer.md.twig' %} 89 | {% endif %} 90 | {% endblock %} 91 | {% endautoescape %} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # phpDocumentor-markdown - Automatically generate Markdown documentation 2 | 3 | Use cases: In-repository documentation, GitHub/GitLab wikis, AI prompt context. 4 | 5 | ## Markdown template for phpDocumentor3 6 | 7 | ![Tests Status](https://github.com/Saggre/phpDocumentor-markdown/workflows/Run%20tests/badge.svg?style=flat-square) 8 | ![Generate Docs Status](https://github.com/Saggre/phpDocumentor-markdown/workflows/Generate%20docs/badge.svg?style=flat-square) 9 | 10 | Wish there was an easy way to generate PHP source code documentation? \ 11 | Using [`phpDocumentor`](https://www.phpdoc.org/) with `phpDocumentor-markdown` template, you can automatically generate GitHub/GitLab-ready Markdown documentation from PHP source code. 12 | 13 | This template can be used to document: 14 | 15 | | | Name | Descriptions | Inheritance | Implements | Constants | Properties | Methods | Inherited methods | 16 | |------------|------|--------------|-------------|------------|-----------|------------|---------|-------------------| 17 | | Classes | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 18 | | Interfaces | ✅ | ✅ | ✅ | ➖ | ✅ | ✅ | ✅ | ➖ | 19 | | Traits | ✅ | ✅ | ➖ | ➖ | ✅ | ✅ | ✅ | ➖ | 20 | 21 | | | Name | Descriptions | Types (parameter, return, etc.) | Access modifiers | 22 | |------------|------|--------------|---------------------------------|------------------| 23 | | Functions | ✅ | ✅ | ✅ | ✅ | 24 | | Methods | ✅ | ✅ | ✅ | ✅ | 25 | | Properties | ✅ | ✅ | ✅ | ✅ | 26 | 27 | ## Example 28 | Examples are available in the [.wiki](.wiki/Home.md) directory. 29 | 30 | ## Installation & Usage 31 | - Please refer to [this guide](https://docs.phpdoc.org/guide/getting-started/installing.html) for instructions on installing phpDocumentor. 32 | - Usage instructions assume that `phpdoc` is the phpDocumentor binary. 33 | 34 | ### Running manually 35 | ```bash 36 | # Run phpDocumentor with --template argument pointed to this directory's markdown template 37 | phpdoc --directory=src --target=docs --template= 38 | ``` 39 | 40 | ### Using Composer 41 | 42 | #### Installation via Composer 43 | ```bash 44 | # Require this package. You probably want it as a dev dependency 45 | composer require --dev saggre/phpdocumentor-markdown 46 | ``` 47 | 48 | #### Running manually after installing via Composer 49 | ```bash 50 | # Run phpDocumentor with --template argument pointed to markdown template inside vendor directory 51 | phpdoc --directory=src --target=docs --template="vendor/saggre/phpdocumentor-markdown/themes/markdown" 52 | ``` 53 | 54 | ### Installation & Usage tips 55 | 56 | #### Adding a Composer helper script 57 | 58 | Add this script to your `composer.json` and run `composer create-docs` to generate the documentation. 59 | 60 | ```json 61 | "scripts": { 62 | "create-docs": "phpdoc --directory=src --target=docs --template='vendor/saggre/phpdocumentor-markdown/themes/markdown'" 63 | }, 64 | ``` 65 | 66 | #### Using with PhpDocumentor XML config 67 | 68 | Add a template element to your phpDocumentor XML config and run `phpDocumentor` to generate the documentation. 69 | 70 | ```xml 71 | 72 | 73 |