Issues (345)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

build.php (16 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 Tarsana\Functional;
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 76 and the first side effect is on line 6.

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 parses the source files using [dox](https://github.com/tj/dox)
4
 * and generates the unit tests and documentation files.
5
 */
6
require __DIR__ . '/vendor/autoload.php';
7
8
/**
9
 * Custom Types:
10
 *  DoxBlock :: {
11
 *     tags: [{
12
 *         type: String,
13
 *         string: String,
14
 *         types: [String],
15
 *         name: String,
16
 *         description: String
17
 *         ...
18
 *     }],
19
 *     description: {
20
 *         full: String,
21
 *         summary: String,
22
 *         body: String
23
 *     },
24
 *     code: String,
25
 *     ctx: {
26
 *         type: String,
27
 *         name: String,
28
 *         ...
29
 *     }
30
 *     isPrivate:
31
 *     isEvent:
32
 *     isConstructor:
33
 *     line:
34
 *     ignore:
35
 *  }
36
 *
37
 * Block :: {
38
 *     type: file|function|class|method
39
 *     name: String // DoxBlock.ctx.name
40
 *     params: [{type: String, name: String}]
41
 *     return: String
42
 *     signatures: [String]
43
 *     description: String
44
 *     summary: String
45
 *     internal: Boolean
46
 *     ignore: Boolean
47
 *     code: String
48
 * }
49
 *
50
 * Operation :: {
51
 *     name: String,
52
 *     signature: String
53
 * }
54
 *
55
 * Module :: {
56
 *     path: String
57
 *     name: String
58
 *     docsPath: String
59
 *     testsPath: String
60
 *     blocks: [Block]
61
 *     docs: String
62
 *     tests: String
63
 *     testsFooter: String
64
 *     streamOperations: String
65
 *     streamMethods: String
66
 * }
67
 */
68
69
/**
70
 * The entry point.
71
 *
72
 * @signature [String] -> IO
73
 * @param  array $modules
74
 * @return void
75
 */
76
function build_main($modules) {
77
    build_init_stream_operations();
78
    each(_f('build_module'), $modules);
0 ignored issues
show
_f('build_module') cannot be passed to each() as the parameter $array expects a reference.
Loading history...
79
    build_close_stream_operations();
80
}
81
82
/**
83
 * Writes the header of the stream operations file.
84
 *
85
 * @signature IO
86
 * @return void
87
 */
88
function build_init_stream_operations() {
89
    file_put_contents(
90
        'src/Internal/_stream_operations.php',
91
        "<?php\n\nuse Tarsana\Functional as F;\n\nreturn F\map(F\apply(F\_f('_stream_operation')), [\n\t['then', 'Function -> Any -> Any', F\_f('_stream_then')],\n"
92
    );
93
    file_put_contents(
94
        'docs/stream-operations.md',
95
        "# Stream Operations"
96
    );
97
}
98
99
/**
100
 * Writes the footer of the stream operations file.
101
 *
102
 * @signature IO
103
 * @return void
104
 */
105
function build_close_stream_operations() {
106
    file_put_contents(
107
        'src/Internal/_stream_operations.php',
108
        "\n]);\n", FILE_APPEND
109
    );
110
}
111
112
/**
113
 * Extracts the modules files from composer.json.
114
 *
115
 * @signature [String]
116
 * @return array
117
 */
118
function get_modules() {
119
    $composer = json_decode(file_get_contents(__DIR__.'/composer.json'));
120
    return $composer->autoload->files;
121
}
122
123
/**
124
 * Generates unit tests and documentation for a module.
125
 *
126
 * @signature String -> IO
127
 * @param  string $path
128
 * @return void
129
 */
130
function build_module($path) {
131
    apply(process_of([
132
        'module_of',
133
        'generate_docs',
134
        'generate_tests',
135
        'generate_stream_operations',
136
        'generate_stream_methods',
137
        'write_module'
138
    ]), [$path]);
139
}
140
141
/**
142
 * Writes the module's docs and tests.
143
 *
144
 * @signature Module -> IO
145
 * @param  object $module
146
 * @return void
147
 */
148
function write_module($module) {
149 View Code Duplication
    if ($module->docs) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
150
        $docsDir  = dirname($module->docsPath);
151
        if (!is_dir($docsDir))
152
            mkdir($docsDir, 0777, true);
153
        file_put_contents($module->docsPath,  $module->docs);
154
    }
155 View Code Duplication
    if ($module->tests) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
156
        $testsDir = dirname($module->testsPath);
157
        if (!is_dir($testsDir))
158
            mkdir($testsDir, 0777, true);
159
        file_put_contents($module->testsPath, $module->tests);
160
    }
161
    if ($module->streamOperations) {
162
        file_put_contents('src/Internal/_stream_operations.php', $module->streamOperations, FILE_APPEND);
163
    }
164
    if ($module->streamMethods) {
165
        file_put_contents('docs/stream-operations.md', $module->streamMethods, FILE_APPEND);
166
    }
167
}
168
169
/**
170
 * Creates a module from a path.
171
 *
172
 * @signature String -> Module
173
 * @param  string $path
174
 * @return object
175
 */
