Skip to content

Commit 305dadf

Browse files
committed
Start of Doc page
1 parent bbec64f commit 305dadf

File tree

5 files changed

+347
-27
lines changed

5 files changed

+347
-27
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ But with PHPFUI\InstaDoc, you can document your site in about a minute (OK, mayb
1616
* Documents all projects loaded via Composer automatically
1717
* Add any local repo directories
1818
* Remove any Composer project you don't care about
19-
* Custom ordering of types (const, static, public, protected, etc.)
19+
* Tabbed documentation so you are not looking at irrelevant methods
2020
* Alphabetized everything, no more searching unalphabetized pages!
2121
* Support for markdown and custom markdown pages
2222
* Quick access to highlighed PHP source with user selectable highlighting
@@ -52,6 +52,5 @@ That is it. You are done!
5252

5353
#### To Do List:
5454

55-
* Create Doc page (the whole point, but do the easy stuff first!)
5655
* Add more documentation
5756
* .git page

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"phpfui/phpfui": "^6.0",
1717
"erusev/parsedown": "^1.7",
1818
"gitonomy/gitlib": "dev-master",
19-
"scrivo/highlight.php": "^9.17"
19+
"scrivo/highlight.php": "^9.17",
20+
"phpdocumentor/reflection-docblock": "^4.3"
2021
},
2122
"autoload": {
2223
"psr-0": {"PHPFUI\\InstaDoc\\": "src/"}

src/PHPFUI/InstaDoc/Section/Doc.php

Lines changed: 299 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,310 @@
55
class Doc extends \PHPFUI\InstaDoc\Section
66
{
77

8+
private $factory;
9+
private $reflection;
10+
private $fullClassPath;
11+
812
public function generate(\PHPFUI\Page $page, string $fullClassPath) : \PHPFUI\Container
913
{
1014
$container = new \PHPFUI\Container();
1115

12-
$container->add(new \PHPFUI\SubHeader('Docs'));
13-
$fullClassPath = str_replace('\\', '/', $fullClassPath);
14-
$container->add(new \PHPFUI\SubHeader($fullClassPath));
16+
$parameters = $this->controller->getParameters();
17+
// $page->addStyleSheet("highlighter/styles/{$parameters['CSS']}.css");
18+
$page->addStyleSheet("highlighter/styles/qtcreator_light.css");
19+
$this->factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
20+
$this->fullClassPath = $fullClassPath;
21+
$this->fullClassPath = str_replace(['/', '.php'], ['\\', ''], $this->fullClassPath);
22+
$start = strpos($this->fullClassPath, '.');
23+
if ($start !== false)
24+
{
25+
$this->fullClassPath = substr($this->fullClassPath, $start + 3);
26+
}
27+
28+
$this->reflection = new \ReflectionClass($this->fullClassPath);
29+
30+
$table = new \PHPFUI\Table();
31+
$table->addClass('hover');
32+
$table->addClass('unstriped');
33+
$table->addClass('stack');
34+
$parent = $this->reflection->getParentClass();
35+
if ($parent)
36+
{
37+
$table->addRow([$this->section('Extends'), $this->getClassName($parent->getName())]);
38+
}
39+
40+
$interfaces = $this->reflection->getInterfaces();
41+
if ($interfaces)
42+
{
43+
ksort($interfaces);
44+
$section = 'Implements';
45+
foreach ($interfaces as $interface)
46+
{
47+
$class = $interface->getName();
48+
$table->addRow([$this->section($section), $this->getClassName($interface->getName())]);
49+
$section = '';
50+
}
51+
}
52+
53+
$container->add($table);
54+
55+
$tabs = new \PHPFUI\Tabs();
56+
$tabs->addTab('Public', $this->getContent('isPublic'), true);
57+
$tabs->addTab('Protected', $this->getContent('isProtected'));
58+
$tabs->addTab('Private', $this->getContent('isPrivate'));
59+
$tabs->addTab('Static', $this->getContent('isStatic'));
60+
61+
$container->add($tabs);
1562

1663
return $container;
1764
}
65+
66+
private function getContent(string $accessType) : \PHPFUI\Table
67+
{
68+
$table = new \PHPFUI\Table();
69+
$table->addClass('hover');
70+
$table->addClass('unstriped');
71+
$table->addClass('stack');
72+
73+
$constants = $this->reflection->getConstants();
74+
if ($constants)
75+
{
76+
ksort($constants);
77+
$section = 'Constants';
78+
foreach ($constants as $name => $value)
79+
{
80+
$constant = new \ReflectionClassConstant($this->fullClassPath, $name);
81+
82+
if ($accessType != 'isStatic' && $constant->$accessType())
83+
{
84+
$info = $this->getAccess($constant) . ' ' . $this->getName($constant, $name) . ' = ' . $this->getValueString($value);
85+
86+
$info .= $this->getComments($constant);
87+
88+
$table->addRow([$this->section($section), $info]);
89+
$section = '';
90+
}
91+
}
92+
}
93+
94+
$properties = $this->reflection->getProperties();
95+
if ($properties)
96+
{
97+
$this->objectSort($properties);
98+
$section = 'Properties';
99+
foreach ($properties as $property)
100+
{
101+
if ($property->$accessType())
102+
{
103+
$info = $this->getAccess($property) . ' ';
104+
if ($property->isStatic())
105+
{
106+
$info .= 'static ';
107+
}
108+
$type = method_exists($property, 'getType') ? $property->getType() : '';
109+
if ($type)
110+
{
111+
$info .= $type->getName() . ' ';
112+
}
113+
114+
$info .= $this->getName($property, '$' . $property->getName());
115+
116+
$info .= $this->getComments($property);
117+
118+
$table->addRow([$this->section($section), $info]);
119+
$section = '';
120+
}
121+
}
122+
}
123+
124+
$methods = $this->reflection->getMethods();
125+
if ($methods)
126+
{
127+
$this->objectSort($methods);
128+
$section = 'Methods';
129+
foreach ($methods as $method)
130+
{
131+
if ($method->$accessType())
132+
{
133+
$info = $this->getAccess($method) . ' ';
134+
if ($method->isStatic())
135+
{
136+
$info .= 'static ';
137+
}
138+
139+
$info .= $this->getName($method, $method->name) . '(';
140+
$comma = '';
141+
foreach ($method->getParameters() as $parameter)
142+
{
143+
$info .= $comma;
144+
$comma = ', ';
145+
if ($parameter->hasType())
146+
{
147+
$type = $parameter->getType();
148+
if ($type->allowsNull())
149+
{
150+
$info .= '?';
151+
}
152+
$info .= $this->getClassName($type);
153+
}
154+
else
155+
{
156+
$info .= 'mixed';
157+
}
158+
$info .= ' ';
159+
$info .= '$' . $parameter->getName();
160+
if ($parameter->isDefaultValueAvailable())
161+
{
162+
$value = $parameter->getDefaultValue();
163+
$info .= ' = ' . $this->getValueString($value);
164+
}
165+
}
166+
$info .= ')';
167+
if ($method->hasReturnType())
168+
{
169+
$info .= ' : ' . $this->getClassName($method->getReturnType()->getName());
170+
}
171+
$info .= $this->getComments($method);
172+
173+
$table->addRow([$this->section($section), $info]);
174+
$section = '';
175+
}
176+
}
177+
}
178+
179+
return $table;
180+
}
181+
182+
private function getAccess($constant) : string
183+
{
184+
$span = new \PHPFUI\HTML5Element('span');
185+
$span->addClass('hljs-keyword');
186+
187+
if ($constant->isPrivate())
188+
{
189+
$span->add('private');
190+
}
191+
elseif ($constant->isProtected())
192+
{
193+
$span->add('protected');
194+
}
195+
else
196+
{
197+
$span->add('public');
198+
}
199+
200+
return $span;
201+
}
202+
203+
private function getClassName(string $class) : string
204+
{
205+
if (strpos($class, '\\') !== false)
206+
{
207+
return new \PHPFUI\Link($this->controller->getClassUrl($class), $class, false);
208+
}
209+
210+
$span = new \PHPFUI\HTML5Element('span');
211+
$span->add($class);
212+
$span->addClass('hljs-type');
213+
214+
return $span;
215+
}
216+
217+
private function section(string $name) : string
218+
{
219+
if (! $name)
220+
{
221+
return $name;
222+
}
223+
224+
$section = new \PHPFUI\HTML5Element('span');
225+
$section->add($name);
226+
$section->addClass('callout');
227+
$section->addClass('small');
228+
$section->addClass('primary');
229+
230+
return $section;
231+
}
232+
233+
private function getName($method, string $name) : string
234+
{
235+
$parent = $method->getDeclaringClass();
236+
if ($parent->getName() != $this->reflection->getName())
237+
{
238+
$link = $this->getClassName($parent->getName());
239+
$name = $link . '::' . $name;
240+
}
241+
242+
return $name;
243+
}
244+
245+
private function getComments($method) : string
246+
{
247+
$comments = $method->getDocComment();
248+
if (! $comments)
249+
{
250+
return '';
251+
}
252+
253+
$docblock = $this->factory->create($comments);
254+
255+
$gridX = new \PHPFUI\GridX();
256+
$cell1 = new \PHPFUI\Cell(1);
257+
$cell1->add(' ');
258+
$gridX->add($cell1);
259+
$cell11 = new \PHPFUI\Cell(11);
260+
$cell11->add($docblock->getSummary());
261+
$gridX->add($cell11);
262+
263+
return $gridX;
264+
}
265+
266+
private function objectSort(array &$objects) : void
267+
{
268+
usort($objects, [$this, 'objectCompare']);
269+
}
270+
271+
private function objectCompare($lhs, $rhs) : int
272+
{
273+
return $lhs->name <=> $rhs->name;
274+
}
275+
276+
private function getValueString($value) : string
277+
{
278+
switch (gettype($value))
279+
{
280+
case 'array':
281+
$text = '[';
282+
$comma = '';
283+
foreach ($value as $part)
284+
{
285+
$text .= $comma . $this->getValueString($part);
286+
$comma = ', ';
287+
}
288+
$text .= ']';
289+
$value = $text;
290+
break;
291+
case 'string':
292+
$span = new \PHPFUI\HTML5Element('span');
293+
$span->addClass('hljs-string');
294+
$span->add("'{$value}'");
295+
$value = $span;
296+
break;
297+
case 'object':
298+
$value = $this->getClassName(get_class($value));
299+
break;
300+
case 'resource':
301+
$value = 'resource';
302+
break;
303+
case 'boolean':
304+
$value = $value ? 'true' : 'false';
305+
break;
306+
case 'NULL':
307+
$value = 'NULL';
308+
break;
309+
}
310+
311+
return $value;
312+
}
313+
18314
}

src/PHPFUI/InstaDoc/Section/Git.php

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,44 @@
55
class Git extends \PHPFUI\InstaDoc\Section
66
{
77

8+
private function displayTree(\PHPFUI\HTML5Element $container, \Gitonomy\Git\Tree $tree, int $indent = 0)
9+
{
10+
$tabs = str_repeat("\t", $indent);
11+
foreach ($tree->getEntries() as $name => $data)
12+
{
13+
list($mode, $entry) = $data;
14+
if ($entry instanceof \Gitonomy\Git\Tree)
15+
{
16+
$container->add($tabs.$name."/");
17+
$this->displayTree($container, $tree, $indent + 1);
18+
}
19+
else
20+
{
21+
$container->add($tabs.$name."\n");
22+
}
23+
}
24+
}
25+
826
public function generate(\PHPFUI\Page $page, string $fullClassPath) : \PHPFUI\Container
927
{
1028
$container = new \PHPFUI\Container();
1129

12-
// $repo = new \Gitonomy\Git\Repository($_SERVER['DOCUMENT_ROOT'] . '/..');
13-
// $tree = $repo->getHeadCommit()->getTree();
14-
// $fullClassPath = substr(str_replace('\\', '/', $fullClassPath), 3);
15-
// $source = $tree->resolvePath($fullClassPath);
16-
// $entries = $source->getEntries();
17-
// $pre = new \PHPFUI\HTML5Element('pre');
18-
// $pre->add($fullClassPath);
19-
// $pre->add(print_r($entries, true));
20-
// $container->add($pre);
21-
$container->add('.git section coming soon.');
30+
$repo = new \Gitonomy\Git\Repository($_SERVER['DOCUMENT_ROOT'] . '/..');
31+
$result = $repo->run('show-branch');
32+
$branch = substr($result, strpos($result, '[') + 1, strpos($result, ']') - 1);
33+
$fullClassPath = substr(str_replace('\\', '/', $fullClassPath), 3);
34+
$log = $repo->getLog($branch, $fullClassPath, 0, 10);
35+
$container->add(get_class($log));
36+
$table = new \PHPFUI\Table();
37+
$table->setHeaders(['Title', 'Date']);
38+
foreach ($log->getCommits() as $commit)
39+
{
40+
$container->add($commit->getShortMessage());
41+
$row['Title'] = $commit->getShortMessage();
42+
$container->add($commit->getCommitterDate()->setTimezone($localTZ)->format('Y-m-d g:i a'));
43+
$table->addRow($row);
44+
}
45+
$container->add($table);
2246

2347
return $container;
2448
}

0 commit comments

Comments
 (0)