2 +----------------------------------------------------------------------+
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>
40 ///////////////////////////////////////////////////////////////////////////////
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");
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());
75 String
getUndefinedConstant(CStrRef name
) {
76 raise_notice("Use of undefined constant %s - assumed '%s'",
77 name
.c_str(), name
.c_str());
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 */) {
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");
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
),
116 } else if (cc
- base
== 6 &&
117 !strncasecmp(base
, "parent", 6)) {
118 CStrRef cls
= FrameInjection::GetParentClassName(skip
);
120 raise_warning("cannot access parent:: when current "
121 "class scope has no parent");
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
),
133 classname
= String(base
, cc
- base
, CopyString
);
134 if (LIKELY(mcp
.dynamicNamedCall(classname
, methodname
))) {
135 doBind
= !mcp
.isObj
|| (mcp
.ci
->m_flags
& CallInfo::StaticMethod
);
140 raise_warning("call_user_func to non-existent function %s", base
);
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
);
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");
161 Variant::TypedValueAccessor tv_meth
= mthname
.getTypedAccessor();
162 if (!Variant::IsString(tv_meth
)) {
163 if (warn
) throw_invalid_argument("function: methodname not string");
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,
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");
190 } else if (cc
- base
== 6 &&
191 !strncasecmp(base
, "static", 6)) {
192 ThreadInfo
*ti
= ThreadInfo::s_threadInfo
.getNoCheck();
193 classname
= FrameInjection::GetStaticClassName(ti
);
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
)) {
205 if (!obj
->o_instanceof(classname
)) {
206 raise_warning("class '%s' is not a subclass of '%s'",
207 obj
->o_getClassName().data(), classname
.data());
209 raise_warning("class '%s' does not have a method '%s'",
210 classname
.data(), methodname
.data());
217 if (LIKELY(mcp
.methodCall(obj
, methodname
))) {
218 if (mcp
.ci
->m_flags
& CallInfo::StaticMethod
) {
220 classname
= obj
->o_getClassName();
226 if (UNLIKELY(!Variant::IsString(tv_cls
))) {
227 if (warn
) throw_invalid_argument("function: classname not string");
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");
241 if (sclass
->isame(s_static
.get())) {
242 ThreadInfo
*ti
= ThreadInfo::s_threadInfo
.getNoCheck();
243 classname
= FrameInjection::GetStaticClassName(ti
);
250 StringData
*smeth
= Variant::GetStringData(tv_meth
);
252 if (LIKELY(mcp
.dynamicNamedCall(classname
, methodname
))) {
253 doBind
&= !mcp
.isObj
|| (mcp
.ci
->m_flags
& CallInfo::StaticMethod
);
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,
262 if (cc
- base
== 4 && !strncasecmp(base
, "self", 4)) {
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");
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());
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());
296 if (LIKELY(mcp
.dynamicNamedCall(classname
, methodname
))) {
297 doBind
&= !mcp
.isObj
|| (mcp
.ci
->m_flags
& CallInfo::StaticMethod
);
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());
308 if (warn
) raise_warning("call_user_func to non-existent function");
311 if (warn
) throw_invalid_argument("function: not string or array");
315 Variant
f_call_user_func_array(CVarRef function
, CArrRef params
,
316 bool bound
/* = false */) {
317 MethodCallPackage mcp
;
318 String classname
, methodname
;
320 if (UNLIKELY(!get_user_func_handler(function
, true, mcp
,
321 classname
, methodname
, doBind
))) {
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
);
332 return mcp
.ci
->getFunc()(mcp
.extra
, params
);
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
);
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
;
351 MethodCallPackage mcp
;
352 String classname
, methodname
;
354 if (UNLIKELY(!get_user_func_handler(function
, false, mcp
,
355 classname
, methodname
, 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
,
368 return mcp
.ci
->getMethFewArgs()(mcp
, count
, a0
, a1
, a2
, a3
, a4
, a5
);
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
,
376 return mcp
.ci
->getFuncFewArgs()(extra
, count
, a0
, a1
, a2
, a3
, a4
, a5
);
380 Variant
invoke_func_few_handler(void *extra
, CArrRef params
,
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();
391 args
.pushWithRef(ad
->getValueRef(pos
));
392 pos
= ad
->iter_advance(pos
);
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
,
406 Variant
invoke(const char *function
, CArrRef params
, int64 hash
/* = -1*/,
407 bool tryInterp
/* = true */, bool fatal
/* = true */) {
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 */) {
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
) {
429 if (LIKELY(get_call_info_builtin(ci
, extra
, s
, hash
))) {
430 return (ci
->getFunc())(extra
, params
);
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
);
444 o_invoke_failed(s
.data(), method
.data(), fatal
);
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(),
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
));
465 throw InvalidFunctionCallException(func
);
467 raise_warning("call_user_func to non-existent function %s", func
);
472 Variant
o_invoke_failed(const char *cls
, const char *meth
,
473 bool fatal
/* = true */) {
475 string msg
= "Unknown method ";
479 throw FatalErrorException(msg
.c_str());
481 raise_warning("call_user_func to non-existent method %s::%s", cls
, meth
);
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();
492 argp
->m_val
.assign(a0
);
496 argp
->m_val
.assign(a1
);
500 argp
->m_val
.assign(a2
);
503 #if INVOKE_FEW_ARGS_COUNT > 3
505 argp
->m_val
.assign(a3
);
509 argp
->m_val
.assign(a4
);
513 argp
->m_val
.assign(a5
);
517 #if INVOKE_FEW_ARGS_COUNT > 6
519 argp
->m_val
.assign(a6
);
523 argp
->m_val
.assign(a7
);
527 argp
->m_val
.assign(a8
);
531 argp
->m_val
.assign(a9
);
535 if (count
> 10) ASSERT(false);
543 return Array(ArrayInit(1).set(a0
).create());
546 return Array(ArrayInit(2).set(a0
).set(a1
).create());
549 return Array(ArrayInit(3).set(a0
).set(a1
).set(a2
).create());
551 #if INVOKE_FEW_ARGS_COUNT > 3
553 return Array(ArrayInit(4).set(a0
).set(a1
).set(a2
).
557 return Array(ArrayInit(5).set(a0
).set(a1
).set(a2
).
558 set(a3
).set(a4
).create());
561 return Array(ArrayInit(6).set(a0
).set(a1
).set(a2
).
562 set(a3
).set(a4
).set(a5
).create());
565 #if INVOKE_FEW_ARGS_COUNT > 6
567 return Array(ArrayInit(7).set(a0
).set(a1
).set(a2
).
568 set(a3
).set(a4
).set(a5
).
572 return Array(ArrayInit(8).set(a0
).set(a1
).set(a2
).
573 set(a3
).set(a4
).set(a5
).
574 set(a6
).set(a7
).create());
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());
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
).
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
));
604 MethodCallPackage mcp
;
607 (mcp
.ci
->getMeth())(mcp
, params
);
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.
619 // Should abort first, but it not try to exit
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();
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
);
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());
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()) {
672 func
.objectForCall()->o_getClassName().c_str(),
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
);
690 raise_warning("Missing argument %d for %s()", num
, fn
);
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
);
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
);
716 Variant
throw_missing_typed_argument(const char *fn
,
717 const char *type
, int arg
) {
719 raise_error("Argument %d passed to %s() must be an array, none given",
722 raise_error("Argument %d passed to %s() must be "
723 "an instance of %s, none given", arg
, fn
, type
);
728 void throw_bad_type_exception(const char *fmt
, ...) {
732 Util::string_vsnprintf(msg
, fmt
, 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
, ...) {
752 Util::string_vsnprintf(msg
, fmt
, 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
);
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
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()) {
836 case KindOfNull
: otype
= "null"; break;
837 case KindOfBoolean
: otype
= "bool"; break;
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;
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
) {
855 Object clone
= Object(v
.toObject()->clone());
859 raise_error("Cannot clone non-object");
863 String
f_serialize(CVarRef value
) {
864 switch (value
.getType()) {
869 return value
.getBoolean() ? "b:1;" : "b:0;";
874 sb
.append(value
.getInt64());
878 case KindOfStaticString
:
880 StringData
*str
= value
.getStringData();
883 sb
.append(str
->size());
885 sb
.append(str
->data(), str
->size());
890 ArrayData
*arr
= value
.getArrayData();
891 if (arr
->empty()) return "a:0:{}";
896 VariableSerializer
vs(VariableSerializer::Serialize
);
897 return vs
.serialize(value
, true);
906 Variant
unserialize_ex(CStrRef str
, VariableUnserializer::Type type
) {
911 VariableUnserializer
vu(str
.data(), str
.size(), type
);
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());
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);
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
);
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);
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
);
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);
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
);
983 return String(buf
, len
, AttachString
);
986 String
concat6(CStrRef s1
, CStrRef s2
, CStrRef s3
, CStrRef s4
, CStrRef s5
,
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);
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
);
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
))) {
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()) {
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();
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();
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
));
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
));
1154 String
get_source_filename(litstr path
, bool dir_component
/* = false */) {
1156 if (path
[0] == '/') {
1158 } else if (RuntimeOption::SourceRoot
.empty()) {
1159 ret
= Process::GetCurrentDirectory() + "/" + path
;
1161 ret
= RuntimeOption::SourceRoot
+ path
;
1164 if (dir_component
) {
1165 return f_dirname(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
) {
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] != '/') {
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
);
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
{
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;
1215 context
->returnValue
= include_impl_invoke(file
, context
->once
,
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()),
1238 if (tryFile(can_path
, ctx
)) {
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()),
1249 if (tryFile(can_path
, ctx
)) {
1255 Array includePaths
= g_context
->getIncludePathArray();
1256 unsigned int path_count
= includePaths
.size();
1258 for (int i
= 0; i
< (int)path_count
; i
++) {
1260 String includePath
= includePaths
[i
];
1262 if (includePath
[0] != '/') {
1263 path
+= (g_context
->getCwd() + "/");
1266 path
+= includePath
;
1268 if (path
[path
.size() - 1] != '/') {
1273 String
can_path(Util::canonicalize(path
.c_str(), path
.size()),
1276 if (tryFile(can_path
, ctx
)) {
1281 if (currentDir
[0] == '/') {
1282 // We are in hphpi, which passes an absolute path
1283 String
path(currentDir
);
1286 String
can_path(Util::canonicalize(path
.c_str(), path
.size()),
1289 if (tryFile(can_path
, ctx
)) {
1294 String
path(g_context
->getCwd() + "/" + currentDir
+ file
);
1295 String
can_path(Util::canonicalize(path
.c_str(), path
.size()),
1298 if (tryFile(can_path
, ctx
)) {
1304 return String((StringData
*)NULL
);
1307 static Variant
include_impl(CStrRef file
, bool once
,
1308 LVariableTable
* variables
,
1309 const char *currentDir
, bool required
,
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()) {
1318 raise_notice("Tried to invoke %s but file not found.", file
->data());
1321 String ms
= "Required file that does not exist: ";
1323 throw FatalErrorException(ms
.data());
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 ///////////////////////////////////////////////////////////////////////////////
1348 IMPLEMENT_REQUEST_LOCAL(AutoloadHandler
, AutoloadHandler::s_instance
);
1350 void AutoloadHandler::requestInit() {
1355 void AutoloadHandler::requestShutdown() {
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
;
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
;
1388 m_running
= l_running
;
1391 if (empty(m_handlers
)) {
1392 m_running
= l_running
;
1395 Object autoloadException
;
1396 for (ArrayIter
iter(m_handlers
); iter
; ++iter
) {
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
;
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)) {
1420 m_running
= l_running
;
1421 if (!autoloadException
.isNull()) {
1422 throw autoloadException
;
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();
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);
1440 // This adds the handler at the beginning
1441 m_handlers
= CREATE_MAP1(name
, handler
) + m_handlers
;
1446 bool AutoloadHandler::isRunning() {
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() {
1460 String
AutoloadHandler::getSignature(CVarRef handler
) {
1462 if (!f_is_callable(handler
, false, ref(name
))) {
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
);
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());
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());
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();
1529 bool isStatic
= cls
->isame(s_static
.get());
1531 cls
= FrameInjection::GetStaticClassName(info
);
1533 FrameInjection::SetStaticClassName(info
, cls
);
1535 Variant ret
= invoke_static_method(cls
, method
, params
, fatal
);
1537 FrameInjection::ResetStaticClassName(info
);
1539 return strongBind(ret
);
1542 bool MethodCallPackage::methodCall(ObjectData
*self
, CStrRef method
,
1543 int64 prehash
/* = -1 */) {
1547 return self
->o_get_call_info(*this, prehash
);
1551 bool MethodCallPackage::methodCall(CVarRef self
, CStrRef method
,
1552 int64 prehash
/* = -1 */) {
1554 ObjectData
*s
= self
.objectForCall();
1557 return s
->o_get_call_info(*this, prehash
);
1560 bool MethodCallPackage::dynamicNamedCall(CVarRef self
, CStrRef method
,
1561 int64 prehash
/* = -1 */) {
1563 if (self
.is(KindOfObject
)) {
1565 rootObj
= self
.getObjectData();
1566 return rootObj
->o_get_call_info(*this, prehash
);
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);
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();
1585 ObjectData
*obj
= FrameInjection::GetThis();
1586 if (!obj
|| !obj
->o_instanceof(self
)) {
1587 return get_call_info_static_method(*this);
1591 return obj
->o_get_call_info_ex(self
, *this, prehash
);
1595 void MethodCallPackage::functionNamedCall(CVarRef func
) {
1597 get_call_info(ci
, extra
, func
);
1600 void MethodCallPackage::functionNamedCall(CStrRef func
) {
1602 get_call_info(ci
, extra
, func
.data());
1605 void MethodCallPackage::functionNamedCall(ObjectData
*func
) {
1607 ci
= func
->t___invokeCallInfoHelper(extra
);
1610 void MethodCallPackage::construct(CObjRef self
) {
1611 rootObj
= self
.get();
1612 self
->getConstructor(*this);
1615 void MethodCallPackage::fail() {
1617 o_invoke_failed(isObj
? rootObj
->o_getClassName() : String(rootCls
),
1621 String
MethodCallPackage::getClassName() {
1623 return rootObj
->o_getClassName();
1628 void MethodCallPackage::lateStaticBind(ThreadInfo
*ti
) {
1629 rootCls
= FrameInjection::GetStaticClassName(ti
).get();
1630 get_call_info_static_method(*this);
1634 const CallInfo
*MethodCallPackage::bindClass(FrameInjection
&fi
) {
1635 if (ci
->m_flags
& CallInfo::StaticMethod
) {
1636 fi
.setStaticClassName(obj
->getRoot()->o_getClassName());
1641 ///////////////////////////////////////////////////////////////////////////////
1642 // debugger and code coverage instrumentation
1644 void throw_exception(CObjRef e
) {
1645 if (!Eval::Debugger::InterruptException(e
)) return;
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
;
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()) {
1662 if (RuntimeOption::RecordCodeCoverage
) {
1663 Eval::CodeCoverage::Record(frame
->getFileName().data(), line0
, line1
);
1669 ///////////////////////////////////////////////////////////////////////////////