176
function module_of($path) {
177
    return apply(process_of([
178
        'fill_name',
179
        'fill_docs_path',
180
        'fill_tests_path',
181
        'fill_blocks'
182
    ]), [(object)['path' => $path]]);
183
}
184
185
/**
186
 * Fills the name of the Module based on the path.
187
 * 'src/xxx/aaa.php' -> 'aaa'
188
 *
189
 * @signature Module -> Module
190
 * @param  object $module
191
 * @return object
192
 */
193
function fill_name($module) {
194
    $module->name = apply(pipe(split('/'), last(), split('.'), head()), [$module->path]);
195
    return $module;
196
}
197
198
/**
199
 * Fills documentation file path based on source file path.
200
 * 'src/xxx.php' -> 'docs/xxx.md'
201
 *
202
 * @signature Module -> Module
203
 * @param  object $module
204
 * @return object
205
 */
206
function fill_docs_path($module) {
207
    $module->docsPath = replace(['src', '.php'], ['docs', '.md'], $module->path);
208
    return $module;
209
}
210
211
/**
212
 * Fills tests file path based on source file path.
213
 * 'src/xxx.php' -> 'tests/xxxTest.php'
214
 *
215
 * @signature Module -> Module
216
 * @param  object $module
217
 * @return object
218
 */
219
function fill_tests_path($module) {
220
    $name = ucfirst(camelCase($module->name));
221
    $dir = 'tests' . remove(3, dirname($module->path));
222
    $module->testsPath = "{$dir}/{$name}Test.php";
223
    return $module;
224
}
225
226
/**
227
 * Fills the blocks of the Module based on the path.
228
 *
229
 * @signature Module -> Module
230
 * @param  array $module
231
 * @return array
232
 */
233
function fill_blocks($module) {
234
    $module->blocks = apply(pipe(
235
        prepend('dox -r < '), // "dox -r < src/...php"
236
        'shell_exec',         // "[{...}, ...]"
237
        'json_decode',        // [DoxBlock]
238
        map(_f('make_block'))
239
        // sort()
240
    ), [$module->path]);
241
    return $module;
242
}
243
244
/**
245
 * Converts a DoxBlock to a Block.
246
 *
247
 * @signature DoxBlock -> Block
248
 * @param  object $doxBlock
249
 * @return object
250
 */
251
function make_block($doxBlock) {
252
    $tags = groupBy(get('name'), tags_of($doxBlock));
253
254
    $type = 'function';
255
    if (has('file', $tags)) $type = 'file';
256
    if (has('class', $tags)) $type = 'class';
257
    if (has('method', $tags)) $type = 'method';
258
259
    $params = map(function($tag){
260
        $parts = split(' ', get('value', $tag));
261
        return [
262
            'type' => $parts[0],
263
            'name' => $parts[1]
264
        ];
265
    }, get('param', $tags) ?: []);
266
267
    $return = getPath(['return', 0, 'value'], $tags);
268
    $signatures = get('signature', $tags);
269
    if ($signatures)
270
        $signatures = map(get('value'), $signatures);
271
    return (object) [
272
        'type' => $type,
273
        'name' => getPath(['ctx', 'name'], $doxBlock),
274
        'params' => $params,
275
        'return' => $return,
276
        'signatures' => $signatures,
277
        'description' => getPath(['description', 'full'], $doxBlock),
278
        'summary' => getPath(['description', 'summary'], $doxBlock),
279
        'internal' => has('internal', $tags),
280
        'ignore' => has('ignore', $tags),
281
        'stream' => has('stream', $tags)
282
        // 'code' => get('code', $doxBlock)
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
283
    ];
284
}
285
286
/**
287
 * Returns an array of tags, each having a name and a value.
288
 *
289
 * @signature DoxBlock -> [{name: String, value: String}]
290
 * @param  object $doxBlock
291
 * @return array
292
 */
293
function tags_of($doxBlock) {
294
    if ($doxBlock->tags)
295
        return map(function($tag){
296
            return (object) [
297
                'name'  => $tag->type,
298
                'value' => $tag->string
299
            ];
300
        }, $doxBlock->tags);
301
    return [];
302
}
303
304
/**
305
 * Generates documentation contents for a module.
306
 *
307
 * @signature Module -> Module
308
 * @param  object $module
309
 * @return object
310
 */
311
function generate_docs($module) {
312
    $module->docs = '';
313
    if (startsWith('_', $module->name))
0 ignored issues
show
The call to startsWith() has too many arguments starting with $module->name.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
314
        return $module;
315
    return apply(process_of([
316
        'generate_docs_header',
317
        'generate_docs_sommaire',
318
        'generate_docs_contents'
319
    ]), [$module]);
320
}
321
322
/**
323
 * Generates documentation header.
324
 *
325
 * @signature Module -> Module
326
 * @param  object $module
327
 * @return object
328
 */
329
function generate_docs_header($module) {
330
    $name = $module->name;
331
    $description = get('description', head($module->blocks));
332
    $module->docs .= "# {$name}\n\n{$description}\n\n";
333
    return $module;
334
}
335
336
/**
337
 * Generates documentation table of contents.
338
 *
339
 * @signature Module -> Module
340
 * @param  object $module
341
 * @return object
342
 */
343 View Code Duplication
function generate_docs_sommaire($module) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
344
    $blocks = filter (
345
        satisfiesAll(['ignore' => not(), 'internal' => not(), 'type' => equals('function')]),
0 ignored issues
show
The call to not() misses a required argument $value.

This check looks for function calls that miss required arguments.

Loading history...
346
        $module->blocks
347
    );
348
    $items = map(_f('generate_docs_sommaire_item'), $blocks);
349
    $module->docs .= join('', $items);
350
    return $module;
351
}
352
353
/**
354
 * Generates an item of the documentation's table of contents.
355
 *
356
 * @signature Block -> String
357
 * @param  object $block
358
 * @return string
359
 */
360
function generate_docs_sommaire_item($block) {
361
    $title = get('name', $block);
362
    $link  = lowerCase($title);
363
    return "- [{$title}](#{$link}) - {$block->summary}\n\n";
364
}
365
366
/**
367
 * Generates documentation contents.
368
 *
369
 * @signature Module -> Module
370
 * @param  object $module
371
 * @return object
372
 */
373 View Code Duplication
function generate_docs_contents($module) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
374
    $blocks = filter (
375
        satisfiesAll(['ignore' => not(), 'internal' => not()]),
0 ignored issues
show
The call to not() misses a required argument $value.

This check looks for function calls that miss required arguments.

Loading history...
376
        $module->blocks
377
    );
378
    $contents = map(_f('generate_docs_contents_item'), $blocks);
379
    $module->docs .= join('', $contents);
380
    return $module;
381
}
382
383
/**
384
 * Generates an item of the documentation's contents.
385
 *
386
 * @signature Block -> String
387
 * @param  object $block
388
 * @return string
389
 */
390
function generate_docs_contents_item($block) {
391
    if ($block->type != 'function')
392
        return '';
393
    $params = join(', ', map(pipe(values(), join(' ')), get('params', $block)));
394
    $return = get('return', $block);
395
    $prototype = "```php\n{$block->name}({$params}) : {$return}\n```\n\n";
396
    $signature = '';
397
    $blockSignature = join("\n", $block->signatures);
398
    if ($blockSignature)
399
        $signature = "```\n{$blockSignature}\n```\n\n";
400
    return "# {$block->name}\n\n{$prototype}{$signature}{$block->description}\n\n";
401
}
402
403
/**
404
 * Generates tests contents for a module.
405
 *
406
 * @signature Module -> Module
407
 * @param  object $module
408
 * @return object
409
 */
410
function generate_tests($module) {
411
    $module->tests = '';
412
    $module->testsFooter = '';
413
    return apply(process_of([
414
        'generate_tests_header',
415
        'generate_tests_contents',
416
        'generate_tests_footer'
417
    ]), [$module]);
418
}
419
420
/**
421
 * Generates module's tests header.
422
 *
423
 * @signature Module -> Module
424
 * @param  object $module
425
 * @return object
426
 */
