[Fix] is_callable should respect __call and __callStatic
[hiphop-php.git] / src / runtime / base / builtin_functions.cpp
blobb26997a46f5e7c46e50d7ca21e5ab559259585ba
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include <runtime/base/type_conversions.h>
18 #include <runtime/base/builtin_functions.h>
19 #include <runtime/base/externals.h>
20 #include <runtime/base/variable_serializer.h>
21 #include <runtime/base/variable_unserializer.h>
22 #include <runtime/base/runtime_option.h>
23 #include <runtime/base/execution_context.h>
24 #include <runtime/base/array/arg_array.h>
25 #include <runtime/eval/debugger/debugger.h>
26 #include <runtime/eval/runtime/code_coverage.h>
27 #include <runtime/ext/ext_process.h>
28 #include <runtime/ext/ext_class.h>
29 #include <runtime/ext/ext_function.h>
30 #include <runtime/ext/ext_file.h>
31 #include <util/logger.h>
32 #include <util/util.h>
33 #include <util/process.h>
35 #include <limits>
37 using namespace std;
39 namespace HPHP {
40 ///////////////////////////////////////////////////////////////////////////////
41 // static strings
43 static StaticString s_offsetExists("offsetExists");
44 static StaticString s___autoload("__autoload");
45 static StaticString s_self("self");
46 static StaticString s_parent("parent");
47 static StaticString s_static("static");
48 static StaticString s_exception("exception");
49 static StaticString s_previous("previous");
51 ///////////////////////////////////////////////////////////////////////////////
53 bool class_exists(CStrRef class_name, bool autoload /* = true */) {
54 return f_class_exists(class_name, autoload);
57 String get_static_class_name(CVarRef objOrClassName) {
58 if (objOrClassName.isString()) {
59 return objOrClassName.toString();
61 if (objOrClassName.isObject()) {
62 return objOrClassName.toObject()->o_getClassName();
64 raise_error("Class name must be a valid object or a string");
65 return "";
68 Variant getDynamicConstant(CVarRef v, CStrRef name) {
69 if (isInitialized(v)) return v;
70 raise_notice("Use of undefined constant %s - assumed '%s'",
71 name.c_str(), name.c_str());
72 return name;
75 String getUndefinedConstant(CStrRef name) {
76 raise_notice("Use of undefined constant %s - assumed '%s'",
77 name.c_str(), name.c_str());
78 return name;
81 /* get_user_func_handler takes a Variant 'function', and sets
82 * up MethodCallPackage 'mcp' appropriately for the given function.
83 * It also sets 'doBind' to indicate whether the caller needs to
84 * set the late bound class.
85 * Note that classname and methodname are needed even though they
86 * dont really pass any information back; the mcp can end up with
87 * references to them, which need to survive until the mcp is used
88 * (after this function returns).
90 bool get_user_func_handler(CVarRef function, bool skip,
91 MethodCallPackage &mcp,
92 String &classname, String &methodname,
93 bool &doBind, bool warn /* = true */) {
94 doBind = false;
95 mcp.noFatal();
97 Variant::TypedValueAccessor tv_func = function.getTypedAccessor();
98 if (Variant::GetAccessorType(tv_func) == KindOfObject) {
99 mcp.functionNamedCall(Variant::GetObjectData(tv_func));
100 if (LIKELY(mcp.ci != 0)) return true;
101 if (warn) raise_warning("call_user_func to non-callback object");
102 return false;
105 if (Variant::IsString(tv_func)) {
106 StringData *sfunc = Variant::GetStringData(tv_func);
107 const char *base = sfunc->data();
108 const char *cc = strstr(base, "::");
109 if (cc && cc != base && cc[2]) {
110 methodname = String(cc + 2, sfunc->size() - (cc - base) - 2, CopyString);
111 if (cc - base == 4 && !strncasecmp(base, "self", 4)) {
112 if (LIKELY(mcp.dynamicNamedCall(FrameInjection::GetClassName(skip),
113 methodname))) {
114 return true;
116 } else if (cc - base == 6 &&
117 !strncasecmp(base, "parent", 6)) {
118 CStrRef cls = FrameInjection::GetParentClassName(skip);
119 if (cls.empty()) {
120 raise_warning("cannot access parent:: when current "
121 "class scope has no parent");
122 return false;
124 if (LIKELY(mcp.dynamicNamedCall(cls, methodname))) return true;
125 } else if (cc - base == 6 &&
126 !strncasecmp(base, "static", 6)) {
127 ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
128 if (LIKELY(mcp.dynamicNamedCall(FrameInjection::GetStaticClassName(ti),
129 methodname))) {
130 return true;
132 } else {
133 classname = String(base, cc - base, CopyString);
134 if (LIKELY(mcp.dynamicNamedCall(classname, methodname))) {
135 doBind = !mcp.isObj || (mcp.ci->m_flags & CallInfo::StaticMethod);
136 return true;
139 if (warn) {
140 raise_warning("call_user_func to non-existent function %s", base);
142 return false;
144 mcp.functionNamedCall(Variant::GetAsString(tv_func));
145 if (LIKELY(mcp.ci != 0)) return true;
146 if (warn) raise_warning("call_user_func to non-existent function %s", base);
147 return false;
150 if (LIKELY(Variant::GetAccessorType(tv_func) == KindOfArray)) {
151 CArrRef arr = Variant::GetAsArray(tv_func);
152 CVarRef clsname = arr.rvalAtRef(0LL);
153 CVarRef mthname = arr.rvalAtRef(1LL);
154 if (arr.size() != 2 ||
155 &clsname == &null_variant ||
156 &mthname == &null_variant) {
157 if (warn) throw_invalid_argument("function: not a valid callback array");
158 return false;
161 Variant::TypedValueAccessor tv_meth = mthname.getTypedAccessor();
162 if (!Variant::IsString(tv_meth)) {
163 if (warn) throw_invalid_argument("function: methodname not string");
164 return false;
167 Variant::TypedValueAccessor tv_cls = clsname.getTypedAccessor();
169 if (Variant::GetAccessorType(tv_cls) == KindOfObject) {
170 ObjectData *obj = Variant::GetObjectData(tv_cls);
172 StringData *smeth = Variant::GetStringData(tv_meth);
173 const char *base = smeth->data();
174 const char *cc = strstr(base, "::");
175 if (UNLIKELY(cc && cc != base && cc[2])) {
176 methodname = String(cc + 2, smeth->size() - (cc - base) - 2,
177 CopyString);
178 mcp.methodCallEx(obj, methodname);
180 if (cc - base == 4 && !strncasecmp(base, "self", 4)) {
181 classname = obj->o_getClassName();
182 } else if (cc - base == 6 &&
183 !strncasecmp(base, "parent", 6)) {
184 classname = obj->o_getParentName();
185 if (classname.empty()) {
186 if (warn) raise_warning("cannot access parent:: when current "
187 "class scope has no parent");
188 return false;
190 } else if (cc - base == 6 &&
191 !strncasecmp(base, "static", 6)) {
192 ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
193 classname = FrameInjection::GetStaticClassName(ti);
194 } else {
195 doBind = true;
196 classname = String(base, cc - base, CopyString);
198 if (LIKELY(obj->o_get_call_info_ex(classname, mcp))) {
199 if (!(mcp.ci->m_flags & CallInfo::StaticMethod)) {
200 doBind = false;
202 return true;
204 if (warn) {
205 if (!obj->o_instanceof(classname)) {
206 raise_warning("class '%s' is not a subclass of '%s'",
207 obj->o_getClassName().data(), classname.data());
208 } else {
209 raise_warning("class '%s' does not have a method '%s'",
210 classname.data(), methodname.data());
213 return false;
216 methodname = smeth;
217 if (LIKELY(mcp.methodCall(obj, methodname))) {
218 if (mcp.ci->m_flags & CallInfo::StaticMethod) {
219 doBind = true;
220 classname = obj->o_getClassName();
222 return true;
224 return false;
225 } else {
226 if (UNLIKELY(!Variant::IsString(tv_cls))) {
227 if (warn) throw_invalid_argument("function: classname not string");
228 return false;
230 StringData *sclass = Variant::GetStringData(tv_cls);
231 if (sclass->isame(s_self.get())) {
232 classname = FrameInjection::GetClassName(skip);
233 } else if (sclass->isame(s_parent.get())) {
234 classname = FrameInjection::GetParentClassName(skip);
235 if (classname.empty()) {
236 if (warn) raise_warning("cannot access parent:: when current "
237 "class scope has no parent");
238 return false;
240 } else {
241 if (sclass->isame(s_static.get())) {
242 ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
243 classname = FrameInjection::GetStaticClassName(ti);
244 } else {
245 classname = sclass;
246 doBind = true;
250 StringData *smeth = Variant::GetStringData(tv_meth);
251 methodname = smeth;
252 if (LIKELY(mcp.dynamicNamedCall(classname, methodname))) {
253 doBind &= !mcp.isObj || (mcp.ci->m_flags & CallInfo::StaticMethod);
254 return true;
257 const char *base = smeth->data();
258 const char *cc = strstr(base, "::");
259 if (UNLIKELY(cc && cc != base && cc[2])) {
260 methodname = String(cc + 2, smeth->size() - (cc - base) - 2,
261 CopyString);
262 if (cc - base == 4 && !strncasecmp(base, "self", 4)) {
263 doBind = false;
264 } else if (cc - base == 6 &&
265 !strncasecmp(base, "parent", 6)) {
266 classname = ObjectData::GetParentName(classname);
267 if (classname.empty()) {
268 if (warn) raise_warning("cannot access parent:: when current "
269 "class scope has no parent");
270 return false;
272 doBind = false;
273 } else if (cc - base == 6 &&
274 !strncasecmp(base, "static", 6)) {
275 ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
276 CStrRef cls = FrameInjection::GetStaticClassName(ti);
277 if (UNLIKELY(!classname.get()->isame(cls.get()) &&
278 !f_is_subclass_of(classname, cls))) {
279 if (warn) raise_warning("class '%s' is not a subclass of '%s'",
280 classname.data(), cls.data());
281 return false;
283 doBind = false;
284 classname = cls;
285 } else {
286 CStrRef cls = String(base, cc - base, CopyString);
287 if (UNLIKELY(!classname.get()->isame(cls.get()) &&
288 !f_is_subclass_of(classname, cls))) {
289 if (warn) raise_warning("class '%s' is not a subclass of '%s'",
290 classname.data(), cls.data());
291 return false;
293 doBind = true;
294 classname = cls;
296 if (LIKELY(mcp.dynamicNamedCall(classname, methodname))) {
297 doBind &= !mcp.isObj || (mcp.ci->m_flags & CallInfo::StaticMethod);
298 return true;
300 } else {
301 // nothing to do. we already checked this case.
304 if (warn) raise_warning("call_user_func to non-existent function %s::%s",
305 classname.data(), methodname.data());
306 return false;
308 if (warn) raise_warning("call_user_func to non-existent function");
309 return false;
311 if (warn) throw_invalid_argument("function: not string or array");
312 return false;
315 Variant f_call_user_func_array(CVarRef function, CArrRef params,
316 bool bound /* = false */) {
317 MethodCallPackage mcp;
318 String classname, methodname;
319 bool doBind;
320 if (UNLIKELY(!get_user_func_handler(function, true, mcp,
321 classname, methodname, doBind))) {
322 return null;
325 if (doBind && !bound) {
326 FrameInjection::StaticClassNameHelper scn(
327 ThreadInfo::s_threadInfo.getNoCheck(), classname);
328 ASSERT(!mcp.m_isFunc);
329 return mcp.ci->getMeth()(mcp, params);
330 } else {
331 if (mcp.m_isFunc) {
332 return mcp.ci->getFunc()(mcp.extra, params);
333 } else {
334 return mcp.ci->getMeth()(mcp, params);
339 Variant call_user_func_few_args(CVarRef function, int count, ...) {
340 ASSERT(count <= CALL_USER_FUNC_FEW_ARGS_COUNT);
341 va_list ap;
342 va_start(ap, count);
343 CVarRef a0 = (count > 0) ? *va_arg(ap, const Variant *) : null_variant;
344 CVarRef a1 = (count > 1) ? *va_arg(ap, const Variant *) : null_variant;
345 CVarRef a2 = (count > 2) ? *va_arg(ap, const Variant *) : null_variant;
346 CVarRef a3 = (count > 3) ? *va_arg(ap, const Variant *) : null_variant;
347 CVarRef a4 = (count > 4) ? *va_arg(ap, const Variant *) : null_variant;
348 CVarRef a5 = (count > 5) ? *va_arg(ap, const Variant *) : null_variant;
349 va_end(ap);
351 MethodCallPackage mcp;
352 String classname, methodname;
353 bool doBind;
354 if (UNLIKELY(!get_user_func_handler(function, false, mcp,
355 classname, methodname, doBind))) {
356 return null;
359 if (doBind) {
360 FrameInjection::StaticClassNameHelper scn(
361 ThreadInfo::s_threadInfo.getNoCheck(), classname);
362 ASSERT(!mcp.m_isFunc);
363 if (UNLIKELY(!mcp.ci->getMethFewArgs())) {
364 return mcp.ci->getMeth()(
365 mcp, Array(ArrayInit::CreateParams(count, &a0, &a1, &a2,
366 &a3, &a4, &a5)));
368 return mcp.ci->getMethFewArgs()(mcp, count, a0, a1, a2, a3, a4, a5);
369 } else {
370 void *extra = mcp.m_isFunc ? mcp.extra : (void*)&mcp;
371 if (UNLIKELY(!mcp.ci->getFuncFewArgs())) {
372 return mcp.ci->getFunc()(
373 extra, Array(ArrayInit::CreateParams(count, &a0, &a1, &a2,
374 &a3, &a4, &a5)));
376 return mcp.ci->getFuncFewArgs()(extra, count, a0, a1, a2, a3, a4, a5);
380 Variant invoke_func_few_handler(void *extra, CArrRef params,
381 Variant (*few_args)(
382 void *extra, int count,
383 INVOKE_FEW_ARGS_IMPL_ARGS)) {
384 VariantVector<INVOKE_FEW_ARGS_COUNT> args;
385 int s = params.size();
386 if (LIKELY(s != 0)) {
387 int i = s > INVOKE_FEW_ARGS_COUNT ? INVOKE_FEW_ARGS_COUNT : s;
388 ArrayData *ad(params.get());
389 ssize_t pos = ad->iter_begin();
390 do {
391 args.pushWithRef(ad->getValueRef(pos));
392 pos = ad->iter_advance(pos);
393 } while (--i);
395 return few_args(extra, s, INVOKE_FEW_ARGS_PASS_ARR_ARGS);
398 Variant invoke(CStrRef function, CArrRef params, int64 hash /* = -1 */,
399 bool tryInterp /* = true */, bool fatal /* = true */) {
400 StringData *sd = function.get();
401 ASSERT(sd && sd->data());
402 return invoke(sd->data(), params, hash < 0 ? sd->hash() : hash,
403 tryInterp, fatal);
406 Variant invoke(const char *function, CArrRef params, int64 hash /* = -1*/,
407 bool tryInterp /* = true */, bool fatal /* = true */) {
408 const CallInfo *ci;
409 void *extra;
410 if (LIKELY(get_call_info(ci, extra, function, hash))) {
411 return (ci->getFunc())(extra, params);
413 return invoke_failed(function, params, fatal);
416 Variant invoke(CVarRef function, CArrRef params,
417 bool tryInterp /* = true */, bool fatal /* = true */) {
418 const CallInfo *ci;
419 void *extra;
420 if (LIKELY(get_call_info(ci, extra, function))) {
421 return (ci->getFunc())(extra, params);
423 return invoke_failed(function, params, fatal);
426 Variant invoke_builtin(const char *s, CArrRef params, int64 hash, bool fatal) {
427 const CallInfo *ci;
428 void *extra;
429 if (LIKELY(get_call_info_builtin(ci, extra, s, hash))) {
430 return (ci->getFunc())(extra, params);
431 } else {
432 return invoke_failed(s, params, fatal);
436 Variant invoke_static_method(CStrRef s, CStrRef method, CArrRef params,
437 bool fatal /* = true */) {
438 MethodCallPackage mcp;
439 if (!fatal) mcp.noFatal();
440 mcp.dynamicNamedCall(s, method, -1);
441 if (LIKELY(mcp.ci != NULL)) {
442 return (mcp.ci->getMeth())(mcp, params);
443 } else {
444 o_invoke_failed(s.data(), method.data(), fatal);
445 return null;
449 Variant invoke_failed(CVarRef func, CArrRef params,
450 bool fatal /* = true */) {
451 if (func.isObject()) {
452 return o_invoke_failed(
453 func.objectForCall()->o_getClassName().c_str(),
454 "__invoke", fatal);
455 } else {
456 return invoke_failed(func.toString().c_str(), params, fatal);
460 Variant invoke_failed(const char *func, CArrRef params,
461 bool fatal /* = true */) {
462 INTERCEPT_INJECTION_ALWAYS("?", func, params, strongBind(r));
464 if (fatal) {
465 throw InvalidFunctionCallException(func);
466 } else {
467 raise_warning("call_user_func to non-existent function %s", func);
468 return false;
472 Variant o_invoke_failed(const char *cls, const char *meth,
473 bool fatal /* = true */) {
474 if (fatal) {
475 string msg = "Unknown method ";
476 msg += cls;
477 msg += "::";
478 msg += meth;
479 throw FatalErrorException(msg.c_str());
480 } else {
481 raise_warning("call_user_func to non-existent method %s::%s", cls, meth);
482 return false;
486 Array collect_few_args(int count, INVOKE_FEW_ARGS_IMPL_ARGS) {
487 if (RuntimeOption::UseArgArray) {
488 if (count == 0) return Array();
489 ArgArray *args = NEW(ArgArray)(count);
490 ArgArray::Argument *argp = args->getStack();
491 if (count > 0) {
492 argp->m_val.assign(a0);
493 argp++;
495 if (count > 1) {
496 argp->m_val.assign(a1);
497 argp++;
499 if (count > 2) {
500 argp->m_val.assign(a2);
501 argp++;
503 #if INVOKE_FEW_ARGS_COUNT > 3
504 if (count > 3) {
505 argp->m_val.assign(a3);
506 argp++;
508 if (count > 4) {
509 argp->m_val.assign(a4);
510 argp++;
512 if (count > 5) {
513 argp->m_val.assign(a5);
514 argp++;
516 #endif
517 #if INVOKE_FEW_ARGS_COUNT > 6
518 if (count > 6) {
519 argp->m_val.assign(a6);
520 argp++;
522 if (count > 7) {
523 argp->m_val.assign(a7);
524 argp++;
526 if (count > 8) {
527 argp->m_val.assign(a8);
528 argp++;
530 if (count > 9) {
531 argp->m_val.assign(a9);
532 argp++;
534 #endif
535 if (count > 10) ASSERT(false);
536 return args;
538 switch (count) {
539 case 0: {
540 return Array();
542 case 1: {
543 return Array(ArrayInit(1).set(a0).create());
545 case 2: {
546 return Array(ArrayInit(2).set(a0).set(a1).create());
548 case 3: {
549 return Array(ArrayInit(3).set(a0).set(a1).set(a2).create());
551 #if INVOKE_FEW_ARGS_COUNT > 3
552 case 4: {
553 return Array(ArrayInit(4).set(a0).set(a1).set(a2).
554 set(a3).create());
556 case 5: {
557 return Array(ArrayInit(5).set(a0).set(a1).set(a2).
558 set(a3).set(a4).create());
560 case 6: {
561 return Array(ArrayInit(6).set(a0).set(a1).set(a2).
562 set(a3).set(a4).set(a5).create());
564 #endif
565 #if INVOKE_FEW_ARGS_COUNT > 6
566 case 7: {
567 return Array(ArrayInit(7).set(a0).set(a1).set(a2).
568 set(a3).set(a4).set(a5).
569 set(a6).create());
571 case 8: {
572 return Array(ArrayInit(8).set(a0).set(a1).set(a2).
573 set(a3).set(a4).set(a5).
574 set(a6).set(a7).create());
576 case 9: {
577 return Array(ArrayInit(9).set(a0).set(a1).set(a2).
578 set(a3).set(a4).set(a5).
579 set(a6).set(a7).set(a8).create());
581 case 10: {
582 return Array(ArrayInit(10).set(a0).set(a1).set(a2).
583 set(a3).set(a4).set(a5).
584 set(a6).set(a7).set(a8).
585 set(a9).create());
587 #endif
588 default:
589 ASSERT(false);
591 return null;
594 void throw_instance_method_fatal(const char *name) {
595 if (!strstr(name, "::__destruct")) {
596 raise_error("Non-static method %s() cannot be called statically", name);
600 Object create_object(CStrRef s, CArrRef params, bool init /* = true */,
601 ObjectData *root /* = NULL */) {
602 Object o(create_object_only(s, root));
603 if (init) {
604 MethodCallPackage mcp;
605 mcp.construct(o);
606 if (mcp.ci) {
607 (mcp.ci->getMeth())(mcp, params);
610 return o;
613 void pause_and_exit() {
614 // NOTE: This is marked as __attribute__((noreturn)) in base/types.h
615 // Signal sent, nothing can be trusted, don't do anything, as we might
616 // write bad data, including calling exit handlers or destructors until the
617 // signal handler (StackTrace) has had a chance to exit.
618 sleep(300);
619 // Should abort first, but it not try to exit
620 pthread_exit(0);
623 void check_request_surprise(ThreadInfo *info) {
624 RequestInjectionData &p = info->m_reqInjectionData;
625 bool do_timedout, do_memExceeded, do_signaled;
627 ssize_t flags = p.fetchAndClearFlags();
628 do_timedout = (flags & RequestInjectionData::TimedOutFlag) && !p.debugger;
629 do_memExceeded = (flags & RequestInjectionData::MemExceededFlag);
630 do_signaled = (flags & RequestInjectionData::SignaledFlag);
632 if (do_timedout && !info->m_pendingException) {
633 generate_request_timeout_exception();
635 if (do_memExceeded && !info->m_pendingException) {
636 generate_memory_exceeded_exception();
638 if (do_signaled) f_pcntl_signal_dispatch();
641 void throw_pending_exception(ThreadInfo *info) {
642 ASSERT(info->m_pendingException);
643 info->m_pendingException = false;
644 FatalErrorException e(info->m_exceptionMsg, info->m_exceptionStack);
645 info->m_exceptionMsg.clear();
646 info->m_exceptionStack.reset();
647 throw e;
650 bool get_call_info(const CallInfo *&ci, void *&extra, CVarRef func) {
651 Variant::TypedValueAccessor tv_func = func.getTypedAccessor();
652 if (Variant::GetAccessorType(tv_func) == KindOfObject) {
653 ObjectData *d = Variant::GetObjectData(tv_func);
654 ci = d->t___invokeCallInfoHelper(extra);
655 return ci != NULL;
657 if (LIKELY(Variant::IsString(tv_func))) {
658 StringData *sd = Variant::GetStringData(tv_func);
659 return get_call_info(ci, extra, sd->data(), sd->hash());
661 return false;
664 bool get_call_info_no_eval(const CallInfo *&ci, void *&extra, CStrRef name) {
665 return get_call_info_no_eval(ci, extra, name->data(), name->hash());
668 void get_call_info_or_fail(const CallInfo *&ci, void *&extra, CVarRef func) {
669 if (UNLIKELY(!get_call_info(ci, extra, func))) {
670 if (func.isObject()) {
671 o_invoke_failed(
672 func.objectForCall()->o_getClassName().c_str(),
673 "__invoke", true);
674 } else {
675 throw InvalidFunctionCallException(func.toString().data());
680 void get_call_info_or_fail(const CallInfo *&ci, void *&extra, CStrRef name) {
681 if (UNLIKELY(!get_call_info(ci, extra, name->data(), name->hash()))) {
682 throw InvalidFunctionCallException(name->data());
686 Variant throw_missing_arguments(const char *fn, int num, int level /* = 0 */) {
687 if (level == 2 || RuntimeOption::ThrowMissingArguments) {
688 raise_error("Missing argument %d for %s()", num, fn);
689 } else {
690 raise_warning("Missing argument %d for %s()", num, fn);
692 return null;
695 Variant throw_toomany_arguments(const char *fn, int num, int level /* = 0 */) {
696 if (level == 2 || RuntimeOption::ThrowTooManyArguments) {
697 raise_error("Too many arguments for %s(), expected %d", fn, num);
698 } else if (level == 1 || RuntimeOption::WarnTooManyArguments) {
699 raise_warning("Too many arguments for %s(), expected %d", fn, num);
701 return null;
704 Variant throw_wrong_arguments(const char *fn, int count, int cmin, int cmax,
705 int level /* = 0 */) {
706 if (cmin >= 0 && count < cmin) {
707 return throw_missing_arguments(fn, count + 1, level);
709 if (cmax >= 0 && count > cmax) {
710 return throw_toomany_arguments(fn, cmax, level);
712 ASSERT(false);
713 return null;
716 Variant throw_missing_typed_argument(const char *fn,
717 const char *type, int arg) {
718 if (!type) {
719 raise_error("Argument %d passed to %s() must be an array, none given",
720 arg, fn);
721 } else {
722 raise_error("Argument %d passed to %s() must be "
723 "an instance of %s, none given", arg, fn, type);
725 return null;
728 void throw_bad_type_exception(const char *fmt, ...) {
729 va_list ap;
730 va_start(ap, fmt);
731 string msg;
732 Util::string_vsnprintf(msg, fmt, ap);
733 va_end(ap);
735 if (RuntimeOption::ThrowBadTypeExceptions) {
736 throw InvalidOperandException(msg.c_str());
739 raise_warning("Invalid operand type was used: %s", msg.c_str());
742 void throw_bad_array_exception() {
743 FrameInjection *fi = FrameInjection::GetStackFrame(0);
744 throw_bad_type_exception("%s expects array(s)",
745 fi ? fi->getFunction() : "(unknown)");
748 void throw_invalid_argument(const char *fmt, ...) {
749 va_list ap;
750 va_start(ap, fmt);
751 string msg;
752 Util::string_vsnprintf(msg, fmt, ap);
753 va_end(ap);
755 if (RuntimeOption::ThrowInvalidArguments) {
756 throw InvalidArgumentException(msg.c_str());
759 raise_warning("Invalid argument: %s", msg.c_str());
762 Variant throw_fatal_unset_static_property(const char *s, const char *prop) {
763 raise_error("Attempt to unset static property %s::$%s", s, prop);
764 return null;
767 void check_request_timeout_info(ThreadInfo *info, int lc) {
768 check_request_timeout(info);
769 if (info->m_pendingException) {
770 throw_pending_exception(info);
772 if (RuntimeOption::MaxLoopCount > 0 && lc > RuntimeOption::MaxLoopCount) {
773 throw FatalErrorException(0, "loop iterated over %d times",
774 RuntimeOption::MaxLoopCount);
778 void check_request_timeout_ex(const FrameInjection &fi, int lc) {
779 ThreadInfo *info = fi.getThreadInfo();
780 check_request_timeout_info(info, lc);
783 void throw_infinite_recursion_exception() {
784 if (!RuntimeOption::NoInfiniteRecursionDetection) {
785 // Reset profiler otherwise it might recurse further causing segfault
786 DECLARE_THREAD_INFO
787 info->m_profiler = NULL;
788 throw UncatchableException("infinite recursion detected");
791 void generate_request_timeout_exception() {
792 ThreadInfo *info = ThreadInfo::s_threadInfo.getNoCheck();
793 RequestInjectionData &data = info->m_reqInjectionData;
794 if (data.timeoutSeconds > 0) {
795 // This extra checking is needed, because there may be a race condition
796 // a TimeoutThread sets flag "true" right after an old request finishes and
797 // right before a new requets resets "started". In this case, we flag
798 // "timedout" back to "false".
799 if (time(0) - data.started >= data.timeoutSeconds) {
800 info->m_pendingException = true;
801 info->m_exceptionMsg = "entire web request took longer than ";
802 info->m_exceptionMsg +=
803 boost::lexical_cast<std::string>(data.timeoutSeconds);
804 info->m_exceptionMsg += " seconds and timed out";
805 if (RuntimeOption::InjectedStackTrace) {
806 info->m_exceptionStack =
807 ArrayPtr(new Array(FrameInjection::GetBacktrace(false, true)));
813 void generate_memory_exceeded_exception() {
814 ThreadInfo *info = ThreadInfo::s_threadInfo.getNoCheck();
815 info->m_pendingException = true;
816 info->m_exceptionMsg = "request has exceeded memory limit";
817 if (RuntimeOption::InjectedStackTrace) {
818 info->m_exceptionStack =
819 ArrayPtr(new Array(FrameInjection::GetBacktrace(false, true)));
823 void throw_call_non_object() {
824 throw FatalErrorException("Call to a member function on a non-object");
827 Variant throw_assign_this() {
828 throw FatalErrorException("Cannot re-assign $this");
831 void throw_unexpected_argument_type(int argNum, const char *fnName,
832 const char *expected, CVarRef val) {
833 const char *otype = NULL;
834 switch (val.getType()) {
835 case KindOfUninit:
836 case KindOfNull: otype = "null"; break;
837 case KindOfBoolean: otype = "bool"; break;
838 case KindOfInt32:
839 case KindOfInt64: otype = "int"; break;
840 case KindOfDouble: otype = "double"; break;
841 case KindOfStaticString:
842 case KindOfString: otype = "string"; break;
843 case KindOfArray: otype = "array"; break;
844 case KindOfObject: otype = val.getObjectData()->o_getClassName(); break;
845 default:
846 ASSERT(false);
848 raise_recoverable_error
849 ("Argument %d passed to %s must be an instance of %s, %s given",
850 argNum, fnName, expected, otype);
853 Object f_clone(CVarRef v) {
854 if (v.isObject()) {
855 Object clone = Object(v.toObject()->clone());
856 clone->t___clone();
857 return clone;
859 raise_error("Cannot clone non-object");
860 return Object();
863 String f_serialize(CVarRef value) {
864 switch (value.getType()) {
865 case KindOfUninit:
866 case KindOfNull:
867 return "N;";
868 case KindOfBoolean:
869 return value.getBoolean() ? "b:1;" : "b:0;";
870 case KindOfInt32:
871 case KindOfInt64: {
872 StringBuffer sb;
873 sb.append("i:");
874 sb.append(value.getInt64());
875 sb.append(';');
876 return sb.detach();
878 case KindOfStaticString:
879 case KindOfString: {
880 StringData *str = value.getStringData();
881 StringBuffer sb;
882 sb.append("s:");
883 sb.append(str->size());
884 sb.append(":\"");
885 sb.append(str->data(), str->size());
886 sb.append("\";");
887 return sb.detach();
889 case KindOfArray: {
890 ArrayData *arr = value.getArrayData();
891 if (arr->empty()) return "a:0:{}";
892 // fall-through
894 case KindOfObject:
895 case KindOfDouble: {
896 VariableSerializer vs(VariableSerializer::Serialize);
897 return vs.serialize(value, true);
899 default:
900 ASSERT(false);
901 break;
903 return "";
906 Variant unserialize_ex(CStrRef str, VariableUnserializer::Type type) {
907 if (str.empty()) {
908 return false;
911 VariableUnserializer vu(str.data(), str.size(), type);
912 Variant v;
913 try {
914 v = vu.unserialize();
915 } catch (Exception &e) {
916 raise_notice("Unable to unserialize: [%s]. [%s] %s.", (const char *)str,
917 e.getStackTrace().hexEncode().c_str(),
918 e.getMessage().c_str());
919 return false;
921 return v;
924 String concat3(CStrRef s1, CStrRef s2, CStrRef s3) {
925 TAINT_OBSERVER(TAINT_BIT_NONE, TAINT_BIT_NONE);
927 int len1 = s1.size();
928 int len2 = s2.size();
929 int len3 = s3.size();
930 int len = len1 + len2 + len3;
931 char *buf = (char *)malloc(len + 1);
932 if (buf == NULL) {
933 throw FatalErrorException(0, "malloc failed: %d", len);
935 memcpy(buf, s1.data(), len1);
936 memcpy(buf + len1, s2.data(), len2);
937 memcpy(buf + len1 + len2, s3.data(), len3);
938 buf[len] = 0;
940 return String(buf, len, AttachString);
943 String concat4(CStrRef s1, CStrRef s2, CStrRef s3, CStrRef s4) {
944 TAINT_OBSERVER(TAINT_BIT_NONE, TAINT_BIT_NONE);
946 int len1 = s1.size();
947 int len2 = s2.size();
948 int len3 = s3.size();
949 int len4 = s4.size();
950 int len = len1 + len2 + len3 + len4;
951 char *buf = (char *)malloc(len + 1);
952 if (buf == NULL) {
953 throw FatalErrorException(0, "malloc failed: %d", len);
955 memcpy(buf, s1.data(), len1);
956 memcpy(buf + len1, s2.data(), len2);
957 memcpy(buf + len1 + len2, s3.data(), len3);
958 memcpy(buf + len1 + len2 + len3, s4.data(), len4);
959 buf[len] = 0;
961 return String(buf, len, AttachString);
964 String concat5(CStrRef s1, CStrRef s2, CStrRef s3, CStrRef s4, CStrRef s5) {
965 TAINT_OBSERVER(TAINT_BIT_NONE, TAINT_BIT_NONE);
967 int len1 = s1.size();
968 int len2 = s2.size();
969 int len3 = s3.size();
970 int len4 = s4.size();
971 int len5 = s5.size();
972 int len = len1 + len2 + len3 + len4 + len5;
973 char *buf = (char *)malloc(len + 1);
974 if (buf == NULL) {
975 throw FatalErrorException(0, "malloc failed: %d", len);
977 memcpy(buf, s1.data(), len1);
978 memcpy(buf + len1, s2.data(), len2);
979 memcpy(buf + len1 + len2, s3.data(), len3);
980 memcpy(buf + len1 + len2 + len3, s4.data(), len4);
981 memcpy(buf + len1 + len2 + len3 + len4, s5.data(), len5);
982 buf[len] = 0;
983 return String(buf, len, AttachString);
986 String concat6(CStrRef s1, CStrRef s2, CStrRef s3, CStrRef s4, CStrRef s5,
987 CStrRef s6) {
988 TAINT_OBSERVER(TAINT_BIT_NONE, TAINT_BIT_NONE);
990 int len1 = s1.size();
991 int len2 = s2.size();
992 int len3 = s3.size();
993 int len4 = s4.size();
994 int len5 = s5.size();
995 int len6 = s6.size();
996 int len = len1 + len2 + len3 + len4 + len5 + len6;
997 char *buf = (char *)malloc(len + 1);
998 if (buf == NULL) {
999 throw FatalErrorException(0, "malloc failed: %d", len);
1001 memcpy(buf, s1.data(), len1);
1002 memcpy(buf + len1, s2.data(), len2);
1003 memcpy(buf + len1 + len2, s3.data(), len3);
1004 memcpy(buf + len1 + len2 + len3, s4.data(), len4);
1005 memcpy(buf + len1 + len2 + len3 + len4, s5.data(), len5);
1006 memcpy(buf + len1 + len2 + len3 + len4 + len5, s6.data(), len6);
1007 buf[len] = 0;
1008 return String(buf, len, AttachString);
1011 bool empty(CVarRef v, bool offset) {
1012 return empty(v, Variant(offset));
1014 bool empty(CVarRef v, int64 offset) {
1015 Variant::TypedValueAccessor tva = v.getTypedAccessor();
1016 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
1017 return empty(Variant::GetArrayData(tva)->get(offset));
1019 return empty(v, VarNR(offset));
1021 bool empty(CVarRef v, double offset) {
1022 return empty(v, VarNR(offset));
1024 bool empty(CVarRef v, CArrRef offset) {
1025 return empty(v, VarNR(offset));
1027 bool empty(CVarRef v, CObjRef offset) {
1028 return empty(v, VarNR(offset));
1030 bool empty(CVarRef v, litstr offset, bool isString /* = false */) {
1031 Variant::TypedValueAccessor tva = v.getTypedAccessor();
1032 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
1033 return empty(Variant::GetAsArray(tva).
1034 rvalAtRef(offset, AccessFlags::IsKey(isString)));
1036 return empty(v, Variant(offset));
1039 bool empty(CVarRef v, CStrRef offset, bool isString /* = false */) {
1040 Variant::TypedValueAccessor tva = v.getTypedAccessor();
1041 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
1042 return empty(Variant::GetAsArray(tva).
1043 rvalAtRef(offset, AccessFlags::IsKey(isString)));
1045 return empty(v, VarNR(offset));
1048 bool empty(CVarRef v, CVarRef offset) {
1049 Variant::TypedValueAccessor tva = v.getTypedAccessor();
1050 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
1051 return empty(Variant::GetAsArray(tva).rvalAtRef(offset));
1053 if (Variant::GetAccessorType(tva) == KindOfObject) {
1054 if (!Variant::GetArrayAccess(tva)->
1055 o_invoke(s_offsetExists, Array::Create(offset))) {
1056 return true;
1058 // fall through to check for 'empty'ness of the value.
1059 } else if (Variant::IsString(tva)) {
1060 uint64 pos = offset.toInt64();
1061 if (pos >= (uint64)Variant::GetStringData(tva)->size()) {
1062 return true;
1065 return empty(v.rvalAt(offset));
1068 bool isset(CArrRef v, int64 offset) {
1069 return isset(v.rvalAtRef(offset));
1071 bool isset(CArrRef v, CArrRef offset) {
1072 return isset(v, VarNR(offset));
1074 bool isset(CArrRef v, CObjRef offset) {
1075 return isset(v, VarNR(offset));
1077 bool isset(CArrRef v, CStrRef offset, bool isString /* = false */) {
1078 return isset(v.rvalAtRef(offset, AccessFlags::IsKey(isString)));
1080 bool isset(CArrRef v, litstr offset, bool isString /* = false */) {
1081 return isset(v.rvalAtRef(offset, AccessFlags::IsKey(isString)));
1083 bool isset(CArrRef v, CVarRef offset) {
1084 return isset(v.rvalAtRef(offset));
1087 bool isset(CVarRef v, bool offset) {
1088 return isset(v, VarNR(offset));
1090 bool isset(CVarRef v, int64 offset) {
1091 Variant::TypedValueAccessor tva = v.getTypedAccessor();
1092 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
1093 return isset(Variant::GetArrayData(tva)->get(offset));
1095 if (Variant::GetAccessorType(tva) == KindOfObject) {
1096 return Variant::GetArrayAccess(tva)->
1097 o_invoke(s_offsetExists, Array::Create(offset), -1);
1099 if (Variant::IsString(tva)) {
1100 return (uint64)offset < (uint64)Variant::GetStringData(tva)->size();
1102 return false;
1104 bool isset(CVarRef v, double offset) {
1105 return isset(v, VarNR(offset));
1107 bool isset(CVarRef v, CArrRef offset) {
1108 return isset(v, VarNR(offset));
1110 bool isset(CVarRef v, CObjRef offset) {
1111 return isset(v, VarNR(offset));
1113 bool isset(CVarRef v, CVarRef offset) {
1114 Variant::TypedValueAccessor tva = v.getTypedAccessor();
1115 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
1116 return isset(Variant::GetAsArray(tva).rvalAtRef(offset));
1118 if (Variant::GetAccessorType(tva) == KindOfObject) {
1119 return Variant::GetArrayAccess(tva)->
1120 o_invoke(s_offsetExists, Array::Create(offset), -1);
1122 if (Variant::IsString(tva)) {
1123 uint64 pos = offset.toInt64();
1124 return pos < (uint64)Variant::GetStringData(tva)->size();
1126 return false;
1128 bool isset(CVarRef v, litstr offset, bool isString /* = false */) {
1129 Variant::TypedValueAccessor tva = v.getTypedAccessor();
1130 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
1131 return isset(Variant::GetAsArray(tva).rvalAtRef(
1132 offset, AccessFlags::IsKey(isString)));
1134 if (Variant::GetAccessorType(tva) == KindOfObject ||
1135 Variant::IsString(tva)) {
1136 return isset(v, Variant(offset));
1138 return false;
1141 bool isset(CVarRef v, CStrRef offset, bool isString /* = false */) {
1142 Variant::TypedValueAccessor tva = v.getTypedAccessor();
1143 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
1144 return isset(Variant::GetAsArray(tva).rvalAtRef(
1145 offset, AccessFlags::IsKey(isString)));
1147 if (Variant::GetAccessorType(tva) == KindOfObject ||
1148 Variant::IsString(tva)) {
1149 return isset(v, Variant(offset));
1151 return false;
1154 String get_source_filename(litstr path, bool dir_component /* = false */) {
1155 String ret;
1156 if (path[0] == '/') {
1157 ret = path;
1158 } else if (RuntimeOption::SourceRoot.empty()) {
1159 ret = Process::GetCurrentDirectory() + "/" + path;
1160 } else {
1161 ret = RuntimeOption::SourceRoot + path;
1164 if (dir_component) {
1165 return f_dirname(ret);
1166 } else {
1167 return ret;
1171 Variant include_impl_invoke(CStrRef file, bool once, LVariableTable* variables,
1172 const char *currentDir) {
1173 if (file[0] == '/') {
1174 if (RuntimeOption::SandboxMode || !RuntimeOption::AlwaysUseRelativePath) {
1175 try {
1176 return invoke_file(file, once, variables, currentDir);
1177 } catch(PhpFileDoesNotExistException &e) {}
1179 string server_root = RuntimeOption::SourceRoot;
1180 if (server_root.empty()) {
1181 server_root = string(g_context->getCwd()->data());
1182 if (server_root.empty() || server_root[server_root.size() - 1] != '/') {
1183 server_root += "/";
1187 String rel_path(Util::relativePath(server_root, string(file.data())));
1189 // Don't try/catch - We want the exception to be passed along
1190 return invoke_file(rel_path, once, variables, currentDir);
1191 } else {
1192 // Don't try/catch - We want the exception to be passed along
1193 return invoke_file(file, once, variables, currentDir);
1198 * Used by include_impl. resolve_include() needs some way of checking the
1199 * existence of a file path, which for hphpc means attempting to invoke it.
1200 * This struct carries some context information needed for the invocation, as
1201 * well as a place for the return value of invoking the file.
1203 struct IncludeImplInvokeContext {
1204 bool once;
1205 LVariableTable* variables;
1206 const char* currentDir;
1208 Variant returnValue;
1211 static bool include_impl_invoke_context(CStrRef file, void* ctx) {
1212 struct IncludeImplInvokeContext* context = (IncludeImplInvokeContext*)ctx;
1213 bool invoked_file = false;
1214 try {
1215 context->returnValue = include_impl_invoke(file, context->once,
1216 context->variables,
1217 context->currentDir);
1218 invoked_file = true;
1219 } catch (PhpFileDoesNotExistException& e) {
1220 context->returnValue = false;
1222 return invoked_file;
1226 * tryFile is a pointer to a function that resolve_include() will use to
1227 * determine if a path references a real file. ctx is a pointer to some context
1228 * information that will be passed through to tryFile. (It's a hacky closure)
1230 String resolve_include(CStrRef file, const char* currentDir,
1231 bool (*tryFile)(CStrRef file, void*), void* ctx) {
1232 const char* c_file = file->data();
1234 if (c_file[0] == '/') {
1235 String can_path(Util::canonicalize(file.c_str(), file.size()),
1236 AttachString);
1238 if (tryFile(can_path, ctx)) {
1239 return can_path;
1242 } else if ((c_file[0] == '.' && (c_file[1] == '/' || (
1243 c_file[1] == '.' && c_file[2] == '/')))) {
1245 String path(String(g_context->getCwd() + "/" + file));
1246 String can_path(Util::canonicalize(path.c_str(), path.size()),
1247 AttachString);
1249 if (tryFile(can_path, ctx)) {
1250 return can_path;
1253 } else {
1255 Array includePaths = g_context->getIncludePathArray();
1256 unsigned int path_count = includePaths.size();
1258 for (int i = 0; i < (int)path_count; i++) {
1259 String path("");
1260 String includePath = includePaths[i];
1262 if (includePath[0] != '/') {
1263 path += (g_context->getCwd() + "/");
1266 path += includePath;
1268 if (path[path.size() - 1] != '/') {
1269 path += "/";
1272 path += file;
1273 String can_path(Util::canonicalize(path.c_str(), path.size()),
1274 AttachString);
1276 if (tryFile(can_path, ctx)) {
1277 return can_path;
1281 if (currentDir[0] == '/') {
1282 // We are in hphpi, which passes an absolute path
1283 String path(currentDir);
1284 path += "/";
1285 path += file;
1286 String can_path(Util::canonicalize(path.c_str(), path.size()),
1287 AttachString);
1289 if (tryFile(can_path, ctx)) {
1290 return can_path;
1292 } else {
1293 // Regular hphp
1294 String path(g_context->getCwd() + "/" + currentDir + file);
1295 String can_path(Util::canonicalize(path.c_str(), path.size()),
1296 AttachString);
1298 if (tryFile(can_path, ctx)) {
1299 return can_path;
1304 return String((StringData*)NULL);
1307 static Variant include_impl(CStrRef file, bool once,
1308 LVariableTable* variables,
1309 const char *currentDir, bool required,
1310 bool raiseNotice) {
1311 struct IncludeImplInvokeContext ctx = {once, variables, currentDir};
1312 String can_path = resolve_include(file, currentDir,
1313 include_impl_invoke_context, (void*)&ctx);
1315 if (can_path.isNull()) {
1316 // Failure
1317 if (raiseNotice) {
1318 raise_notice("Tried to invoke %s but file not found.", file->data());
1320 if (required) {
1321 String ms = "Required file that does not exist: ";
1322 ms += file;
1323 throw FatalErrorException(ms.data());
1325 return false;
1328 return ctx.returnValue;
1331 Variant include(CStrRef file, bool once /* = false */,
1332 LVariableTable* variables /* = NULL */,
1333 const char *currentDir /* = NULL */,
1334 bool raiseNotice /*= true*/) {
1335 return include_impl(file, once, variables, currentDir, false, raiseNotice);
1338 Variant require(CStrRef file, bool once /* = false */,
1339 LVariableTable* variables /* = NULL */,
1340 const char *currentDir /* = NULL */,
1341 bool raiseNotice /*= true*/) {
1342 return include_impl(file, once, variables, currentDir, true, raiseNotice);
1345 ///////////////////////////////////////////////////////////////////////////////
1346 // class Limits
1348 IMPLEMENT_REQUEST_LOCAL(AutoloadHandler, AutoloadHandler::s_instance);
1350 void AutoloadHandler::requestInit() {
1351 m_running = false;
1352 m_handlers.reset();
1355 void AutoloadHandler::requestShutdown() {
1356 m_handlers.reset();
1359 void AutoloadHandler::fiberInit(AutoloadHandler *handler,
1360 FiberReferenceMap &refMap) {
1361 m_running = handler->m_running;
1362 m_handlers = handler->m_handlers.fiberMarshal(refMap);
1365 void AutoloadHandler::fiberExit(AutoloadHandler *handler,
1366 FiberReferenceMap &refMap,
1367 FiberAsyncFunc::Strategy default_strategy) {
1368 refMap.unmarshal(m_handlers, handler->m_handlers, default_strategy);
1372 * invokeHandler returns true if any autoload handlers were executed,
1373 * false otherwise. When this function returns true, it is the caller's
1374 * responsibility to check if the given class or interface exists.
1376 bool AutoloadHandler::invokeHandler(CStrRef className,
1377 const bool *declared /* = NULL */,
1378 bool forceSplStack /* = false */) {
1379 Array params(ArrayInit(1).set(className).create());
1380 bool l_running = m_running;
1381 m_running = true;
1382 if (m_handlers.isNull() && !forceSplStack) {
1383 if (function_exists(s___autoload)) {
1384 invoke(s___autoload, params, -1, true, false);
1385 m_running = l_running;
1386 return true;
1388 m_running = l_running;
1389 return false;
1391 if (empty(m_handlers)) {
1392 m_running = l_running;
1393 return false;
1395 Object autoloadException;
1396 for (ArrayIter iter(m_handlers); iter; ++iter) {
1397 try {
1398 f_call_user_func_array(iter.second(), params);
1399 } catch (Object& ex) {
1400 ASSERT(ex.instanceof(s_exception));
1401 if (autoloadException.isNull()) {
1402 autoloadException = ex;
1403 } else {
1404 Object cur = ex;
1405 Variant next = cur->o_get(s_previous, false, s_exception);
1406 while (next.isObject()) {
1407 cur = next.toObject();
1408 next = cur->o_get(s_previous, false, s_exception);
1410 cur->o_set(s_previous, autoloadException, false, s_exception);
1411 autoloadException = ex;
1414 // TODO: f_class_exists() does not check interfaces. We need to
1415 // fix this to check for both classes and interfaces.
1416 if (declared ? *declared : f_class_exists(className, false)) {
1417 break;
1420 m_running = l_running;
1421 if (!autoloadException.isNull()) {
1422 throw autoloadException;
1424 return true;
1427 bool AutoloadHandler::addHandler(CVarRef handler, bool prepend) {
1428 String name = getSignature(handler);
1429 if (name.isNull()) return false;
1431 if (m_handlers.isNull()) {
1432 m_handlers = Array::Create();
1435 if (!prepend) {
1436 // The following ensures that the handler is added at the end
1437 m_handlers.remove(name, true);
1438 m_handlers.add(name, handler, true);
1439 } else {
1440 // This adds the handler at the beginning
1441 m_handlers = CREATE_MAP1(name, handler) + m_handlers;
1443 return true;
1446 bool AutoloadHandler::isRunning() {
1447 return m_running;
1450 void AutoloadHandler::removeHandler(CVarRef handler) {
1451 String name = getSignature(handler);
1452 if (name.isNull()) return;
1453 m_handlers.remove(name, true);
1456 void AutoloadHandler::removeAllHandlers() {
1457 m_handlers.reset();
1460 String AutoloadHandler::getSignature(CVarRef handler) {
1461 Variant name;
1462 if (!f_is_callable(handler, false, ref(name))) {
1463 return null_string;
1465 String lName = StringUtil::ToLower(name);
1466 if (handler.isArray()) {
1467 Variant first = handler.getArrayData()->get(0LL);
1468 if (first.isObject()) {
1469 // Add the object address as part of the signature
1470 int64 data = (int64)first.getObjectData();
1471 lName += String((const char *)&data, sizeof(data), CopyString);
1474 return lName;
1477 bool function_exists(CStrRef function_name) {
1478 String name = get_renamed_function(function_name);
1479 return ClassInfo::FindFunction(name);
1482 void checkClassExists(CStrRef name, Globals *g, bool nothrow /* = false */) {
1483 if (g->class_exists(name)) return;
1484 AutoloadHandler::s_instance->invokeHandler(name);
1485 if (nothrow) return;
1486 if (!g->class_exists(name)) {
1487 string msg = "unknown class ";
1488 msg += name.c_str();
1489 throw_fatal(msg.c_str());
1493 bool autoloadClassThrow(CStrRef name, bool *declared) {
1494 if (autoloadClassNoThrow(name, declared)) return true;
1495 string msg = "unknown class ";
1496 msg += name.c_str();
1497 throw_fatal(msg.c_str());
1498 return false;
1501 bool autoloadClassNoThrow(CStrRef name, bool *declared) {
1502 AutoloadHandler::s_instance->invokeHandler(name, declared);
1503 return declared && *declared;
1506 bool autoloadInterfaceThrow(CStrRef name, bool *declared) {
1507 if (autoloadInterfaceNoThrow(name, declared)) return true;
1508 string msg = "unknown interface ";
1509 msg += name.c_str();
1510 throw_fatal(msg.c_str());
1511 return false;
1514 bool autoloadInterfaceNoThrow(CStrRef name, bool *declared) {
1515 AutoloadHandler::s_instance->invokeHandler(name, declared);
1516 return declared && *declared;
1519 Variant &get_static_property_lval(CStrRef s, const char *prop) {
1520 Variant *ret = get_static_property_lv(s, prop);
1521 if (ret) return *ret;
1522 return Variant::lvalBlackHole();
1525 Variant invoke_static_method_bind(CStrRef s, CStrRef method,
1526 CArrRef params, bool fatal /* = true */) {
1527 ThreadInfo *info = ThreadInfo::s_threadInfo.getNoCheck();
1528 String cls = s;
1529 bool isStatic = cls->isame(s_static.get());
1530 if (isStatic) {
1531 cls = FrameInjection::GetStaticClassName(info);
1532 } else {
1533 FrameInjection::SetStaticClassName(info, cls);
1535 Variant ret = invoke_static_method(cls, method, params, fatal);
1536 if (!isStatic) {
1537 FrameInjection::ResetStaticClassName(info);
1539 return strongBind(ret);
1542 bool MethodCallPackage::methodCall(ObjectData *self, CStrRef method,
1543 int64 prehash /* = -1 */) {
1544 isObj = true;
1545 rootObj = self;
1546 name = &method;
1547 return self->o_get_call_info(*this, prehash);
1550 HOT_FUNC
1551 bool MethodCallPackage::methodCall(CVarRef self, CStrRef method,
1552 int64 prehash /* = -1 */) {
1553 isObj = true;
1554 ObjectData *s = self.objectForCall();
1555 rootObj = s;
1556 name = &method;
1557 return s->o_get_call_info(*this, prehash);
1560 bool MethodCallPackage::dynamicNamedCall(CVarRef self, CStrRef method,
1561 int64 prehash /* = -1 */) {
1562 name = &method;
1563 if (self.is(KindOfObject)) {
1564 isObj = true;
1565 rootObj = self.getObjectData();
1566 return rootObj->o_get_call_info(*this, prehash);
1567 } else {
1568 String str = self.toString();
1569 ObjectData *obj = FrameInjection::GetThis();
1570 if (!obj || !obj->o_instanceof(str)) {
1571 rootCls = str.get();
1572 return get_call_info_static_method(*this);
1573 } else {
1574 isObj = true;
1575 rootObj = obj;
1576 return obj->o_get_call_info_ex(str->data(), *this, prehash);
1581 bool MethodCallPackage::dynamicNamedCall(CStrRef self, CStrRef method,
1582 int64 prehash /* = -1 */) {
1583 rootCls = self.get();
1584 name = &method;
1585 ObjectData *obj = FrameInjection::GetThis();
1586 if (!obj || !obj->o_instanceof(self)) {
1587 return get_call_info_static_method(*this);
1588 } else {
1589 isObj = true;
1590 rootObj = obj;
1591 return obj->o_get_call_info_ex(self, *this, prehash);
1595 void MethodCallPackage::functionNamedCall(CVarRef func) {
1596 m_isFunc = true;
1597 get_call_info(ci, extra, func);
1600 void MethodCallPackage::functionNamedCall(CStrRef func) {
1601 m_isFunc = true;
1602 get_call_info(ci, extra, func.data());
1605 void MethodCallPackage::functionNamedCall(ObjectData *func) {
1606 m_isFunc = true;
1607 ci = func->t___invokeCallInfoHelper(extra);
1610 void MethodCallPackage::construct(CObjRef self) {
1611 rootObj = self.get();
1612 self->getConstructor(*this);
1615 void MethodCallPackage::fail() {
1616 if (m_fatal) {
1617 o_invoke_failed(isObj ? rootObj->o_getClassName() : String(rootCls),
1618 *name, true);
1621 String MethodCallPackage::getClassName() {
1622 if (isObj) {
1623 return rootObj->o_getClassName();
1624 } else {
1625 return rootCls;
1628 void MethodCallPackage::lateStaticBind(ThreadInfo *ti) {
1629 rootCls = FrameInjection::GetStaticClassName(ti).get();
1630 get_call_info_static_method(*this);
1633 HOT_FUNC
1634 const CallInfo *MethodCallPackage::bindClass(FrameInjection &fi) {
1635 if (ci->m_flags & CallInfo::StaticMethod) {
1636 fi.setStaticClassName(obj->getRoot()->o_getClassName());
1638 return ci;
1641 ///////////////////////////////////////////////////////////////////////////////
1642 // debugger and code coverage instrumentation
1644 void throw_exception(CObjRef e) {
1645 if (!Eval::Debugger::InterruptException(e)) return;
1646 throw e;
1649 bool set_line(int line0, int char0 /* = 0 */, int line1 /* = 0 */,
1650 int char1 /* = 0 */) {
1651 ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
1652 FrameInjection *frame = ti->m_top;
1653 if (frame) {
1654 frame->setLine(line0);
1655 if (RuntimeOption::EnableDebugger && ti->m_reqInjectionData.debugger) {
1656 Eval::InterruptSite site(frame, Object(), char0, line1, char1);
1657 Eval::Debugger::InterruptFileLine(site);
1658 if (site.isJumping()) {
1659 return false;
1662 if (RuntimeOption::RecordCodeCoverage) {
1663 Eval::CodeCoverage::Record(frame->getFileName().data(), line0, line1);
1666 return true;
1669 ///////////////////////////////////////////////////////////////////////////////