stub2ref
[mdref/mdref] / mdref / Tree.php
1 <?php
2
3 namespace mdref;
4
5 use Iterator;
6 use RecursiveIterator;
7 use function array_filter;
8 use function array_search;
9 use function basename;
10 use function current;
11 use function dirname;
12 use function glob;
13 use function is_dir;
14 use function key;
15 use function next;
16 use function pathinfo;
17 use function preg_match;
18 use function reset;
19 use function strcmp;
20 use function usort;
21
22 class Tree implements RecursiveIterator {
23 /**
24 * The repository
25 * @var \mdref\Repo
26 */
27 private $repo;
28
29 /**
30 * List of first level entries
31 * @var array
32 */
33 private $list = array();
34
35 /**
36 * The list iterator
37 * @var array
38 */
39 private $iter;
40
41 /**
42 * @param string $path
43 * @param \mdref\Repo $repo
44 */
45 public function __construct(string $path, Repo $repo) {
46 if (realpath($path)."/" === $repo->getPath()) {
47 $list = [$path ."/". $repo->getName() .".md"];
48 } elseif (!($list = glob("$path/*.md"))) {
49 $list = glob("$path/*/*.md");
50 }
51 if ($list) {
52 $this->list = array_filter($list, $this->generateFilter($list));
53 usort($this->list, $this->generateSorter());
54 }
55 $this->repo = $repo;
56 }
57
58 /**
59 * @param array $list
60 * @return callable
61 */
62 private function generateFilter(array $list) : \Closure {
63 return function($v) use($list) {
64 if ($v[0] === ".") {
65 return false;
66 }
67 if (false !== array_search("$v.md", $list, true)) {
68 return false;
69 }
70
71 $pi = pathinfo($v);
72 if (isset($pi["extension"]) && "md" !== $pi["extension"]) {
73 return false;
74 }
75
76 return true;
77 };
78 }
79
80 /**
81 * @return callable
82 */
83 private function generateSorter() : \Closure {
84 return function($a, $b) {
85 $ab = basename($a, ".md");
86 $bb = basename($b, ".md");
87
88 if ($ab[0] === ":" && $bb[0] === ":") {
89 return strcmp($ab, $bb);
90 } elseif ($ab[0] === ":") {
91 return -1;
92 } elseif ($bb[0] === ":") {
93 return 1;
94 }
95 /*
96 $ad = is_dir(dirname($a)."/$ab");
97 $bd = is_dir(dirname($b)."/$bb");
98
99 if ($ad && $bd) {
100 return strcmp($ab, $bb);
101 } elseif ($ad) {
102 return -1;
103 } elseif ($bd) {
104 return 1;
105 }
106 */
107 $au = preg_match("/^\p{Lu}/", $ab);
108 $bu = preg_match("/^\p{Lu}/", $bb);
109
110 if ($au && $bu) {
111 return strcmp($ab, $bb);
112 } elseif ($au) {
113 return -1;
114 } elseif ($bu) {
115 return 1;
116 }
117
118 return strcmp($ab, $bb);
119 };
120 }
121
122 /**
123 * Implements \Iterator
124 * @return \mdref\Entry
125 */
126 public function current() : Entry {
127 return $this->repo->getEntry($this->repo->hasFile(current($this->iter)));
128 }
129
130 /**
131 * Implements \Iterator
132 */
133 public function next() : void {
134 next($this->iter);
135 }
136
137 /**
138 * Implements \Iterator
139 * @return int
140 */
141 public function key() : int {
142 return key($this->iter);
143 }
144
145 /**
146 * Implements \Iterator
147 */
148 public function rewind() : void {
149 $this->iter = $this->list;
150 reset($this->iter);
151 }
152
153 /**
154 * Implements \Iterator
155 * @return bool
156 */
157 public function valid() : bool {
158 return null !== key($this->iter);
159 }
160
161 /**
162 * Implements \RecursiveIterator
163 * @return bool
164 */
165 public function hasChildren() : bool {
166 return $this->current()->hasIterator();
167 }
168
169 /**
170 * Implements \RecursiveIterator
171 * @return \mdref\Tree
172 */
173 public function getChildren() : Tree {
174 return $this->current()->getIterator();
175 }
176 }