Completed
Push — master ( d55531...cfe428 )
by Amine
02:42
created

generate-docs.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php namespace Demo;
2
/**
3
 * This script is an example of usage Tarsana\Functional
4
 * It is used to generate the Reference documentation of
5
 * functions in the library as markdown files using
6
 * [dox](https://github.com/tj/dox) to parse source
7
 * files and extract documentation from comments. This is not a
8
 * perfect example of Functional Programming, it simply shows a
9
 * real use case of this library.
10
 */
11
require __DIR__ . '/vendor/autoload.php';
12
13
use Tarsana\Functional as F;
14
15
define('URL', 'https://github.com/tarsana/functional/blob/master');
16
17
// Reads the list of sources files from 'composer.json'
18
// * -> IO [String]
19
function modules() {
20
    $composer = json_decode(file_get_contents(__DIR__.'/composer.json'));
21
    return $composer->autoload->files;
22
}
23
24
// Extracts tags with specific type from a docblock.
25
// String -> Object -> [name => String, description => String, string => String]
26
function tags($type, $data) {
27
    return F\filter(function($tag) use($type) {
28
        return $tag->type == $type;
29
    }, $data->tags);
30
}
31
32
// Extracts list of arguments from a docblock.
33
// Object -> [Arg]
34
/**
35
 * @type Arg
36
 * Argument of a function.
37
 *
38
 * @field name String
39
 * @field type String
40
 */
41
function argsOf($data) {
42
    return F\map(function($tag){
43
        return (object) [
44
            'type' => $tag->name,
45
            'name' => $tag->description
46
        ];
47
    }, tags('param', $data));
48
}
49
50
// Extracts signatures of a function.
51
// Object -> [String]
52
function signaturesOf($data) {
53
    return F\map(function($tag){
54
        return $tag->string;
55
    }, tags('signature', $data));
56
}
57
58
// Extracts the return type of a function.
59
// Object -> String
60
function returnOf($data) {
61
    $returns = tags('return', $data);
62
    return (F\length($returns) > 0)
63
        ? $returns[0]->description
64
        : null;
65
}
66
67
// Extracts the type of a block
68
// Object -> String
69
function typeOf($data) {
70
    if (isset($data->ctx->type))
71
        return $data->ctx->type;
72
    if (F\length(tags('var', $data)) > 0)
73
        return 'attr';
74
    if (F\length(tags('return', $data)) > 0)
75
        return 'method';
76
}
77
78
// Extract keywords
79
// Object -> [String]
80
function keywords($data) {
81
    if (!isset($data->code)) {
82
        return [];
83
    }
84
    $size = strpos($data->code, '(');
85
    if ($size === false)
86
        $size = strlen($data->code);
87
    $keywords = F\pipe(
88
        F\take($size),
89
        F\split(' '),
90
        F\map('trim'),
91
        F\filter(F\notEq(''))
92
    );
93
    return $keywords($data->code);
94
}
95
96
// Object -> DocBlock
97
/**
98
 * @type DocBlock
99
 * Documentation block of a function.
100
 *
101
 * @field type String
102
 * @field name String
103
 * @field args [Arg]
104
 * @field return String
105
 * @field signatures [String]
106
 * @field description String
107
 * @field is_internal Boolean
108
 * @field is_static Boolean
109
 */
110
function block($data) {
111
    $keywords = keywords($data);
112
    return (object) [
113
        'type' => typeOf($data),
114
        'name' => isset($data->ctx->name) ? $data->ctx->name : F\last($keywords),
115
        'args' => argsOf($data),
116
        'return' => returnOf($data),
117
        'signatures' => signaturesOf($data),
118
        'description' => $data->description->full,
119
        'is_static' => in_array('static', $keywords),
120
        'is_internal' => in_array('private', $keywords) || in_array('protected', $keywords) || (0 < F\length(tags('internal', $data)))
121
    ];
122
}
123
124
// Get a markdown code block
125
// String -> String -> String
126
function code($lang, $text) {
127
    if(trim($text) == '')
128
        return '';
129
    return "```{$lang}\n{$text}\n```";
130
}
131
132
// Gets the markdown of a function/method/class
133
// DocBlock -> String
134
function markdown($fn) {
135
    if ($fn->type == 'class') {
136
        return $fn->description;
137
    } else {
138
        $args = F\map(function($arg) {
139
            return $arg->type . ' ' . $arg->name;
140
        }, $fn->args);
141
        $proto = $fn->name . '('. F\join(', ', $args) .') : ' . $fn->return;
142
        return F\join("\n\n", [
143
            "## {$fn->name}",
144
            code('php', $proto),
145
            code('', F\join("\n", $fn->signatures)),
146
            $fn->description
147
        ]);
148
    }
149
}
150
151
// Adds title and table of contents
152
// String -> [Object] -> [String]
153
function addContents() {
154
    $addContents = function($name, $parts) {
155
        $names = F\filter(F\notEq($name), F\map(F\get('name'), $parts));
156
        var_dump($names);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($names); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
157
        $contents = F\map(function ($partname) use($name) {
158
            $link = URL . "/docs/{$name}.md#{$partname}";
159
            return "- [{$partname}]($link)";
160
        }, $names);
161
        file_put_contents ("docs/README.md",
162
            F\join("\n\n", F\concat(["## {$name}"], $contents)) . "\n\n"
163
        , FILE_APPEND);
164
        return array_merge(['# ' . $name, '## Table Of Contents'], $contents, F\map(F\get('md'), $parts));
165
    };
166
    return F\apply(F\curry($addContents), func_get_args());
167
}
168
// Generates documentation for a module of functions
169
// String -> IO
170
function generateModule($file) {
171
    $content = F\pipe(
172
        F\map('Demo\\block'),
173
        F\filter(function($block){
174
            return $block->type == 'function' && !$block->is_internal;
175
        }),
176
        F\map(function($block){
177
            return [
178
                'name' => $block->name,
179
                'md' => markdown($block)
180
            ];
181
        }),
182
        addContents(F\replace(['src/', '.php'], '', $file)),
183
        F\join("\n\n")
184
    );
185
186
    file_put_contents (
187
        F\replace(['src', '.php'], ['docs', '.md'], $file),
188
        $content(json_decode(shell_exec("dox -r < {$file}")))
189
    );
190
}
191
192
// Generates documentation for a class
193
// String -> IO
194
function generateClass($name) {
195
    $content = F\pipe(
196
        F\map('Demo\\block'),
197
        F\filter(function($block){
198
            return in_array($block->type, ['method', 'class']) && !$block->is_internal;
199
        }),
200
        f\map(function($block) use ($name) {
201
            if ($block->type == 'method') {
202
                $block->name = ($block->is_static ? $name . '::' : '') . $block->name;
203
            }
204
            return $block;
205
        }),
206
        F\map(function($block){
207
            return [
208
                'name' => $block->name,
209
                'md' => markdown($block)
210
            ];
211
        }),
212
        addContents($name),
213
        F\join("\n\n")
214
    );
215
216
    file_put_contents (
217
        "docs/{$name}.md",
218
        $content(json_decode(shell_exec("dox -r < src/{$name}.php")))
219
    );
220
}
221
222
// The entry point
223
file_put_contents('docs/README.md', "# Reference Documentation \n\n");
224
file_put_contents ("docs/README.md", "# Function Modules\n\n" , FILE_APPEND);
225
F\each('Demo\\generateModule', modules());
226
file_put_contents ("docs/README.md", "# Containers\n\n" , FILE_APPEND);
227
F\each('Demo\\generateClass', ['Stream', 'Error']);
228