<?php
/* A component method that reaches through $parent without setting it
first dies where it reaches. The component holds the controller as
$this->parent, and the convention in these files is to take a copy
at the top of a method; a method that skips that line and uses the
name anyway looks right and crashes when it runs.
This has now been reported four times, each from a different screen:
the podcast download job, the group controller, the mail attachment
read, and making a wiki page an article page. Each was one missing
line. A fifth was sitting unreported on the payment error path.
This reads every method in the tree for a $parent used where nothing
in that method set it. A method handed $parent as an argument, or one
that sets it from anything at all, counts as having it.
php devlog/devscripts/check_parent_vars.php
*/
$paths = [];
foreach (["src", "tests"] as $folder) {
$walk = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($folder));
foreach ($walk as $file) {
if ($file->isDir() || substr($file->getFilename(), -4) !== ".php") {
continue;
}
$paths[] = $file->getPathname();
}
}
$missing = [];
foreach ($paths as $path) {
$lines = file($path);
$starts = [];
foreach ($lines as $at => $one) {
if (preg_match('/^\s*(?:public|private|protected|static|\s)*' .
'function\s+(\w+)/', $one, $found)) {
$starts[] = [$at, $found[1]];
}
}
$starts[] = [count($lines), ""];
for ($which = 0; $which < count($starts) - 1; $which++) {
$from = $starts[$which][0];
$to = $starts[$which + 1][0];
$body = implode("", array_slice($lines, $from, $to - $from));
/* A docblock before the next method is not part of this one,
and reading it counted names that method is handed. */
$doc_at = strpos($body, "\n /**");
if ($doc_at !== false) {
$body = substr($body, 0, $doc_at);
}
/* Only a reach through the name counts as a use: a bare mention
in a comment or a string does not run. */
if (!preg_match('/\$parent(->|\[)/', $body)) {
continue;
}
/* Given as an argument, or given a value anywhere. */
if (preg_match('/function\s+\w+\s*\([^)]*\$parent\b/s', $body) ||
preg_match('/\$parent\s*=[^=]/', $body)) {
continue;
}
$missing[] = basename($path) . "::" . $starts[$which][1] .
" reaches through \$parent";
}
}
foreach ($missing as $one) {
echo " " . $one . "\n";
}
echo "check: " . (count($missing) ?
count($missing) . " method(s) reach through a parent nothing set" :
"every parent a method reaches through is set there") . "\n";
exit(count($missing) ? 1 : 0);