In questi ultimi giorni mi sono dedicato ad un piccolo progetto "casalingo"... PHPMarkdoc.
PHPMarkdoc
Lo scopo è quello di generare documentazione PHP nel formato Markdown in modo da poterlo usare per inserire la documentazione di una classe in un Wiki di Bitbucket o Github.
L'idea mi è venuta quando dovevo scrivere la documentazione online di alcune classi che avevo creato per un progetto e vedendo che tutti i generatori automatici erano tutti per HTML ho pensato di crearne una mia versione che scrivesse tutto in Markdown.
Il progetto utilizza Java, mentre per legge i file da analizzare viene usato il PHP (
php-cgi -f <file_name>). Il file
source.php utilizza le API Reflection del PHP per leggere i dati delle classi dell'utente e le memorizza in una struttura Json che sarà letta e convertita in un oggetto FileDoc da Java.
Il codice dell'utente viene iniettato da Java utilizzato un file di "passaggio" che ho chiamato
source_injector.php infatti, questo file viene creato da Java ogni volta che è necessario analizzare un file e contiene una singola linea:
CODICE
require_once("URL_DEL_FILE_PHP");
In questo modo, all'interno di
source.php è presente il codice PHP della classe (p.s al momento non è presente nessun algoritmo che "blocca" output diversi dal Json di source.php).
Le funzioni
get_declared_classes,
get_declared_interfaces,
get_defined_functions e
get_declared_traits si occuperanno di leggere i nomi delle classi dell'utente.
Va notato che, prima di
require_once("source_injector.php"); troviamo:
CODICE
$default_classes = get_declared_classes();
$default_interfaces = get_declared_interfaces();
$default_traits = get_declared_traits();
Con questo "trucco", possiamo fare in modo di isolare le classi di PHP da quelle dell'utente utilizzando
array_diff.
Non c'è nulla del genere per le funzioni perchè il PHP già fa la divisione con un array interno con
[internal] e
[user] per differenziare i due tipi, eseguire un
array_filter su questo array è obbligatorio per rimuovere da questo elenco le funzioni del parser (infatti, anche se get_defined_functions() viene eseguito
prima che il
source.php definisca una funzione, il PHP fa una prima lettura del sorgente per leggere tutte le funzioni (e tanto altro)) ed è per questo che hanno tutte un prefix
parse_. Purtroppo questo sistema ha i suoi limiti, se l'utente dichiara una funzione con il prefisso
parse_ questa sarà "silenziosamente" ignorata. Per questo sto pensando di utilizzare un array interno per definire le funzioni di sistema, ma il problema rimane lo stesso se l'utente utilizza funzioni con gli stessi nomi (
parse_ è molto comune, per questo dovrà essere modificato in
phpmarkdoc_parser_*).
Dopo tutto questo lavoro, come ho detto prima, il PHP ritorna a Java il Json con tutte le informazioni necessarie sul file:
- Il numero di classi/traits/funzioni e interfacce trovate
- Tutte le classi trovate
- Tutte le interfacce trovate
- Tutti i traits trovati
- Tutte le funzioni trovate
Questi ultimi 4 non contengono solamente i nomi ma
tutte le informazioni necessarie trovate. (Tranne per quanto riguarda informazioni come super classi o interfacce implementate, dato che potrebbe capitare che la definizione si trovi da qualche altra parte.. Anche se non sono soddisfatto di questo sistema, dato che a causa dell'eccessiva "flessibilità" del PHP potrebbe essere difficile capire quale sia la vera super classe/interfaccia)
Da come si può intuire,
FileDoc è il padre di tutto. Contiene tutti i dati Json portati in forma di classi Java.
Non c'è molto da scrivere a riguardo, sono solo una lista di campi che riportano i dati trovati.
Il sistema di scrittura
Il mio scopo era renderlo il
più flessibile possibile!
Più flessibile è, meno codice è necessario cambiare per modificare il tipo di output. L'interfaccia
Writer si occupa di rappresentare una classe il cui compito è quello di scrivere su un file senza sapere nulla sul formato o altro.
Nome metodo | Descrizione |
start(String fileName, Path outputDirectory) | Richiamato quando il Parser vuole scrivere qualcosa su un determinato file (fileName) e dove (outputDirectory). |
writeClassInfo(LClass lClass) | Chiamato quando si vuole scrivere informazioni riguardo una determinata classe (lClass) |
writeTraitInfo(Trait trait) | Chiamato quando si vuole scrivere informazioni riguardo un determinato Trait (trait) |
writeInterfaceInfo(Interface mInterface) | Chiamato quando si vuole scrivere informazioni riguardo una determinata interfaccia (mInterface) |
writeFunction(Function function) | Chiamato quando si vuole scrivere informazioni riguardo una determinata funzione (function) |
close() | Chiamato quando il parser ha finito di scrivere (in genere, chiude il writer) |
La prima (e attualmente, l'unica) implementazione è
MarkdownWriterL'intero progetto è disponibile
qui.
Edited by .ReVo. - 25/7/2014, 17:40