427
function generate_tests_header($module) {
428
    $namespace = "Tarsana\UnitTests\Functional";
429
    $additionalNamespace = replace("/", "\\", remove(6, dirname($module->testsPath)));
430
    if ($additionalNamespace)
431
        $namespace .= "\\" . $additionalNamespace;
432
    $name = remove(-4, last(split("/", $module->testsPath)));
433
    $module->tests .= "<?php namespace {$namespace};\n\nuse Tarsana\Functional as F;\n\nclass {$name} extends \Tarsana\UnitTests\Functional\UnitTest {\n";
434
    return $module;
435
}
436
437
/**
438
 * Generates module's tests contents.
439
 *
440
 * @signature Module -> Module
441
 * @param  object $module
442
 * @return object
443
 */
444
function generate_tests_contents($module) {
445
    $blocks = filter (
446
        satisfiesAll(['ignore' => not()]),
0 ignored issues
show
The call to not() misses a required argument $value.

This check looks for function calls that miss required arguments.

Loading history...
447
        $module->blocks
448
    );
449
    $contents = join("\n", map(function($block) use($module) {
450
        return generate_tests_contents_item($block, $module);
451
    }, $blocks));
452
    if (trim($contents) != '')
453
        $module->tests .= $contents;
454
    else
455
        $module->tests = '';
456
    return $module;
457
}
458
459
/**
460
 * Generates a test for a module.
461
 *
462
 * @signature Block -> Module -> String
463
 * @param  object $block
464
 * @param  object $module
465
 * @return string
466
 */
467
function generate_tests_contents_item($block, $module) {
468
    if ($block->type != 'function')
469
        return '';
470
471
    $code = apply(pipe(
472
        _f('code_from_description'),
473
        chunks("\"\"''{}[]()", "\n"),
474
        map(function($part) use($module) {
475
            return add_assertions($part, $module);
476
        }),
477
        filter(pipe('trim', notEq(''))),
478
        chain(split("\n")),
479
        map(prepend("\t\t")),
480
        join("\n")
481
    ), [$block]);
482
483
    if ('' == trim($code))
484
        return '';
485
    return prepend("\tpublic function test_{$block->name}() {\n",
486
        append("\n\t}\n", $code)
487
    );
488
}
489
490
/**
491
 * Extracts the code snippet from the description of a block.
492
 *
493
 * @signature Block -> String
494
 * @param  object $block
495
 * @return string
496
 */
497
function code_from_description($block) {
498
    $description = get('description', $block);
499
    if (!contains('```php', $description))
500
        return '';
501
    $code = remove(7 + indexOf('```php', $description), $description);
502
    return remove(-4, trim($code));
503
}
504
505
/**
506
 * Adds assertions to a part of the code.
507
 *
508
 * @signature String -> String
509
 * @param  string $part
510
 * @return string
511
 */
512
function add_assertions($part, $module) {
513
    if (contains('; //=> ', $part)) {
514
        $pieces = split('; //=> ', $part);
515
        $part = "\$this->assertEquals({$pieces[1]}, {$pieces[0]});";
516
    }
517
    elseif (contains('; // throws ', $part)) {
518
        $pieces = split('; // throws ', $part);
519
        $variables = match('/ \$[0-9a-zA-Z_]+/', $pieces[0]);
520
        $use = '';
521
        if (length($variables)) {
522
            $variables = join(', ', map('trim', $variables));
523
            $use = "use({$variables}) ";
524
        }
525
        return "\$this->assertErrorThrown(function() {$use}{\n\t$pieces[0]; \n},\n{$pieces[1]});";
526
    }
527
    elseif (startsWith('class ', $part) || startsWith('function ', $part)) {
0 ignored issues
show
The call to startsWith() has too many arguments starting with $part.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
528
        $module->testsFooter .= $part . "\n\n";
529
        $part = '';
530
    }
531
    return $part;
532
}
533
534
/**
535
 * Generates module's tests footer.
536
 *
537
 * @signature Module -> Module
538
 * @param  object $module
539
 * @return object
540
 */
541
function generate_tests_footer($module) {
542
    if ($module->tests)
543
        $module->tests .= "}\n\n{$module->testsFooter}";
544
    return $module;
545
}
546
547
/**
548
 * Generates module's stream operations.
549
 *
550
 * @signature Module -> Module
551
 * @param  array $module
552
 * @return array
553
 */
