<?php
namespace PHPFUI\InstaDoc\Section;
class Doc extends \PHPFUI\InstaDoc\Section\CodeCommon
{
private string $class;
public function __construct(\PHPFUI\InstaDoc\Controller $controller)
{
parent::__construct($controller);
}
public function generate(\PHPFUI\InstaDoc\PageInterface $page, string $fullClassPath) : \PHPFUI\Container
{
$container = new \PHPFUI\Container();
$this->class = $this->controller->getParameter(\PHPFUI\InstaDoc\Controller::NAMESPACE) . '\\' . $this->controller->getParameter(\PHPFUI\InstaDoc\Controller::CLASS_NAME);
if (empty($this->class) || '\\' === $this->class)
{
return $container;
}
try
{
$this->reflection = new \ReflectionClass($this->class);
try
{
$this->reflection->isInstantiable();
}
catch (\Throwable)
{
$this->reflection = new \ReflectionEnum($this->class);
}
}
catch (\Throwable)
{
$functionView = new \PHPFUI\InstaDoc\Section\Functions($this->controller);
$container->add($functionView->generate($page, $fullClassPath));
return $container;
}
$comments = $this->reflection->getDocComment();
$comments = \str_replace('{@inheritdoc}', '@inheritdoc', $comments);
if ($comments)
{
$docblock = $this->factory->create($comments);
$callout = new \PHPFUI\Callout('secondary');
$callout->add($this->formatComments($docblock, $this->reflection));
$container->add($callout);
}
$attributes = [
'Abstract',
'Anonymous',
'Cloneable',
'Final',
'Instantiable',
'Interface',
'Internal',
'Iterable',
'Promoted',
'Trait',
];
$row = new \PHPFUI\GridX();
foreach ($attributes as $attribute)
{
$method = 'is' . $attribute;
if (\method_exists($this->reflection, $method))
{
if ($this->reflection->{$method}())
{
$row->add($this->section($attribute));
}
}
}
if ($row->count())
{
$container->add($row);
}
$parent = $this->reflection->getParentClass();
$parentNames = [];
if ($parent)
{
while ($parent)
{
$parentNames[] = $parent->getName();
$parent = $parent->getParentClass();
}
}
$accordion = new \PHPFUI\Accordion();
$accordion->addAttribute('data-multi-expand', 'true');
$accordion->addAttribute('data-allow-all-closed', 'true');
$table = new \PHPFUI\Table();
$table->addClass('hover');
$table->addClass('unstriped');
foreach (\array_reverse($parentNames) as $name)
{
$table->addRow([$this->getClassName($name)]);
}
if (\count($table))
{
$accordion->addTab('Extends', $table);
}
$table = new \PHPFUI\Table();
$table->addClass('hover');
$table->addClass('unstriped');
foreach (\PHPFUI\InstaDoc\ChildClasses::getChildClasses($this->class) as $class)
{
$table->addRow([$this->getClassName($class)]);
}
if (\count($table))
{
$accordion->addTab('Children', $table);
}
$interfaces = $this->reflection->getInterfaces();
if ($interfaces)
{
\ksort($interfaces, SORT_FLAG_CASE | SORT_STRING);
$table = new \PHPFUI\Table();
$table->addClass('hover');
$table->addClass('unstriped');
foreach ($interfaces as $interface)
{
$table->addRow([$this->getClassName($interface->getName())]);
}
if (\count($table))
{
$accordion->addTab('Implements', $table);
}
}
$traits = $this->getTraits($this->reflection);
if ($traits)
{
\ksort($traits, SORT_FLAG_CASE | SORT_STRING);
$table = new \PHPFUI\Table();
$table->addClass('hover');
$table->addClass('unstriped');
foreach ($traits as $trait)
{
$table->addRow([$this->getClassName($trait->getName())]);
}
if (\count($table))
{
$accordion->addTab('Traits', $table);
}
}
$reflectionAttributes = $this->getAttributes($this->reflection);
if ($reflectionAttributes)
{
$table = new \PHPFUI\Table();
$table->addClass('hover');
$table->addClass('unstriped');
foreach ($reflectionAttributes as $attribute)
{
$table->addRow([$this->formatAttribute($attribute)]);
}
if (\count($table))
{
$accordion->addTab('Attributes', $table);
}
}
if (\count($accordion))
{
$container->add($accordion);
}
$parent = $this->reflection->getParentClass();
$allMenuItem = null;
if ($parentNames)
{
$parts = \array_merge(['All', 'self'], $parentNames);
$filterMenu = new \PHPFUI\Menu();
foreach ($parts as $name)
{
$menuItem = new \PHPFUI\MenuItem($name, '#');
if ('All' == $name)
{
$allMenuItem = $menuItem;
}
else
{
if ('self' == $name)
{
$menuItem->setActive();
}
$menuItem->addClass('visMenu');
$menuItem->addAttribute('onclick', '$(this).toggleClass("is-active");$(".' .
$this->getHtmlClass($name) . '").toggleClass("hide")');
}
$filterMenu->addMenuItem($menuItem);
}
\array_shift($parts);
$allSelector = '';
$comma = '';
foreach ($parts as $name)
{
$allSelector .= $comma . '.' . $this->getHtmlClass($name);
$comma = ', ';
}
$allMenuItem->addAttribute('onclick', 'if($(this).hasClass("is-active")){$(".visMenu").removeClass("is-active");$("' .
$allSelector . '").addClass("hide")}else{$(".visMenu").addClass("is-active");$("' . $allSelector .
'").removeClass("hide")};$(this).toggleClass("is-active")');
$container->add($filterMenu);
}
$tabs = new \PHPFUI\Tabs();
$first = true;
foreach ($this->controller->getAccessTabs() as $section)
{
$table = $this->getContent('is' . $section);
if (\count($table))
{
$tabs->addTab($section, $table, $first);
$first = false;
}
}
$container->add($tabs);
return $container;
}
protected function getAccess(\ReflectionProperty|\ReflectionClassConstant $constant) : string
{
if ($constant->isPrivate())
{
$type = $this->getColor('keyword', 'private');
}
elseif ($constant->isProtected())
{
$type = $this->getColor('keyword', 'protected');
}
else
{
$type = $this->getColor('keyword', 'public');
}
if (\method_exists($constant, 'isReadOnly') && $constant->isReadOnly())
{
$type .= ' ' . $this->getColor('keyword', 'readonly');
}
return $type;
}
protected function getConstant(\ReflectionClassConstant $constant, string $name, mixed $value) : string
{
$docBlock = $this->getDocBlock($constant);
$info = $this->getAccess($constant) . ' ' . $this->getColor('constant', $this->getColor('constant', $this->getName($constant, $name, true)));
$valueString = $this->getValueString($value);
if (\strlen($valueString))
{
$info .= ' = ' . $valueString;
}
$info .= $this->getComments($docBlock);
return $info;
}
protected function getContent(string $accessType) : \PHPFUI\Table
{
$table = new \PHPFUI\Table();
$table->addClass('hover');
$table->addClass('unstriped');
$table->addClass('stack');
$constants = $this->reflection->getConstants();
if ($constants && 'isStatic' != $accessType)
{
\ksort($constants, SORT_FLAG_CASE | SORT_STRING);
$section = 'Constants';
foreach ($constants as $name => $value)
{
$constant = new \ReflectionClassConstant($this->class, $name);
if ($constant->{$accessType}())
{
if ($section)
{
$table->addRow([$this->section($section)]);
$section = '';
}
$table->addNextRowAttribute('class', $this->getRowClasses($constant));
$table->addRow([$this->getConstant($constant, $name, $value)]);
}
}
}
$properties = $this->reflection->getProperties();
if ($properties)
{
$this->objectSort($properties);
$section = 'Properties';
foreach ($properties as $property)
{
if ($property->{$accessType}())
{
if ($section)
{
$table->addRow([$this->section($section)]);
$section = '';
}
$table->addNextRowAttribute('class', $this->getRowClasses($property));
$table->addRow([$this->getProperty($property, $this->reflection)]);
}
}
}
$methods = $this->reflection->getMethods();
if ($methods)
{
$this->objectSort($methods);
$section = 'Methods';
foreach ($methods as $method)
{
if ($method->{$accessType}())
{
if ($section)
{
$table->addRow([$this->section($section)]);
$section = '';
}
$table->addNextRowAttribute('class', $this->getRowClasses($method));
$table->addRow([$this->getMethod($method)]);
}
}
}
return $table;
}
protected function getMethod(\ReflectionMethod $method) : string
{
$info = '';
$types = ['public', 'protected', 'private', 'abstract', 'final', 'static'];
foreach ($types as $type)
{
$isType = 'is' . \ucfirst($type);
if ($method->{$isType}())
{
$info .= $this->getColor('keyword', $type) . ' ';
}
}
$info .= $this->getName($method, $this->getColor('name', $method->name));
$info .= $this->getMethodParametersBlock($method);
return $info;
}
protected function getName(\ReflectionClassConstant|\ReflectionMethod|\ReflectionProperty $method, string $name, bool $fullyQualify = false) : string
{
$parent = $this->getNameScope($method, $fullyQualify);
if ($parent)
{
$link = $this->getClassName($parent, $fullyQualify);
$name = $link . '::' . $name;
}
return $name;
}
protected function getNameScope(\ReflectionProperty|\ReflectionMethod|\ReflectionClassConstant $method, bool $fullyQualify = false) : string
{
$parent = $method->getDeclaringClass();
if ($fullyQualify || ($parent->getName() != $this->reflection->getName()))
{
return $parent->getName();
}
return '';
}
protected function getProperty(\ReflectionProperty $property, \ReflectionClass $class) : string
{
$docBlock = $this->getDocBlock($property);
$info = $this->getAccess($property) . ' ';
if ($property->isStatic())
{
$info .= $this->getColor('keyword', 'static') . ' ';
}
$type = $property->getType();
if ($type)
{
$info .= $this->getColor('type', $this->getClassName($type)) . ' ';
}
$info .= $this->getName($property, $this->getColor('variable', '$' . $property->getName()));
if ($property->isStatic())
{
if ($property->hasDefaultValue())
{
$value = $property->getValue();
$info .= ' = ' . $this->getValueString($value);
}
}
else
{
$defaults = $class->getDefaultProperties();
if (\array_key_exists($property->getName(), $defaults))
{
$info .= ' = ' . $this->getValueString($defaults[$property->getName()]);
}
}
$info .= $this->getComments($docBlock);
return $info;
}
protected function getRowClasses(\ReflectionClassConstant|\ReflectionMethod|\ReflectionProperty $method) : string
{
$class = $this->getNameScope($method);
if (\strlen($class))
{
return $this->getHtmlClass($class) . ' hide';
}
return 'self';
}
protected function objectCompare(object $lhs, object $rhs) : int
{
return \strcasecmp($lhs->name, $rhs->name);
}
protected function objectSort(array &$objects) : void
{
\usort($objects, [$this, 'objectCompare']);
}
private function getTraits(\ReflectionClass $reflection) : array
{
$traits = [];
$parent = $reflection->getParentClass();
if ($parent)
{
$traits = $this->getTraits($parent);
}
return \array_merge($traits, $reflection->getTraits());
}
}