1 /*
2 +----------------------------------------------------------------------+
3 | Yet Another Framework |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Xinchen Hui <laruence@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "php.h"
22 #include "php_ini.h" /* for zend_alter_ini_entry */
23 #include "Zend/zend_exceptions.h" /* for zend_throw_exception_ex */
24 #include "Zend/zend_interfaces.h" /* for zend_call_method_with_* */
25
26 #include "php_yaf.h"
27 #include "yaf_namespace.h"
28 #include "yaf_application.h"
29 #include "yaf_dispatcher.h"
30 #include "yaf_router.h"
31 #include "yaf_config.h"
32 #include "yaf_loader.h"
33 #include "yaf_request.h"
34 #include "yaf_bootstrap.h"
35 #include "yaf_exception.h"
36
37 zend_class_entry *yaf_application_ce;
38 static zend_object_handlers yaf_application_obj_handlers;
39
40 /** {{{ ARG_INFO
41 * */
42 ZEND_BEGIN_ARG_INFO_EX(yaf_application_void_arginfo, 0, 0, 0)
ZEND_END_ARG_INFO()43 ZEND_END_ARG_INFO()
44
45 ZEND_BEGIN_ARG_INFO_EX(yaf_application_construct_arginfo, 0, 0, 1)
46 ZEND_ARG_INFO(0, config)
47 ZEND_ARG_INFO(0, environ)
48 ZEND_END_ARG_INFO()
49
50 ZEND_BEGIN_ARG_INFO_EX(yaf_application_app_arginfo, 0, 0, 0)
51 ZEND_END_ARG_INFO()
52
53 ZEND_BEGIN_ARG_INFO_EX(yaf_application_execute_arginfo, 0, 0, 2)
54 ZEND_ARG_INFO(0, entry)
55 ZEND_ARG_INFO(0, ...)
56 ZEND_END_ARG_INFO()
57
58 ZEND_BEGIN_ARG_INFO_EX(yaf_application_getconfig_arginfo, 0, 0, 0)
59 ZEND_END_ARG_INFO()
60
61 ZEND_BEGIN_ARG_INFO_EX(yaf_application_getmodule_arginfo, 0, 0, 0)
62 ZEND_END_ARG_INFO()
63
64 ZEND_BEGIN_ARG_INFO_EX(yaf_application_getdispatch_arginfo, 0, 0, 0)
65 ZEND_END_ARG_INFO()
66
67 ZEND_BEGIN_ARG_INFO_EX(yaf_application_bootstrap_arginfo, 0, 0, 0)
68 ZEND_ARG_INFO(0, bootstrap)
69 ZEND_END_ARG_INFO()
70
71 ZEND_BEGIN_ARG_INFO_EX(yaf_application_environ_arginfo, 0, 0, 0)
72 ZEND_END_ARG_INFO()
73
74 ZEND_BEGIN_ARG_INFO_EX(yaf_application_run_arginfo, 0, 0, 0)
75 ZEND_END_ARG_INFO()
76
77 ZEND_BEGIN_ARG_INFO_EX(yaf_application_setappdir_arginfo, 0, 0, 1)
78 ZEND_ARG_INFO(0, directory)
79 ZEND_END_ARG_INFO()
80 /* }}} */
81
82 static void yaf_application_free(zend_object *object) /* {{{ */ {
83 yaf_application_object *app = yaf_application_instance();
84
85 if ((app != php_yaf_application_fetch_object(object)) || !app->env) {
86 zend_object_std_dtor(object);
87 return;
88 }
89
90 zend_string_release(app->env);
91
92 OBJ_RELEASE(Z_OBJ(app->config));
93 OBJ_RELEASE(Z_OBJ(app->dispatcher));
94
95 zend_string_release(app->default_module);
96 zend_string_release(app->default_controller);
97 zend_string_release(app->default_action);
98 if (app->library) {
99 zend_string_release(app->library);
100 }
101 zend_string_release(app->directory);
102
103 if (app->ext) {
104 zend_string_release(app->ext);
105 }
106 if (app->bootstrap) {
107 zend_string_release(app->bootstrap);
108 }
109 if (app->view_ext) {
110 zend_string_release(app->view_ext);
111 }
112 if (app->base_uri) {
113 zend_string_release(app->base_uri);
114 }
115 if (app->err_msg) {
116 zend_string_release(app->err_msg);
117 }
118 if (app->modules) {
119 if (GC_DELREF(app->modules) == 0) {
120 GC_REMOVE_FROM_BUFFER(app->modules);
121 zend_array_destroy(app->modules);
122 }
123 }
124 if (app->properties) {
125 if (GC_DELREF(app->properties) == 0) {
126 GC_REMOVE_FROM_BUFFER(app->properties);
127 zend_array_destroy(app->properties);
128 }
129 }
130
131 zend_object_std_dtor(object);
132 }
133 /* }}} */
134
yaf_application_new(zend_class_entry * ce)135 static zend_object* yaf_application_new(zend_class_entry *ce) /* {{{ */ {
136 yaf_application_object *app = emalloc(sizeof(yaf_application_object) + zend_object_properties_size(ce));
137
138 memset(app, 0, XtOffsetOf(yaf_application_object, std));
139 zend_object_std_init(&app->std, ce);
140 app->std.handlers = &yaf_application_obj_handlers;
141
142 return &app->std;
143 }
144 /* }}} */
145
yaf_application_get_gc(yaf_object * obj,zval ** table,int * n)146 static HashTable *yaf_application_get_gc(yaf_object *obj, zval **table, int *n) /* {{{ */ {
147 yaf_application_object *app = php_yaf_application_fetch_object(yaf_strip_obj(obj));
148
149 *table = &app->dispatcher;
150 *n = 2;
151
152 return NULL;
153 }
154 /* }}} */
155
yaf_application_get_properties(yaf_object * obj)156 static HashTable *yaf_application_get_properties(yaf_object *obj) /* {{{ */ {
157 zval rv;
158 HashTable *ht;
159 yaf_application_object *app = php_yaf_application_fetch_object(yaf_strip_obj(obj));
160
161 if (!app->properties) {
162 ALLOC_HASHTABLE(app->properties);
163 zend_hash_init(app->properties, 16, NULL, ZVAL_PTR_DTOR, 0);
164 YAF_ALLOW_VIOLATION(app->properties);
165 }
166
167 ht = app->properties;
168 ZVAL_STR_COPY(&rv, app->directory);
169 zend_hash_str_update(ht, "directory", sizeof("directory") - 1, &rv);
170
171 if (app->library) {
172 ZVAL_STR_COPY(&rv, app->library);
173 } else {
174 ZVAL_NULL(&rv);
175 }
176 zend_hash_str_update(ht, "library", sizeof("library") - 1, &rv);
177
178 if (app->bootstrap) {
179 ZVAL_STR_COPY(&rv, app->bootstrap);
180 } else {
181 ZVAL_NULL(&rv);
182 }
183 zend_hash_str_update(ht, "bootstrap", sizeof("bootstrap") - 1, &rv);
184
185 if (app->ext) {
186 ZVAL_STR_COPY(&rv, app->ext);
187 } else {
188 ZVAL_STRINGL(&rv, YAF_DEFAULT_EXT, sizeof(YAF_DEFAULT_EXT) - 1);
189 }
190 zend_hash_str_update(ht, "ext", sizeof("ext") - 1, &rv);
191
192 if (app->view_ext) {
193 ZVAL_STR_COPY(&rv, app->view_ext);
194 } else {
195 ZVAL_STRINGL(&rv, YAF_DEFAULT_VIEW_EXT, sizeof(YAF_DEFAULT_VIEW_EXT) - 1);
196 }
197 zend_hash_str_update(ht, "view_ext", sizeof("view_ext") - 1, &rv);
198
199 ZVAL_STR_COPY(&rv, app->env);
200 zend_hash_str_update(ht, "environ:protected", sizeof("environ:protected") - 1, &rv);
201
202 ZVAL_BOOL(&rv, YAF_APP_FLAGS(app) & YAF_APP_RUNNING);
203 zend_hash_str_update(ht, "running:protected", sizeof("running:protected") - 1, &rv);
204
205 if (app->err_msg) {
206 ZVAL_STR_COPY(&rv, app->err_msg);
207 } else {
208 ZVAL_NULL(&rv);
209 }
210 zend_hash_str_update(ht, "err_msg:protected", sizeof("err_msg:protected") - 1, &rv);
211
212 ZVAL_LONG(&rv, app->err_no);
213 zend_hash_str_update(ht, "err_no:protected", sizeof("err_no:protected") - 1, &rv);
214 if (Z_TYPE(app->config) == IS_OBJECT) {
215 ZVAL_OBJ(&rv, Z_OBJ(app->config));
216 Z_ADDREF(rv);
217 } else {
218 ZVAL_NULL(&rv);
219 }
220 zend_hash_str_update(ht, "config:protected", sizeof("config:protected") - 1, &rv);
221
222 if (Z_TYPE(app->dispatcher) == IS_OBJECT) {
223 ZVAL_OBJ(&rv, Z_OBJ(app->dispatcher));
224 GC_ADDREF(Z_OBJ(app->dispatcher));
225 } else {
226 ZVAL_NULL(&rv);
227 }
228 zend_hash_str_update(ht, "dispatcher:protected", sizeof("dispatcher:protected") - 1, &rv);
229
230 if (app->modules) {
231 ZVAL_ARR(&rv, app->modules);
232 GC_ADDREF(app->modules);
233 } else {
234 zval t;
235 array_init(&rv);
236 if (app->default_module) {
237 ZVAL_STR_COPY(&t, app->default_module);
238 } else {
239 ZVAL_STR(&t, YAF_KNOWN_STR(YAF_DEFAULT_MODULE));
240 }
241 zend_hash_index_update(Z_ARRVAL(rv), 0, &t);
242 }
243 zend_hash_str_update(ht, "modules:protected", sizeof("modules:protected") - 1, &rv);
244
245 if (app->default_route) {
246 ZVAL_ARR(&rv, zend_array_dup(app->default_route));
247 } else {
248 ZVAL_NULL(&rv);
249 }
250 zend_hash_str_update(ht, "default_route:protected", sizeof("default_route:protected") - 1, &rv);
251
252 return ht;
253 }
254 /* }}} */
255
yaf_application_errors_hub(int type,...)256 static ZEND_COLD zend_never_inline void yaf_application_errors_hub(int type, ...) /* {{{ */ {
257 va_list args;
258
259 va_start(args, type);
260 if (type == 0) {
261 yaf_application_object *app = va_arg(args, yaf_application_object*);
262 if (Z_TYPE(YAF_G(app)) == IS_OBJECT) {
263 zend_throw_exception_ex(NULL, YAF_ERR_STARTUP_FAILED, "Only one application can be initialized");
264 } else if (Z_TYPE(app->config) != IS_OBJECT) {
265 zend_throw_exception_ex(NULL, YAF_ERR_STARTUP_FAILED, "Initialization of application config failed");
266 } else {
267 zval *pzval;
268 HashTable *conf = Z_YAFCONFIGOBJ(app->config)->config;
269 if ((((pzval = zend_hash_find(conf, YAF_KNOWN_STR(YAF_APPLICATION))) == NULL) || Z_TYPE_P(pzval) != IS_ARRAY) &&
270 (((pzval = zend_hash_find(conf, YAF_KNOWN_STR(YAF))) == NULL) || Z_TYPE_P(pzval) != IS_ARRAY)) {
271 yaf_trigger_error(YAF_ERR_TYPE_ERROR, "%s", "Expected an array of application configuration");
272 } else {
273 yaf_trigger_error(YAF_ERR_STARTUP_FAILED, "%s", "Expected 'directory' entry in application configuration");
274 }
275 zval_ptr_dtor(&app->config);
276 }
277 } else if (type == 1) {
278 zend_class_entry *ce = va_arg(args, zend_class_entry*);
279 char *bootstrap_path = va_arg(args, char*);
280
281 if (ce) {
282 yaf_trigger_error(YAF_ERR_TYPE_ERROR, "'%s' is not a subclass of %s", ZSTR_VAL(ce->name), ZSTR_VAL(yaf_bootstrap_ce->name));
283 } else if (zend_hash_str_exists(&EG(included_files), bootstrap_path, strlen(bootstrap_path))) {
284 php_error_docref(NULL, E_WARNING, "Couldn't find class %s in %s", YAF_DEFAULT_BOOTSTRAP, bootstrap_path);
285 } else {
286 php_error_docref(NULL, E_WARNING, "Couldn't find bootstrap file %s", bootstrap_path);
287 }
288 }
289 va_end(args);
290 }
291 /* }}} */
292
yaf_application_get_dispatcher(yaf_application_object * app)293 static inline zend_object *yaf_application_get_dispatcher(yaf_application_object *app) /* {{{ */ {
294 if (Z_TYPE(app->dispatcher) == IS_OBJECT) {
295 Z_ADDREF(app->dispatcher);
296 return Z_OBJ(app->dispatcher);
297 }
298 return NULL;
299 }
300 /* }}} */
301
yaf_application_get_config(yaf_application_object * app)302 static inline zend_object *yaf_application_get_config(yaf_application_object *app) /* {{{ */ {
303 if (Z_TYPE(app->config) == IS_OBJECT) {
304 GC_ADDREF(Z_OBJ(app->config));
305 return Z_OBJ(app->config);
306 }
307 return NULL;
308 }
309 /* }}} */
310
yaf_application_read_property(yaf_object * obj,void * name,int type,void ** cache_slot,zval * rv)311 static zval *yaf_application_read_property(yaf_object *obj, void *name, int type, void **cache_slot, zval *rv) /* {{{ */ {
312 zend_string *member;
313 yaf_application_object *app = php_yaf_application_fetch_object(yaf_strip_obj(obj));
314
315 #if PHP_VERSION_ID < 80000
316 if (UNEXPECTED(Z_TYPE_P((zval*)name) != IS_STRING)) {
317 return &EG(uninitialized_zval);
318 }
319 member = Z_STR_P((zval*)name);
320 #else
321 member = (zend_string*)name;
322 #endif
323
324 if (UNEXPECTED(type == BP_VAR_W || type == BP_VAR_RW)) {
325 return &EG(error_zval);
326 }
327
328 if (zend_string_equals_literal(member, "directory")) {
329 if (app->directory) {
330 ZVAL_STR_COPY(rv, app->directory);
331 return rv;
332 }
333 return &EG(uninitialized_zval);
334 }
335
336 if (zend_string_equals_literal(member, "library")) {
337 if (app->library) {
338 ZVAL_STR_COPY(rv, app->library);
339 return rv;
340 }
341 return &EG(uninitialized_zval);
342 }
343
344 if (zend_string_equals_literal(member, "bootstrap")) {
345 if (app->bootstrap) {
346 ZVAL_STR_COPY(rv, app->bootstrap);
347 return rv;
348 }
349 return &EG(uninitialized_zval);
350 }
351
352 if (zend_string_equals_literal(member, "ext")) {
353 if (app->ext) {
354 ZVAL_STR_COPY(rv, app->ext);
355 return rv;
356 }
357 return &EG(uninitialized_zval);
358 }
359
360 if (zend_string_equals_literal(member, "view_ext")) {
361 if (app->view_ext) {
362 ZVAL_STR_COPY(rv, app->view_ext);
363 return rv;
364 }
365 return &EG(uninitialized_zval);
366 }
367
368 /*
369 if (zend_string_equals_literal(member, "environ")) {
370 if (app->env) {
371 ZVAL_STR_COPY(rv, app->env);
372 return rv;
373 }
374 return &EG(uninitialized_zval);
375 }
376
377 if (zend_string_equals_literal(member, "running")) {
378 ZVAL_BOOL(rv, app->running);
379 return rv;
380 }
381
382
383 if (zend_string_equals_literal(member, "dispatcher")) {
384 zend_object *dispatcher = yaf_application_get_dispatcher(app);
385 if (dispatcher) {
386 ZVAL_OBJ(rv, dispatcher);
387 return rv;
388 }
389 return &EG(uninitialized_zval);
390 }
391
392 if (zend_string_equals_literal(member, "config")) {
393 zend_object *config = yaf_application_get_config(app);
394 if (config) {
395 ZVAL_OBJ(rv, config);
396 return rv;
397 }
398 return &EG(uninitialized_zval);
399 }
400
401 if (zend_string_equals_literal(member, "err_msg")) {
402 if (app->err_msg) {
403 ZVAL_STR_COPY(rv, app->err_msg);
404 return rv;
405 }
406 return &EG(uninitialized_zval);
407 }
408
409 if (zend_string_equals_literal(member, "err_no")) {
410 ZVAL_BOOL(rv, app->err_no);
411 return rv;
412 }
413 */
414
415 return &EG(uninitialized_zval);
416 }
417 /* }}} */
418
yaf_application_write_property(yaf_object * obj,void * name,zval * value,void ** cache_slot)419 static YAF_WRITE_HANDLER yaf_application_write_property(yaf_object *obj, void *name, zval *value, void **cache_slot) /* {{{ */ {
420 zend_string *member;
421 yaf_application_object *app = php_yaf_application_fetch_object(yaf_strip_obj(obj));
422
423 #if PHP_VERSION_ID < 80000
424 if (UNEXPECTED(Z_TYPE_P((zval*)name) != IS_STRING)) {
425 YAF_WHANDLER_RET(value);
426 }
427 member = Z_STR_P((zval*)name);
428 #else
429 member = (zend_string*)name;
430 #endif
431
432 if (zend_string_equals_literal(member, "directory")) {
433 if (Z_TYPE_P(value) != IS_STRING) {
434 YAF_WHANDLER_RET(value);
435 }
436 if (app->directory) {
437 zend_string_release(app->directory);
438 }
439 app->directory = zend_string_copy(Z_STR_P(value));
440 YAF_WHANDLER_RET(value);
441 }
442
443 if (zend_string_equals_literal(member, "bootstrap")) {
444 if (Z_TYPE_P(value) != IS_STRING) {
445 YAF_WHANDLER_RET(value);
446 }
447 if (app->bootstrap) {
448 zend_string_release(app->bootstrap);
449 }
450 app->bootstrap = zend_string_copy(Z_STR_P(value));
451 YAF_WHANDLER_RET(value);
452 }
453
454 if (zend_string_equals_literal(member, "library")) {
455 if (Z_TYPE_P(value) != IS_STRING) {
456 YAF_WHANDLER_RET(value);
457 }
458 if (app->library) {
459 zend_string_release(app->library);
460 }
461 app->library = zend_string_copy(Z_STR_P(value));
462 YAF_WHANDLER_RET(value);
463 }
464
465 if (zend_string_equals_literal(member, "view_ext")) {
466 if (Z_TYPE_P(value) != IS_STRING) {
467 YAF_WHANDLER_RET(value);
468 }
469 if (app->view_ext) {
470 zend_string_release(app->view_ext);
471 }
472 app->view_ext = zend_string_copy(Z_STR_P(value));
473 YAF_WHANDLER_RET(value);
474 }
475
476 if (zend_string_equals_literal(member, "ext")) {
477 if (Z_TYPE_P(value) != IS_STRING) {
478 YAF_WHANDLER_RET(value);
479 }
480 if (app->ext) {
481 zend_string_release(app->ext);
482 }
483 app->ext = zend_string_copy(Z_STR_P(value));
484 YAF_WHANDLER_RET(value);
485 }
486
487 YAF_WHANDLER_RET(value);
488 }
489 /* }}} */
490
yaf_application_is_module_name(zend_string * name)491 ZEND_HOT int yaf_application_is_module_name(zend_string *name) /* {{{ */ {
492 zval *pzval;
493 yaf_application_object *app = yaf_application_instance();
494
495 if (UNEXPECTED(app == NULL)) {
496 return 0;
497 }
498 if (app->modules == NULL) {
499 if (UNEXPECTED(app->default_module)) {
500 return zend_string_equals_ci(app->default_module, name);
501 }
502 return zend_string_equals_ci(name, YAF_KNOWN_STR(YAF_DEFAULT_MODULE));
503 }
504
505 ZEND_HASH_FOREACH_VAL(app->modules, pzval) {
506 if (UNEXPECTED(Z_TYPE_P(pzval) != IS_STRING)) {
507 continue;
508 }
509 if (zend_string_equals_ci(Z_STR_P(pzval), name)) {
510 return 1;
511 }
512 } ZEND_HASH_FOREACH_END();
513 return 0;
514 }
515 /* }}} */
516
yaf_application_is_module_name_str(const char * name,size_t len)517 ZEND_HOT int yaf_application_is_module_name_str(const char *name, size_t len) /* {{{ */ {
518 zval *pzval;
519 yaf_application_object *app = yaf_application_instance();
520
521 if (UNEXPECTED(app == NULL)) {
522 return 0;
523 }
524 if (app->modules == NULL) {
525 if (UNEXPECTED(app->default_module)) {
526 return len == ZSTR_LEN(app->default_module) && !strncasecmp(name, ZSTR_VAL(app->default_module), len);
527 }
528 return len == strlen(YAF_KNOWN_CHARS(YAF_DEFAULT_MODULE)) &&
529 !strncasecmp(name, YAF_KNOWN_CHARS(YAF_DEFAULT_MODULE), len);
530 }
531
532 ZEND_HASH_FOREACH_VAL(app->modules, pzval) {
533 if (UNEXPECTED(Z_TYPE_P(pzval) != IS_STRING)) {
534 continue;
535 }
536 if (Z_STRLEN_P(pzval) == len && strncasecmp(Z_STRVAL_P(pzval), name, len) == 0) {
537 return 1;
538 }
539 } ZEND_HASH_FOREACH_END();
540
541 return 0;
542 }
543 /* }}} */
544
yaf_application_parse_optional(yaf_application_object * app,zend_array * conf)545 static zend_never_inline void yaf_application_parse_optional(yaf_application_object *app, zend_array *conf) /* {{{ */ {
546 zval *pzval, *psval;
547
548 /* following options are optional */
549 if (UNEXPECTED((pzval = zend_hash_find(conf, YAF_KNOWN_STR(YAF_BOOTSTRAP))) != NULL &&
550 Z_TYPE_P(pzval) == IS_STRING)) {
551 app->bootstrap = zend_string_copy(Z_STR_P(pzval));
552 }
553
554 if (UNEXPECTED((pzval = zend_hash_str_find(conf, ZEND_STRL("ext"))) != NULL &&
555 Z_TYPE_P(pzval) == IS_STRING)) {
556 app->ext = zend_string_copy(Z_STR_P(pzval));
557 }
558
559 if (UNEXPECTED((pzval = zend_hash_str_find(conf, ZEND_STRL("library"))) != NULL)) {
560 if (EXPECTED(IS_STRING == Z_TYPE_P(pzval))) {
561 app->library = zend_string_copy(Z_STR_P(pzval));
562 } else if (IS_ARRAY == Z_TYPE_P(pzval)) {
563 if ((psval = zend_hash_find(Z_ARRVAL_P(pzval), YAF_KNOWN_STR(YAF_DIRECTORY))) != NULL &&
564 Z_TYPE_P(psval) == IS_STRING) {
565 app->library = zend_string_copy(Z_STR_P(psval));
566 }
567 if ((psval = zend_hash_str_find(Z_ARRVAL_P(pzval), ZEND_STRL("namespace"))) != NULL) {
568 yaf_loader_object *loader = Z_YAFLOADEROBJ(YAF_G(loader));
569 if (Z_TYPE_P(psval) == IS_STRING) {
570 if (Z_STRLEN_P(psval)) {
571 zend_string *prefix;
572 char *src = Z_STRVAL_P(psval), *pos;
573 size_t len = Z_STRLEN_P(psval);
574 while ((pos = memchr(src, ',', len))) {
575 len -= (pos - src) + 1;
576 while (*src == ' ') src++;
577 prefix = zend_string_init(src, pos - src, 0);
578 yaf_loader_register_namespace(loader, prefix, NULL);
579 zend_string_release(prefix);
580 src = pos + 1;
581 }
582
583 if (len) {
584 while (*src == ' ') src++, len--;
585 prefix = zend_string_init(src, len, 0);
586 yaf_loader_register_namespace(loader, prefix, NULL);
587 zend_string_release(prefix);
588 }
589 }
590 } else if (Z_TYPE_P(psval) == IS_ARRAY) {
591 zend_string *name;
592 zval *path;
593 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(psval), name, path) {
594 if (name == NULL) {
595 continue;
596 }
597 if (Z_TYPE_P(path) == IS_STRING) {
598 yaf_loader_register_namespace(Z_YAFLOADEROBJ(YAF_G(loader)), name, Z_STR_P(path));
599 } else {
600 yaf_loader_register_namespace(Z_YAFLOADEROBJ(YAF_G(loader)), name, NULL);
601 }
602 } ZEND_HASH_FOREACH_END();
603 }
604 }
605 }
606 }
607
608 if (UNEXPECTED((pzval = zend_hash_str_find(conf, ZEND_STRL("view"))) != NULL &&
609 Z_TYPE_P(pzval) == IS_ARRAY &&
610 ((psval = zend_hash_str_find(Z_ARRVAL_P(pzval), ZEND_STRL("ext"))) != NULL &&
611 Z_TYPE_P(psval) == IS_STRING))) {
612 app->view_ext = zend_string_copy(Z_STR_P(psval));
613 }
614
615 if (UNEXPECTED((pzval = zend_hash_str_find(conf, ZEND_STRL("baseUri"))) != NULL &&
616 Z_TYPE_P(pzval) == IS_STRING)) {
617 app->base_uri = zend_string_copy(Z_STR_P(pzval));
618 }
619
620 do {
621 zval rv;
622 if (UNEXPECTED((pzval = zend_hash_str_find(conf, ZEND_STRL("modules"))) != NULL &&
623 Z_TYPE_P(pzval) == IS_STRING && Z_STRLEN_P(pzval))) {
624 char *ptrptr = NULL;
625 char *seg, *modules;
626
627 ALLOC_HASHTABLE(app->modules);
628 zend_hash_init(app->modules, 8, NULL, ZVAL_PTR_DTOR, 0);
629 YAF_ALLOW_VIOLATION(app->modules);
630
631 modules = estrndup(Z_STRVAL_P(pzval), Z_STRLEN_P(pzval));
632 seg = php_strtok_r(modules, ",", &ptrptr);
633 while (seg) {
634 if (seg && strlen(seg)) {
635 ZVAL_STR(&rv, yaf_build_camel_name(seg, strlen(seg)));
636 zend_hash_next_index_insert(app->modules, &rv);
637 }
638 seg = php_strtok_r(NULL, ",", &ptrptr);
639 }
640 efree(modules);
641 }
642 } while (0);
643
644 if (UNEXPECTED((pzval = zend_hash_str_find(conf, ZEND_STRL("system"))) != NULL &&
645 Z_TYPE_P(pzval) == IS_ARRAY)) {
646 zval *value;
647 char name[128];
648 zend_string *key;
649 size_t len;
650
651 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pzval), key, value) {
652 zend_string *str, *val;
653 len = snprintf(name, sizeof(name), "%s.%s", "yaf", ZSTR_VAL(key));
654 if (len > sizeof(name) -1) {
655 continue;
656 }
657 str = zend_string_init(name, len, 0);
658 val = zval_get_string(value);
659 zend_alter_ini_entry(str, val, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
660 zend_string_release(str);
661 zend_string_release(val);
662 } ZEND_HASH_FOREACH_END();
663
664 /* We have to reset the loader as the yaf.* inis has beend changed */
665 yaf_loader_reset(Z_YAFLOADEROBJ(YAF_G(loader)));
666 if (*YAF_G(global_library)) {
667 zend_string *library = zend_string_init(YAF_G(global_library), strlen(YAF_G(global_library)), 0);
668 yaf_loader_set_global_library_path(Z_YAFLOADEROBJ(YAF_G(loader)), library);
669 zend_string_release(library);
670 }
671 }
672 }
673 /* }}} */
674
yaf_application_parse_option(yaf_application_object * app)675 int yaf_application_parse_option(yaf_application_object *app) /* {{{ */ {
676 zval *pzval;
677 HashTable *conf;
678 uint32_t items;
679
680 conf = Z_YAFCONFIGOBJ(app->config)->config;
681 if (UNEXPECTED((pzval = zend_hash_find(conf, YAF_KNOWN_STR(YAF_APPLICATION))) == NULL) || Z_TYPE_P(pzval) != IS_ARRAY) {
682 /* For back compatibilty */
683 if (((pzval = zend_hash_find(conf, YAF_KNOWN_STR(YAF))) == NULL) || Z_TYPE_P(pzval) != IS_ARRAY) {
684 return 0;
685 }
686 }
687
688 conf = Z_ARRVAL_P(pzval);
689 if (UNEXPECTED((pzval = zend_hash_find(conf, YAF_KNOWN_STR(YAF_DIRECTORY))) == NULL ||
690 Z_TYPE_P(pzval) != IS_STRING || Z_STRLEN_P(pzval) == 0)) {
691 return 0;
692 }
693
694 if (UNEXPECTED(*(Z_STRVAL_P(pzval) + Z_STRLEN_P(pzval) - 1) == DEFAULT_SLASH)) {
695 app->directory = zend_string_init(Z_STRVAL_P(pzval), Z_STRLEN_P(pzval) - 1, 0);
696 } else {
697 app->directory = zend_string_copy(Z_STR_P(pzval));
698 }
699
700 items = zend_hash_num_elements(conf) - 1;
701 if (UNEXPECTED((pzval = zend_hash_find(conf, YAF_KNOWN_STR(YAF_DISPATCHER))) != NULL &&
702 Z_TYPE_P(pzval) == IS_ARRAY)) {
703 zval *psval;
704
705 items--;
706 if ((psval = zend_hash_str_find(Z_ARRVAL_P(pzval), ZEND_STRL("defaultModule"))) != NULL &&
707 Z_TYPE_P(psval) == IS_STRING) {
708 app->default_module = yaf_canonical_name(1, Z_STR_P(psval));
709 } else {
710 app->default_module = YAF_KNOWN_STR(YAF_DEFAULT_MODULE);
711 }
712
713 if ((psval = zend_hash_str_find(Z_ARRVAL_P(pzval), ZEND_STRL("defaultController"))) != NULL &&
714 Z_TYPE_P(psval) == IS_STRING) {
715 app->default_controller = yaf_canonical_name(1, Z_STR_P(psval));
716 } else {
717 app->default_controller = YAF_KNOWN_STR(YAF_DEFAULT_CONTROLLER);
718 }
719
720 if ((psval = zend_hash_str_find(Z_ARRVAL_P(pzval), ZEND_STRL("defaultAction"))) != NULL &&
721 Z_TYPE_P(psval) == IS_STRING) {
722 app->default_action = yaf_canonical_name(0, Z_STR_P(psval));
723 } else {
724 app->default_action = YAF_KNOWN_STR(YAF_DEFAULT_ACTION);
725 }
726
727 if ((psval = zend_hash_str_find(Z_ARRVAL_P(pzval), ZEND_STRL("throwException"))) != NULL) {
728 yaf_set_throw_exception(zend_is_true(psval));
729 }
730
731 if ((psval = zend_hash_str_find(Z_ARRVAL_P(pzval), ZEND_STRL("catchException"))) != NULL) {
732 yaf_set_catch_exception(zend_is_true(psval));
733 }
734
735 if ((psval = zend_hash_str_find(Z_ARRVAL_P(pzval), ZEND_STRL("defaultRoute"))) != NULL &&
736 Z_TYPE_P(psval) == IS_ARRAY) {
737 /* leave it to configs destructor */
738 app->default_route = Z_ARRVAL_P(psval);
739 }
740 } else {
741 app->default_module = YAF_KNOWN_STR(YAF_DEFAULT_MODULE);
742 app->default_controller = YAF_KNOWN_STR(YAF_DEFAULT_CONTROLLER);
743 app->default_action = YAF_KNOWN_STR(YAF_DEFAULT_ACTION);
744 }
745
746 /* prasing optional configs */
747 if (items) {
748 yaf_application_parse_optional(app, conf);
749 }
750
751 return 1;
752 }
753 /* }}} */
754
755 /** {{{ proto Yaf_Application::__construct(mixed $config, string $environ = YAF_G(environ_name))
756 */
PHP_METHOD(yaf_application,__construct)757 PHP_METHOD(yaf_application, __construct) {
758 zval *config;
759 zend_string *section = NULL;
760 yaf_loader_t *loader;
761 yaf_application_object *app = Z_YAFAPPOBJ_P(getThis());
762
763 if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "z|S", &config, §ion) == FAILURE) {
764 return;
765 }
766
767 if (EXPECTED(Z_TYPE(YAF_G(app)) != IS_OBJECT)) {
768 if (!section || !ZSTR_LEN(section)) {
769 section = zend_string_init(YAF_G(environ_name), strlen(YAF_G(environ_name)), 0);
770 } else {
771 zend_string_copy(section);
772 }
773 yaf_config_instance(&app->config, config, section);
774 if (EXPECTED(Z_TYPE(app->config) == IS_OBJECT)) {
775 loader = yaf_loader_instance(NULL);
776 if (EXPECTED(yaf_application_parse_option(app))) {
777 app->env = section /* initialized flag */;
778 if (app->library == NULL) {
779 zend_string *local_library = zend_string_alloc(ZSTR_LEN(app->directory) + sizeof(YAF_LIBRARY_DIRECTORY_NAME), 0);
780 yaf_compose_2_pathes(ZSTR_VAL(local_library), app->directory, ZEND_STRS(YAF_LIBRARY_DIRECTORY_NAME));
781 yaf_loader_set_library_path_ex(Z_YAFLOADEROBJ_P(loader), local_library);
782 } else {
783 yaf_loader_set_library_path(Z_YAFLOADEROBJ_P(loader), app->library);
784 }
785
786 GC_ADDREF(&app->std);
787 ZVAL_OBJ(&YAF_G(app), &app->std);
788 yaf_dispatcher_instance(&app->dispatcher);
789 yaf_request_instance(&Z_YAFDISPATCHEROBJ(app->dispatcher)->request, app->base_uri);
790 return;
791 }
792 }
793 zend_string_release(section);
794 }
795
796 yaf_application_errors_hub(0, app);
797 return;
798 }
799 /* }}} */
800
801 /** {{{ proto public Yaf_Application::run(void)
802 */
PHP_METHOD(yaf_application,run)803 PHP_METHOD(yaf_application, run) {
804 yaf_application_object *app = Z_YAFAPPOBJ_P(getThis());
805 yaf_response_t *response;
806
807 if (UNEXPECTED(YAF_APP_FLAGS(app) & YAF_APP_RUNNING)) {
808 yaf_trigger_error(YAF_ERR_STARTUP_FAILED, "Application is already started");
809 RETURN_FALSE;
810 }
811
812 YAF_APP_FLAGS(app) |= YAF_APP_RUNNING;
813 if ((response = yaf_dispatcher_dispatch(Z_YAFDISPATCHEROBJ(app->dispatcher)))) {
814 YAF_APP_FLAGS(app) &= ~YAF_APP_RUNNING;
815 RETURN_ZVAL(response, 1, 0);
816 }
817
818 YAF_APP_FLAGS(app) &= ~YAF_APP_RUNNING;
819 RETURN_FALSE;
820 }
821 /* }}} */
822
823 /** {{{ proto public Yaf_Application::bootstrap(void)
824 */
PHP_METHOD(yaf_application,bootstrap)825 PHP_METHOD(yaf_application, bootstrap) {
826 zval bootstrap;
827 zend_string *func;
828 char buf[MAXPATHLEN];
829 zend_function *fptr;
830 zend_class_entry *ce;
831 const char *bootstrap_path;
832 uint32_t bootstrap_path_len;
833 yaf_application_object *app = Z_YAFAPPOBJ_P(getThis());
834 yaf_dispatcher_t *dispatcher = &app->dispatcher;
835
836 if (!(ce = zend_hash_find_ptr(EG(class_table), YAF_KNOWN_STR(YAF_BOOTSTRAP)))) {
837 if (UNEXPECTED(app->bootstrap)) {
838 bootstrap_path = ZSTR_VAL(app->bootstrap);
839 bootstrap_path_len = ZSTR_LEN(app->bootstrap);
840 } else {
841 bootstrap_path_len = yaf_compose_2_pathes(buf, app->directory, ZEND_STRL(YAF_DEFAULT_BOOTSTRAP));
842 buf[bootstrap_path_len++] = '.';
843 if (UNEXPECTED(app->ext)) {
844 memcpy(buf + bootstrap_path_len, ZSTR_VAL(app->ext), ZSTR_LEN(app->ext));
845 bootstrap_path_len += ZSTR_LEN(app->ext);
846 } else {
847 memcpy(buf + bootstrap_path_len, YAF_DEFAULT_EXT, sizeof(YAF_DEFAULT_EXT) - 1);
848 bootstrap_path_len += sizeof(YAF_DEFAULT_EXT) - 1;
849 }
850 buf[bootstrap_path_len] = '\0';
851 bootstrap_path = buf;
852 }
853 if (UNEXPECTED((!yaf_loader_import(bootstrap_path, bootstrap_path_len)) ||
854 (!(ce = zend_hash_find_ptr(EG(class_table), YAF_KNOWN_STR(YAF_BOOTSTRAP)))))) {
855 goto error;
856 }
857 }
858
859 if (EXPECTED(instanceof_function(ce, yaf_bootstrap_ce))) {
860 zend_object *obj;
861
862 object_init_ex(&bootstrap, ce);
863 obj = Z_OBJ(bootstrap);
864 ZEND_HASH_FOREACH_STR_KEY_PTR(&(ce->function_table), func, fptr) {
865 zval ret;
866 if (UNEXPECTED(ZSTR_LEN(func) < sizeof("_init")) ||
867 !yaf_slip_equal(ZSTR_VAL(func), ZEND_STRL(YAF_BOOTSTRAP_INITFUNC_PREFIX))) {
868 continue;
869 }
870 if (UNEXPECTED(!yaf_call_user_method_with_1_arguments(obj, fptr, dispatcher, &ret))) {
871 /** an uncaught exception threw in function call */
872 if (UNEXPECTED(EG(exception))) {
873 OBJ_RELEASE(Z_OBJ(bootstrap));
874 RETURN_FALSE;
875 }
876 }
877 /* Must always return bool? */
878 /* zval_ptr_dtor(&ret); */
879 } ZEND_HASH_FOREACH_END();
880 OBJ_RELEASE(Z_OBJ(bootstrap));
881
882 RETURN_ZVAL(getThis(), 1, 0);
883 }
884
885 error:
886 yaf_application_errors_hub(1, ce, bootstrap_path);
887 RETURN_FALSE;
888 }
889 /* }}} */
890
891 /** {{{ proto public Yaf_Application::getDispatcher(void)
892 */
PHP_METHOD(yaf_application,getDispatcher)893 PHP_METHOD(yaf_application, getDispatcher) {
894 yaf_application_object *app = Z_YAFAPPOBJ_P(getThis());
895 zend_object *dispatcher = yaf_application_get_dispatcher(app);
896
897 if (dispatcher) {
898 RETURN_OBJ(dispatcher);
899 }
900
901 RETURN_NULL();
902 }
903 /* }}} */
904
905 /** {{{ proto public Yaf_Application::getConfig(void)
906 */
PHP_METHOD(yaf_application,getConfig)907 PHP_METHOD(yaf_application, getConfig) {
908 yaf_application_object *app = Z_YAFAPPOBJ_P(getThis());
909 zend_object *config = yaf_application_get_config(app);
910
911 if (config) {
912 RETURN_OBJ(config);
913 }
914
915 RETURN_NULL();
916 }
917 /* }}} */
918
919 /** {{{ proto public Yaf_Application::app(void)
920 */
PHP_METHOD(yaf_application,app)921 PHP_METHOD(yaf_application, app) {
922 RETURN_ZVAL(&YAF_G(app), 1, 0);
923 }
924 /* }}} */
925
926 /** {{{ proto public Yaf_Application::getModules(void)
927 */
PHP_METHOD(yaf_application,getModules)928 PHP_METHOD(yaf_application, getModules) {
929 yaf_application_object *app = Z_YAFAPPOBJ_P(getThis());
930
931 if (app->modules) {
932 GC_ADDREF(app->modules);
933 RETURN_ARR(app->modules);
934 } else {
935 RETURN_NULL();
936 }
937 }
938 /* }}} */
939
940 /** {{{ proto public Yaf_Application::environ(void)
941 */
PHP_METHOD(yaf_application,environ)942 PHP_METHOD(yaf_application, environ) {
943 yaf_application_object *app = Z_YAFAPPOBJ_P(getThis());
944
945 if (zend_parse_parameters_none() == FAILURE) {
946 return;
947 }
948 if (app->env) {
949 RETURN_STR(zend_string_copy(app->env));
950 }
951 RETURN_EMPTY_STRING();
952 }
953 /* }}} */
954
955 /** {{{ proto public Yaf_Application::execute(callback $func)
956 * We can not call to zif_call_user_func on windows, since it was not declared with dllexport
957 */
PHP_METHOD(yaf_application,execute)958 PHP_METHOD(yaf_application, execute) {
959 zval retval;
960 zend_fcall_info fci;
961 zend_fcall_info_cache fci_cache;
962
963 if (zend_parse_parameters(ZEND_NUM_ARGS(), "f*", &fci, &fci_cache, &fci.params, &fci.param_count) == FAILURE) {
964 return;
965 }
966
967 fci.retval = &retval;
968
969 if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
970 ZVAL_COPY_VALUE(return_value, &retval);
971 } else {
972 RETURN_FALSE;
973 }
974 }
975 /* }}} */
976
977 /** {{{ proto public Yaf_Application::getLastErrorNo(void)
978 */
PHP_METHOD(yaf_application,getLastErrorNo)979 PHP_METHOD(yaf_application, getLastErrorNo) {
980 if (zend_parse_parameters_none() == FAILURE) {
981 return;
982 }
983 RETURN_LONG(Z_YAFAPPOBJ_P(getThis())->err_no);
984 }
985 /* }}} */
986
987 /** {{{ proto public Yaf_Application::getLastErrorMsg(void)
988 */
PHP_METHOD(yaf_application,getLastErrorMsg)989 PHP_METHOD(yaf_application, getLastErrorMsg) {
990 yaf_application_object *app = Z_YAFAPPOBJ_P(getThis());
991
992 if (zend_parse_parameters_none() == FAILURE) {
993 return;
994 }
995 if (app->err_msg) {
996 RETURN_STR_COPY(app->err_msg);
997 } else {
998 RETURN_EMPTY_STRING();
999 }
1000 }
1001 /* }}} */
1002
1003 /** {{{ proto public Yaf_Application::clearLastError(void)
1004 */
PHP_METHOD(yaf_application,clearLastError)1005 PHP_METHOD(yaf_application, clearLastError) {
1006 yaf_application_object *app = Z_YAFAPPOBJ_P(getThis());
1007
1008 if (zend_parse_parameters_none() == FAILURE) {
1009 return;
1010 }
1011 if (app->err_msg) {
1012 zend_string_release(app->err_msg);
1013 app->err_msg = NULL;
1014 }
1015 app->err_no = 0;
1016 RETURN_ZVAL(getThis(), 1, 0);
1017 }
1018 /* }}} */
1019
1020 /** {{{ proto public Yaf_Application::setAppDirectory(string $directory)
1021 */
PHP_METHOD(yaf_application,setAppDirectory)1022 PHP_METHOD(yaf_application, setAppDirectory) {
1023 zend_string *directory;
1024 yaf_application_object *app = Z_YAFAPPOBJ_P(getThis());
1025
1026 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &directory) == FAILURE) {
1027 return;
1028 }
1029
1030 if (ZSTR_LEN(directory) == 0 || !IS_ABSOLUTE_PATH(ZSTR_VAL(directory), ZSTR_LEN(directory))) {
1031 RETURN_FALSE;
1032 }
1033
1034 zend_string_release(app->directory);
1035 app->directory = zend_string_copy(directory);
1036
1037 RETURN_ZVAL(getThis(), 1, 0);
1038 }
1039 /* }}} */
1040
1041 /** {{{ proto public Yaf_Application::getAppDirectory(void)
1042 */
PHP_METHOD(yaf_application,getAppDirectory)1043 PHP_METHOD(yaf_application, getAppDirectory) {
1044 yaf_application_object *app = Z_YAFAPPOBJ_P(getThis());
1045
1046 if (zend_parse_parameters_none() == FAILURE) {
1047 return;
1048 }
1049
1050 if (app->directory) {
1051 RETURN_STR(zend_string_copy(app->directory));
1052 } else {
1053 RETURN_NULL();
1054 }
1055 }
1056 /* }}} */
1057
1058 /** {{{ yaf_application_methods
1059 */
1060 zend_function_entry yaf_application_methods[] = {
1061 PHP_ME(yaf_application, __construct, yaf_application_construct_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
1062 PHP_ME(yaf_application, run, yaf_application_run_arginfo, ZEND_ACC_PUBLIC)
1063 PHP_ME(yaf_application, execute, yaf_application_execute_arginfo, ZEND_ACC_PUBLIC)
1064 PHP_ME(yaf_application, app, yaf_application_app_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
1065 YAF_ME(yaf_application_environ, "environ", yaf_application_environ_arginfo, ZEND_ACC_PUBLIC)
1066 PHP_ME(yaf_application, bootstrap, yaf_application_bootstrap_arginfo, ZEND_ACC_PUBLIC)
1067 PHP_ME(yaf_application, getConfig, yaf_application_getconfig_arginfo, ZEND_ACC_PUBLIC)
1068 PHP_ME(yaf_application, getModules, yaf_application_getmodule_arginfo, ZEND_ACC_PUBLIC)
1069 PHP_ME(yaf_application, getDispatcher, yaf_application_getdispatch_arginfo, ZEND_ACC_PUBLIC)
1070 PHP_ME(yaf_application, setAppDirectory, yaf_application_setappdir_arginfo, ZEND_ACC_PUBLIC)
1071 PHP_ME(yaf_application, getAppDirectory, yaf_application_void_arginfo, ZEND_ACC_PUBLIC)
1072 PHP_ME(yaf_application, getLastErrorNo, yaf_application_void_arginfo, ZEND_ACC_PUBLIC)
1073 PHP_ME(yaf_application, getLastErrorMsg, yaf_application_void_arginfo, ZEND_ACC_PUBLIC)
1074 PHP_ME(yaf_application, clearLastError, yaf_application_void_arginfo, ZEND_ACC_PUBLIC)
1075 PHP_MALIAS(yaf_application, getInstance, app, yaf_application_app_arginfo, ZEND_ACC_PUBLIC)
1076 {NULL, NULL, NULL}
1077 };
1078 /* }}} */
1079
1080 /** {{{ YAF_STARTUP_FUNCTION
1081 */
YAF_STARTUP_FUNCTION(application)1082 YAF_STARTUP_FUNCTION(application) {
1083 zend_class_entry ce;
1084 YAF_INIT_CLASS_ENTRY(ce, "Yaf_Application", "Yaf\\Application", yaf_application_methods);
1085
1086 yaf_application_ce = zend_register_internal_class_ex(&ce, NULL);
1087 yaf_application_ce->ce_flags |= ZEND_ACC_FINAL;
1088 yaf_application_ce->create_object = yaf_application_new;
1089 yaf_application_ce->serialize = zend_class_serialize_deny;
1090 yaf_application_ce->unserialize = zend_class_unserialize_deny;
1091
1092 memcpy(&yaf_application_obj_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1093 yaf_application_obj_handlers.offset = XtOffsetOf(yaf_application_object, std);
1094 yaf_application_obj_handlers.clone_obj = NULL;
1095 yaf_application_obj_handlers.get_gc = yaf_application_get_gc;
1096 yaf_application_obj_handlers.free_obj = yaf_application_free;
1097 yaf_application_obj_handlers.get_properties = yaf_application_get_properties;
1098 yaf_application_obj_handlers.read_property = (zend_object_read_property_t)yaf_application_read_property;
1099 yaf_application_obj_handlers.write_property = (zend_object_write_property_t)yaf_application_write_property;
1100
1101 return SUCCESS;
1102 }
1103 /* }}} */
1104
1105 /*
1106 * Local variables:
1107 * tab-width: 4
1108 * c-basic-offset: 4
1109 * End:
1110 * vim600: noet sw=4 ts=4 fdm=marker
1111 * vim<600: noet sw=4 ts=4
1112 */
1113