Skip to content

Instantly share code, notes, and snippets.

@yoosefi
Created June 1, 2020 08:08
Show Gist options
  • Save yoosefi/ba560f54677169fe68ca0a3c699d03c6 to your computer and use it in GitHub Desktop.
Save yoosefi/ba560f54677169fe68ca0a3c699d03c6 to your computer and use it in GitHub Desktop.
generate php magic method annotations
#!/usr/bin/php
<?php
const ALIASES = [
'at' => 'iso8601',
'description' => 'text',
'on' => 'date',
'quantity' => 'qty',
];
array_shift($argv);
sort($argv);
// $name => $argName
$get = [];
$has = [];
$set = [];
$select = []; // object arrays only
foreach ($argv as $field) {
// parse the name
$words = explode('_', preg_replace('/[^a-z0-9_]/i', '', $field));
$lastWord = $words[count($words) - 1];
$argName = ALIASES[$lastWord] ?? $lastWord;
$studly = implode('', array_map('ucwords', $words));
// parse the type
$depends = strpos($field, '@') !== false;
$nullable = strpos($field, '?') !== false;
if (strpos($field, '.')) {
$type = 'bool';
}
elseif (strpos($field, '##')) {
$type = 'int';
}
elseif (strpos($field, '#')) {
$type = 'number';
}
else {
$type = 'string';
}
$readonly = strpos($field, '-');
// process composites
if ($object = strpos($field, '{}')) {
$type = $studly;
}
if (strpos($field, '[]')) {
if ($object) {
$type = preg_replace('/(s|ies)$/', '', $type);
$eachName = preg_replace('/(s|ies)$/', '', $argName);
$select['select' . $studly] = [$type . '[]', $type, $eachName];
}
$type .= '[]';
$has['has' . $studly] = 'has' . $studly;
}
$get[$type === 'bool' ? 'is' . $studly : 'get' . $studly] = [
($nullable ? 'null|' . $type : $type),
$readonly
];
if (!$readonly) {
$set['set' . $studly] = [
$nullable ? '?' . $type : $type,
$argName,
$depends
];
}
}
/**
* The right-side tab padding.
*
* @param array $names
* @param int $start chars to the starting boundary
* @return int
*/
function rpad (array $names, int $start = 0): int {
$len = $realLen = max(array_map('strlen', $names));
// if the name stops on a tab, add a space.
if (0 === ($start + $len) % 4) {
$len++;
}
// pad to the nearest tab from the start
$pad = $start + ceil($len / 4) * 4;
// weird hack for negative start position that i don't understand.
if ($realLen >= $pad) {
$pad += 4;
}
return $pad;
}
function ruler () {
// -123456789-123456789-123456789-123456789-123456789-123456789-
//echo " * | | | | | | | | | | | | | | |\n";
}
echo "/**\n";
if ($get) {
$tPad = rpad(array_column($get, 0), 1); // longType starts 1 char before tab
$mPad = rpad(array_keys($get));
ruler();
foreach ($get as $method => [$longType, $readonly]) {
echo sprintf(" * @method %-{$tPad}s%-{$mPad}s()", $longType, $method);
if ($readonly) {
echo ' read-only';
}
echo "\n";
}
}
if ($has) {
echo " *\n";
$mPad = rpad(array_keys($has)); // starts on tab
ruler();
foreach ($has as $method) {
echo sprintf(" * @method bool %-{$mPad}s()\n", $method);
}
}
if ($set) {
echo " *\n";
$mPad = rpad(array_keys($set), -1); // starts 1 char after tab
ruler();
foreach ($set as $method => [$type, $arg, $depends]) {
echo sprintf(" * @method \$this %-{$mPad}s(%s %s)", $method, $type, "\${$arg}");
if ($depends) {
echo ' @depends ';
}
echo "\n";
}
}
if ($select) {
echo " *\n";
$tPad = rpad(array_column($select, 0), 1); // type starts 1 char before tab
$mPad = rpad(array_keys($select));
ruler();
foreach ($select as $method => [$type, $eachType, $eachName]) {
echo sprintf(
" * @method %-{$tPad}s%-{$mPad}s(callable \$filter) `fn( {$eachType} \${$eachName} ): bool`\n",
$type, $method
);
}
}
echo " */\n";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment