Completed
Push — master ( 5e2d42...f80c85 )
by Amine
02:13
created

generate-docs.php (2 issues)

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;
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 15 and the first side effect is on line 11.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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