554
function generate_stream_operations($module) {
555
    $blocks = filter (
556
        satisfiesAll(['ignore' => equals(false), 'stream' => equals(true)]),
557
        $module->blocks
558
    );
559
    $operations = map(_f('stream_operation_declaration'), chain(_f('stream_operations_of_block'), $blocks));
560
    $module->streamOperations = join("", $operations);
561
    return $module;
562
}
563
564
/**
565
 * Gets stream operations from a block.
566
 *
567
 * @signature Block -> [Operation]
568
 * @param  object $block
569
 * @return string
570
 */
571
function stream_operations_of_block($block) {
572
    return map(function($signature) use($block) {
573
        return (object) [
574
            'name' => $block->name,
575
            'signature' => normalize_signature($signature)
576
        ];
577
    }, get('signatures', $block));
578
}
579
580
/**
581
 * Converts a formal signature to a stream signature.
582
 * [a]       becomes List
583
 * {k: v}    becomes Array|Object
584
 * (a -> b)  becomes Function
585
 *  *        becomes Any
586
 *
587
 * @signature String -> String
588
 * @param  string $signature
589
 * @return string
590
 */
591
function normalize_signature($signature) {
592
    // This is not the best way to do it :P
593
    return join(' -> ', map(pipe(
594
        regReplace('/Maybe\([a-z][^\)]*\)/', 'Any'),
595
        regReplace('/Maybe\(([^\)]+)\)/', '$1|Null'),
596
        regReplace('/\([^\)]+\)/', 'Function'),
597
        regReplace('/\[[^\]]+\]/', 'List'),
598
        regReplace('/\{[^\}]+\}/', 'Object|Array'),
599
        regReplace('/^.$/', 'Any'),
600
        regReplace('/[\(\)\[\]\{\}]/', '')
601
    ), chunks('(){}', ' -> ', $signature)));
602
}
603
604
/**
605
 * Converts a stream operation to declaration array.
606
 *
607
 * @signature Operation -> String
608
 * @param  object $operation
609
 * @return string
610
 */
611
function stream_operation_declaration($operation) {
612
    $name = rtrim($operation->name, '_');
613
    return "\t['{$name}', '{$operation->signature}', F\\{$operation->name}()],\n";
614
}
615
616
/**
617
 * Generates module's stream methods documentation.
618
 *
619
 * @signature Module -> Module
620
 * @param  array $module
621
 * @return array
622
 */
623
function generate_stream_methods($module) {
624
    $blocks = filter (
625
        satisfiesAll(['ignore' => equals(false), 'stream' => equals(true)]),
626
        $module->blocks
627
    );
628
    $methods = map(stream_method_link($module->name), $blocks);
629
    $module->streamMethods = (length($methods) > 0)
630
        ? "\n\n## {$module->name}\n\n" . join("\n", $methods)
631
        : '';
632
    return $module;
633
}
634
635
/**
636
 * Gets an element of the stream methods list.
637
 *
638
 * @signature String -> Block -> String
639
 * @param  string $moduleName
0 ignored issues
show
There is no parameter named $moduleName. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
640
 * @param  object $block
0 ignored issues
show
There is no parameter named $block. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
641
 * @return string
642
 */
643
function stream_method_link() {
644
    static $curried = false;
645
    $curried = $curried ?: curry(function($moduleName, $block) {
646
        return "- [{$block->name}](https://github.com/tarsana/functional/blob/master/docs/{$moduleName}.md#{$block->name}) - {$block->summary}\n";
647
    });
648
    return _apply($curried, func_get_args());
649
}
650
651
/**
652
 * process_of(['f1', 'f2']) == pipe(_f('f1'), _f('f2'));
653
 *
654
 * @signature [String] -> Function
655
 * @param array $fns
656
 * @return callable
657
 */
658
function process_of($fns) {
659
    return apply(_f('pipe'), map(_f('_f'), $fns));
660
}
661
662
/**
663
 * Dump a variable and returns it.
664
 *
665
 * @signature a -> a
666
 * @param  mixed $something
0 ignored issues
show
There is no parameter named $something. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
667
 * @return mixed
668
 */
669
function log() {
670
    $log = function($something) {
671
        echo toString($something);
672
        return $something;
673
    };
674
    return apply(curry($log), func_get_args());
675
}
676
677
// Convert Warnings to Exceptions
678
set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext) {
0 ignored issues
show
The parameter $errcontext is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
679
    if (0 === error_reporting())
680
        return false;
681
    throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
682
});
683
684
// Run the build
685
build_main(get_modules());
686