PageRenderTime 290ms CodeModel.GetById 131ms app.highlight 74ms RepoModel.GetById 66ms app.codeStats 14ms

/generator.php

https://github.com/bjori/PHit
PHP | 368 lines | 336 code | 30 blank | 2 comment | 58 complexity | d83688eecce0170bbb72370864bf4fda MD5 | raw file
  1<?php
  2
  3function render($include, array $data = null) {
  4	if ($data) {
  5		extract($data);
  6	}
  7	ob_start();
  8	include $include;
  9	return ob_get_clean();
 10}
 11
 12function untab($text) {
 13	$text = preg_replace("/^\t/m", "", $text);
 14	$text = preg_replace("/^    /m", "\t", $text);
 15    return $text;
 16}
 17
 18function map_iface($i) {
 19	$map = [
 20        "IteratorAggregate" => "zend_ce_aggregate",
 21		"Iterator" => "spl_ce_Iterator",
 22		"Countable" => "spl_ce_Countable",
 23	];
 24    $user = substr($i, strrpos($i, "\\")+1);
 25
 26    $user = strtolower($user);
 27	return isset($map[$i]) ? $map[$i] : "php_phongo_{$user}_ce";
 28}
 29
 30if ($_SERVER["argc"] !== 2 || !is_file($_SERVER["argv"][1])) {
 31	printf("Usage:\n\tphp -dauto_prepend_file=README.md %s php_phongo.c.in >php_phongo.c\n", 
 32		basename($_SERVER["argv"][0]));
 33	var_dump($argc,$argv);
 34	exit(-1);
 35}
 36
 37$interfaces = $classes = array();
 38foreach(array(get_declared_interfaces(), get_declared_classes()) as $entry) {
 39    foreach ($entry as $iface) {
 40        $reflection = new ReflectionClass($iface);
 41
 42        if ($reflection->isInternal()) {
 43            fprintf(STDERR, "Skipping $iface\n");
 44            continue;
 45        }
 46        if ($reflection->isInterface()) {
 47            $interfaces[$reflection->getShortName()] = $iface;
 48        } else {
 49            $classes[$reflection->getShortName()] = $iface;
 50        }
 51    }
 52}
 53foreach(array($interfaces, $classes) as $entry) {
 54    foreach ($entry as $iface) {
 55$declarations = "";
 56$registrations = "";
 57$implementations = "";
 58
 59        $reflection = new ReflectionClass($iface);
 60        $pos = strrpos($iface, "\\");
 61        $ns = substr($iface, 0, $pos);
 62        $class = substr($iface, $pos + 1);
 63        $ns = str_replace("\\", "\\\\", $ns);
 64
 65        $config = isset($$class) ? $$class : array();
 66
 67        $docblock = untab($reflection->getDocComment())."\n";
 68        if ($reflection->isInterface()) {
 69            $classorinterface = "interface";
 70            $config["generate_handlers"] = false;
 71        } else {
 72            $classorinterface = "class";
 73        }
 74        $isfinalclass = $reflection->isFinal();
 75        if ($parent = $reflection->getParentClass()) {
 76            $parent = $parent->getName();
 77        } else {
 78            $parent = "NULL";
 79        }
 80
 81
 82        $registrations .= render("generator/$classorinterface.php", 
 83            compact("ns", "class", "docblock", "parent", "isfinalclass", "config"));
 84        $entries = "";
 85        $arginfos = "";
 86
 87        if (($impl_ifaces = $reflection->getInterfaceNames())) {
 88            $faces = array();
 89            foreach($impl_ifaces as $face) {
 90                if ($face == "Traversable") {
 91                    continue;
 92                }
 93                foreach($faces as $f) {
 94                    $rf = null;
 95                    try {
 96                        $rf = new ReflectionClass($f);
 97                    } catch(Exception $e) {
 98                    }
 99                    if (!$rf || $rf->implementsInterface($face)) {
100                        continue 2;
101                    }
102                }
103                $faces[] = map_iface($face);
104            }
105            $ni = count($faces);
106            $impl_list = implode(", ", $faces);
107            $registrations .= render("generator/implements.php", 
108                compact("class", "ni", "impl_list", "config"));
109        }
110        foreach ($reflection->getConstants() as $cname => $cvalue) {
111            $registrations .= render("generator/constant.php",
112                compact("class", "cname", "cvalue", "config"));
113        }
114        foreach ($reflection->getMethods() as $m) {
115            $zpp = array();
116            /* @var $m ReflectionMethod */
117            if ($m->getDeclaringClass()->getName() !== $iface) {
118                continue;
119            }
120            $method = $m->getName();
121            $docblock = untab($m->getDocComment())."\n";
122            $template = $reflection->isInterface() ? "abstract_me.php" : "me.php";
123            $flags = "ZEND_ACC_PUBLIC";
124            $flags .= $reflection->isFinal() ? "|ZEND_ACC_FINAL" : "";
125            $entries .= render("generator/$template", 
126                compact("class", "method", "docblock", "flags", "config"));
127            $args = "";
128            $req_args = 0;
129            $protoargs = "";
130            $n = 0;
131            foreach ($m->getParameters() as $n => $param) {
132                if ($n > 0) {
133                    if ($param->isOptional()) {
134                        $protoargs .= "[, ";
135                    } else {
136                        $protoargs .= ", ";
137                    }
138                }
139
140
141
142
143                /* @var $param ReflectionParameter */
144                $arg = $param->getName();
145                $null = (int) $param->allowsNull();
146                $by_ref = (int) $param->isPassedByReference();
147                try {
148                    $param->getClass();
149                } catch(Exception $e) {
150                    echo "Broken typehint:\n";
151                    echo $class, "::", $m->getName();
152                    exit;
153                }
154
155                list($type, $short, $argprotohint) = get_zpp_type($n+1, $m->getNumberOfParameters(), $param, $docblock);
156                if ($param->isArray()) {
157                    $args .= render("generator/ai_arr.php", 
158                        compact("class", "method", "arg", "null", "by_ref", "config"));
159                    $protoargs .= "array \$$arg";
160                    if ($param->isOptional()) {
161                        $protoargs .= " = array()";
162                    }
163                } elseif (($arg_class = $param->getClass())) {
164                    $arg_class = $arg_class->getName();
165                    $protoargs .= "$arg_class \$$arg";
166                    $args .= render("generator/ai_obj.php", 
167                        compact("class", "method", "arg", "null", "by_ref", "arg_class", "config"));
168                    if ($param->isOptional()) {
169                        $protoargs .= " = null";
170                    }
171                } else {
172                    $callable = $param->isCallable();
173                    $protoargs .= $callable ? "callable \$$arg" : "$argprotohint \$$arg";
174                    $args .= render("generator/ai_std.php", 
175                        compact("class", "method", "arg", "null", "by_ref", "callable", "config"));
176                }
177                if ($param->isDefaultValueAvailable()) {
178                    $default = $param->getDefaultValue();
179                    if ($default === NULL) {
180                        $default = "NULL";
181                    } elseif ($default === true) {
182                        $default = 1;
183                    } elseif ($default === false) {
184                        $default = 0;
185                    } elseif ($default === array()) {
186                        $default = "NULL";
187                    }
188                } else {
189                    $default = "";
190                }
191                $zpp[] = array(
192                    "type" => $type,
193                    "short" => $short,
194                    "name"  => $arg,
195                    "default" => $default,
196                );
197                $req_args += !$param->isOptional();
198            }
199            $classtype = "php_phongo_{$class}_t";
200            $codes = get_code_from($m->getFilename(), $m->getStartLine(), $m->getEndLine());
201            $code = render("generator/zpp.php", 
202                compact("zpp", "req_args", "classtype", "codes", "config"));
203            if ($n>0) {
204                $protoargs .= str_repeat("]", ($n+1)-$req_args);
205            }
206
207            $arginfos .= render("generator/arginfos.php", 
208                compact("class", "method", "args", "req_args", "docblock", "config"));
209
210            $oneliner = explode("\n", $docblock)[1];
211            $oneliner = "   " . trim($oneliner, " *\t");
212            $rettype = get_return_type($docblock);
213            if ($method == "__construct") {
214                $rettype = str_replace("\\\\", "\\", $ns) . "\\" . $class;
215            }
216            if (!$reflection->isInterface()) {
217                $implementations .= render("generator/implementations.php", 
218                    compact("class", "method", "oneliner", "protoargs", "code", "rettype", "config"));
219            }
220        }
221        $docblock = untab($reflection->getDocComment())."\n";
222        $declarations .= render("generator/declaration.php", 
223            compact("ns", "class", "entries", "docblock", "arginfos", "config"));
224        $minitname = $class;
225        $data = render($_SERVER["argv"][1], compact("declarations", "registrations", "implementations", "minitname", "class", "config"));
226        $namespace = str_replace("\\", "/", $reflection->getNamespaceName());
227        if (!is_dir("src/$namespace")) {
228            mkdir("src/$namespace", 0777, true);
229        }
230        file_put_contents("src/$namespace/$class.c", $data);
231    }
232}
233
234function get_return_type($docblock) {
235    global $classes, $interfaces;
236
237    $blocks = explode("\n", trim($docblock));
238    foreach($blocks as $tmp) {
239        if(strpos($tmp, "@return") !== false) {
240            list($nada, $param, $type) = explode(" ", trim($tmp));
241            if (isset($classes[$type])) {
242                return $classes[$type];
243            }
244            if (isset($interfaces[$type])) {
245                return $interfaces[$type];
246            }
247            return $type;
248        }
249    }
250    return "void";
251}
252function get_zpp_type($current, $max, $param, $docblock) {
253    if ($param->isArray()) {
254        return array("zval", "a", "array");
255    }
256    $blocks = explode("\n", trim($docblock));
257    $argn = 0;
258    foreach($blocks as $argproto) {
259        if(strpos($argproto, "@param") !== false) {
260            if (++$argn == $current) {
261                break;
262            }
263        }
264    }
265    if (strpos($argproto, $param->getName()) === false) {
266        echo "Error - Not the argument name I expected in docblock\n";
267        var_dump($param->getDeclaringClass()->getName(), $param->getName(), $argproto);
268        exit;
269    }
270    list($nada, $param, $type, $name) = explode(" ", trim($argproto));
271    switch($type) {
272    case "bool":
273    case "boolean":
274        return array("zend_bool", "b", "boolean");
275    case "integer|string":
276    case "string":
277        return array("char", "s", "string");
278    case "integer":
279        return array("long", "l", "integer");
280    case "array|object":
281        return array("zval", "A", "array|object");
282    case "mixed":
283    case "zval":
284        return array("zval", "z", "mixed");
285    default:
286        if ($type == "callable") {
287            return array("zval", "o", "callable");
288        }
289        return array($type, "O", $type);
290    }
291}
292
293function getZPPType($type) {
294    switch($type) {
295    case "zval":
296    case "char":
297    case "long":
298    case "zend_bool":
299        return $type;
300    default:
301        return "zval";
302    }
303}
304function get_code_from($filename, $start, $end)
305{
306    $retval = array();
307    if ($end > $start+1) {
308        $file = file($filename, FILE_IGNORE_NEW_LINES);
309        $content = array_slice($file, $start, $end-$start);
310        $cef = 0;
311        $cimpl = 0;
312        $data = array();
313        foreach($content as $locator) {
314            switch(trim($locator)) {
315            case "/*** CEF ***/":
316                $cef++;
317                break;
318            case "/*** CIMPL ***/":
319                $cimpl++;
320                break;
321            }
322
323            if ($cef == 1) {
324                $cef++;
325            } else if ($cef == 2) {
326                $ws = strspn($locator, " ");
327                $tabs = $ws / 4;
328                $data[] = str_repeat("\t", $tabs) . trim($locator, " ");
329            } else if ($cef == 3) {
330                $retval["cef"] = join("\n", array_slice($data, 1, -1));
331                $data = array();
332                $cef++;
333
334            }
335            if ($cimpl == 1) {
336                $cimpl++;
337            } else if ($cimpl == 2) {
338                $ws = strspn($locator, " ");
339                $tabs = $ws / 4;
340                $data[] = str_repeat("\t", $tabs) . trim($locator, " ");
341            } else if ($cimpl == 3) {
342                $retval["cimpl"] = join("\n", array_slice($data, 1, -1));
343                $cimpl++;
344            }
345        }
346    }
347
348    return $retval;
349}
350
351
352function getDefaultConfig($config) {
353    $def = array(
354        "free" => false,
355        "funcs" => "", 
356        "internwrapper" => "",
357        "ce" => array(),
358        "headers" => array(),
359        "handlers_callback" => "phongo_get_std_object_handlers",
360        "handlers_init"     => "",
361        "forward_declarations" => "",
362        "generate_handlers" => true,
363    );
364
365    $config = array_merge($def, $config);
366
367    return $config;
368}