-
-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Documentation #64
Comments
do we have an example on how documentation files should look like ? |
I don't have an example in mind, but one thing to note is that we want to document generic functions, so instead of: function at(array $array, $key) it would be: function at<Tk as array-key, Tv>(array<Tk, Tv> $array, Tk $key): Tv as that's how the function would look like if PHP had generics, but currently we use Psalm for that. Note, when using generics in code, i would recommand using example:
Another thing is that there's no need to have a folder for each version, as once we are done with the ideal structure for the files would be something like this:
so that every component can have its own folder, with a readme.md, in that readme we would also list all functions/classes of that component, with links to their documentation ( docs/str/README.md ): example: # Str
<-- documentation -->
## constants
- [`Psl\Str\FOO`](constants.md)
- [`Psl\Str\BAR`](constants.md)
## functions
- [`Psl\Str\replace`](function/replace.function.md)
- [`Psl\Str\replace_ci`](function/replace_ci.md)
...
## interfaces
- [`Psl\Str\SomeInterface`](interface/someinterface.md)
## classes
- [`Psl\Str\SomeClass`](class/someclass.md)
## traits
- [`Psl\Str\SomeTrait`](trait/sometrait.md)
|
by the way, I would suggest documenting 1 component at the time and sending a PR for that instead of trying to document everything together, so it makes things easier to review, and we can focus on one thing at the time. |
Currently the code also contains detailed docblocks with code samples in various places. |
I have actually tried to do that in the past but gave up on it. this is the generated I previously wrote ( a bit messy and incomplete ): <?php
declare(strict_types=1);
namespace Local;
use Psl\{Arr, Str};
use Roave\BetterReflection\BetterReflection;
use Roave\BetterReflection\Reflection\ReflectionClass;
use Roave\BetterReflection\Reflection\ReflectionConstant;
use Roave\BetterReflection\Reflection\ReflectionFunction;
use Roave\BetterReflection\Reflector\ConstantReflector;
use Roave\BetterReflection\SourceLocator\Type\SingleFileSourceLocator;
use RuntimeException;
require_once __DIR__ . '/../vendor/autoload.php';
$symbols = [];
$symbols['constants'] = get_defined_constants();
$symbols['functions'] = get_defined_functions()['user'];
$symbols['classes'] = get_declared_classes();
$symbols['interfaces'] = get_declared_interfaces();
$symbols['traits'] = get_declared_traits();
$extract_component = static function (string $type): ?string {
if (!Str\starts_with($type, 'Psl\\')) {
return null;
}
if (Str\contains($type, 'Internal')) {
return null;
}
$name = Str\slice($type, Str\search_last($type, '\\') + 1);
$component = Str\strip_suffix($type, Str\concat('\\', $name));
$component = Str\strip_prefix($component, 'Psl\\');
return $component;
};
$fix_function_casing = static function (string $function): string {
$parts = Str\split($function, '\\');
$parts = Arr\map($parts, fn(string $part) => Str\capitalize($part));
$parts[Arr\last_key($parts)] = Str\lowercase(Arr\last($parts));
return Str\join($parts, '\\');
};
/**
* @psalm-var array<string, array{
* constants: list<string>,
* functions: list<string>,
* classes: list<string>,
* interfaces: list<string>,
* traits: list<string>
* }> $components
*/
$components = [];
foreach ($symbols['constants'] as $symbol => $value) {
$component = $extract_component($symbol);
if (null === $component) {
continue;
}
$components[$component]['constants'][] = $symbol;
}
foreach (['functions', 'classes', 'interfaces', 'traits'] as $type) {
foreach ($symbols[$type] as $symbol) {
if ('functions' === $type) {
$symbol = $fix_function_casing($symbol);
}
$component = $extract_component($symbol);
if (null === $component) {
continue;
}
$components[$component][$type][] = $symbol;
}
}
/**
* @param ReflectionConstant|ReflectionClass|ReflectionFunction $reflection
*/
$extract_docblock = static function($reflection): ?string {
$doc = $reflection->getDocComment();
$lines = Str\split(Str\trim($doc), "\n");
if (0 === Arr\count($lines) || (1 === Arr\count($lines) && Str\is_empty(Arr\first($lines)))) {
return null;
}
$lines = Arr\slice($lines, 1, Arr\count($lines) - 2);
$lines = Arr\map($lines, fn(string $line) => Str\strip_prefix($line, ' * '));
$lines = Arr\map($lines, fn(string $line) => Str\strip_prefix($line, ' *'));
$lines = Arr\filter($lines);
$lines = Arr\filter($lines, fn(string $line) => !Str\starts_with($line, '@'));
$lines = Arr\map($lines, static function(string $line): string {
if (Str\starts_with($line, ' ')) {
return Str\format(' %s', $line);
}
if (Str\ends_with($line, ':')) {
return Str\format(' %s%s', $line, "\n");
}
return Str\format(' %s', $line);
});
return Str\join($lines, "\n");
};
$generate_constants_documentation = static function(string $component, array $constants) use($extract_docblock): string {
$docs = <<<MARKDOWN
## Predefined Constants
The constants below are defined by the ${component} component of PSL.
MARKDOWN;
$br = (new BetterReflection());
$ref = new ConstantReflector(new SingleFileSourceLocator(
Str\format('%s/%s/constants.php', __DIR__.'/../src/Psl', $component),
$br->astLocator()
), $br->classReflector());
foreach ($constants as $constant) {
$reflection = $ref->reflect($constant);
$docblock = $extract_docblock($reflection);
$type = gettype(constant($constant));
$type = 'double' === $type ? 'float' : $type;
$nl = "\n";
if ($docblock) {
$nl .= <<<DOCS
${nl}${docblock}{$nl}{$nl}
DOCS;
}
$docs .= <<<DOCS
- *${constant}* ( ${type} )${nl}
DOCS;
}
return $docs;
};
$generate_documentation = static function(string $type, string $symbol): string {
die();
};
$mkdir = static function (string $directory): void {
if (!is_dir($directory) && !mkdir($directory) && !is_dir($directory)) {
throw new RuntimeException(sprintf('Directory "%s" was not created', $directory));
}
};
foreach($components as $component => $symbols) {
$directory = Str\format('%s/../docs/%s', __DIR__, Str\lowercase(Str\replace($component, '\\', '/')));
$mkdir($directory);
foreach ($symbols as $type => $list_of_symbols) {
if ('constants' === $type) {
$file = Str\format('%s/%s.md', $directory, $type);
$documentation = $generate_constants_documentation($component, $list_of_symbols);
file_put_contents($file, $documentation);
} else {
$sub_directory = Str\format('%s/%s', $directory, $type);
$mkdir($sub_directory);
continue;
foreach ($list_of_symbols as $symbol) {
$file = Str\format('%s/%s.md', $sub_directory, $symbol);
$documentation = $generate_documentation($type, $symbol);
file_put_contents($file, $documentation);
}
}
}
} cc @lotfio if you want to do that, you can use the generator as a starting point. |
We should probably have documentation that people can use to lookup functions and their descriptions + a couple of examples.
The text was updated successfully, but these errors were encountered: