2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 "hphp/runtime/base/builtin-functions.h"
19 #include "hphp/runtime/base/backtrace.h"
20 #include "hphp/runtime/base/code-coverage.h"
21 #include "hphp/runtime/base/container-functions.h"
22 #include "hphp/runtime/base/execution-context.h"
23 #include "hphp/runtime/base/file-util.h"
24 #include "hphp/runtime/base/request-injection-data.h"
25 #include "hphp/runtime/base/runtime-option.h"
26 #include "hphp/runtime/base/unit-cache.h"
27 #include "hphp/runtime/base/variable-serializer.h"
28 #include "hphp/runtime/base/variable-unserializer.h"
30 #include "hphp/runtime/debugger/debugger.h"
32 #include "hphp/runtime/ext/std/ext_std_closure.h"
33 #include "hphp/runtime/ext/std/ext_std_function.h"
34 #include "hphp/runtime/ext/string/ext_string.h"
36 #include "hphp/runtime/vm/event-hook.h"
37 #include "hphp/runtime/vm/func.h"
38 #include "hphp/runtime/vm/jit/translator-inline.h"
39 #include "hphp/runtime/vm/jit/translator.h"
40 #include "hphp/runtime/vm/method-lookup.h"
41 #include "hphp/runtime/vm/repo.h"
42 #include "hphp/runtime/vm/reified-generics.h"
43 #include "hphp/runtime/vm/type-constraint.h"
44 #include "hphp/runtime/vm/unit-util.h"
45 #include "hphp/runtime/vm/unit.h"
47 #include "hphp/system/systemlib.h"
49 #include "hphp/util/logger.h"
50 #include "hphp/util/process.h"
51 #include "hphp/util/string-vsnprintf.h"
52 #include "hphp/util/text-util.h"
54 #include <folly/Format.h>
62 ///////////////////////////////////////////////////////////////////////////////
66 s_offsetExists("offsetExists"),
67 s___invoke("__invoke"),
72 const StaticString
s_cmpWithCollection(
73 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
74 "a collection with an integer, double, string, array, or object"
76 const StaticString
s_cmpWithVec(
77 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
78 "a vec with a non-vec"
80 const StaticString
s_cmpWithDict(
81 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
84 const StaticString
s_cmpWithKeyset(
85 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
88 const StaticString
s_cmpWithRecord(
89 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
92 const StaticString
s_cmpWithClsMeth(
93 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
94 "a clsmeth with a non-clsmeth"
96 const StaticString
s_cmpWithNonRecord(
97 "Cannot compare records with non-records"
100 ///////////////////////////////////////////////////////////////////////////////
102 bool array_is_valid_callback(const Array
& arr
) {
103 if (!arr
.isPHPArray() && !arr
.isVecArray() && !arr
.isDict()) return false;
104 if (arr
.size() != 2 || !arr
.exists(int64_t(0)) || !arr
.exists(int64_t(1))) {
107 auto const elem0
= arr
.rval(0);
108 if (!isStringType(elem0
.type()) && !isObjectType(elem0
.type()) &&
109 !isClassType(elem0
.type())) {
112 auto const elem1
= arr
.rval(1);
113 if (!isStringType(elem1
.type()) && !isFuncType(elem1
.type())) {
120 s__invoke("__invoke"),
121 s_Closure__invoke("Closure::__invoke"),
124 bool is_callable(const Variant
& v
) {
126 ctx
.invName
= nullptr;
127 vm_decode_function(v
, ctx
, DecodeFlags::LookupOnly
);
128 if (ctx
.invName
!= nullptr) {
129 decRefStr(ctx
.invName
);
131 return ctx
.func
!= nullptr && !ctx
.func
->isAbstract();
134 bool is_callable(const Variant
& v
, bool syntax_only
, Variant
* name
) {
136 if (LIKELY(!syntax_only
)) {
137 ret
= is_callable(v
);
138 if (LIKELY(!name
)) return ret
;
141 auto const tv_func
= v
.asTypedValue();
142 if (isFuncType(tv_func
->m_type
)) {
143 auto func_name
= tv_func
->m_data
.pfunc
->fullName();
144 if (name
) *name
= Variant
{func_name
, Variant::PersistentStrInit
{}};
148 if (isClsMethType(tv_func
->m_type
)) {
149 auto const clsmeth
= tv_func
->m_data
.pclsmeth
;
152 clsmeth
->getCls()->nameStr(), "::", clsmeth
->getFunc()->nameStr());
157 if (isStringType(tv_func
->m_type
)) {
162 if (isArrayType(tv_func
->m_type
) ||
163 isVecType(tv_func
->m_type
) ||
164 isDictType(tv_func
->m_type
)) {
165 auto const arr
= Array(tv_func
->m_data
.parr
);
166 auto const clsname
= arr
.rval(int64_t(0));
167 auto const mthname
= arr
.rval(int64_t(1));
169 if (arr
.size() != 2 ||
170 clsname
.is_dummy() ||
171 (!isStringType(mthname
.type()) && !isFuncType(mthname
.type()))) {
172 if (name
) *name
= array_string
;
176 StringData
* clsString
= nullptr;
177 if (isObjectType(clsname
.type())) {
178 clsString
= clsname
.val().pobj
->getClassName().get();
179 } else if (isStringType(clsname
.type())) {
180 clsString
= clsname
.val().pstr
;
181 } else if (isClassType(clsname
.type())) {
182 clsString
= const_cast<StringData
*>(clsname
.val().pclass
->name());
184 if (name
) *name
= array_string
;
188 if (isFuncType(mthname
.type())) {
190 *name
= Variant
{mthname
.val().pfunc
->fullName(),
191 Variant::PersistentStrInit
{}};
197 *name
= concat3(String
{clsString
},
199 String
{mthname
.val().pstr
});
204 if (tv_func
->m_type
== KindOfObject
) {
205 ObjectData
*d
= tv_func
->m_data
.pobj
;
206 const Func
* invoke
= d
->getVMClass()->lookupMethod(s__invoke
.get());
208 if (d
->instanceof(c_Closure::classof())) {
209 // Hack to stop the mangled name from showing up
210 *name
= s_Closure__invoke
;
212 *name
= d
->getClassName().asString() + "::__invoke";
215 return invoke
!= nullptr;
222 Class
* vm_decode_class_from_name(
223 const String
& clsName
,
224 const String
& funcName
,
225 bool nameContainsClass
,
230 Class
* cls
= nullptr;
231 if (clsName
.get()->isame(s_self
.get())) {
235 if (!nameContainsClass
) {
238 } else if (clsName
.get()->isame(s_parent
.get())) {
239 if (ctx
&& ctx
->parent()) {
242 if (!nameContainsClass
) {
245 } else if (clsName
.get()->isame(s_static
.get())) {
246 if (ar
&& ar
->func()->cls()) {
248 cls
= ar
->getThis()->getVMClass();
250 cls
= ar
->getClass();
252 if (flags
!= DecodeFlags::NoWarn
&& cls
) {
253 if (RuntimeOption::EvalWarnOnSkipFrameLookup
) {
255 "vm_decode_function() used to decode a LSB class "
263 if (flags
== DecodeFlags::Warn
&& nameContainsClass
) {
264 String nameClass
= funcName
.substr(0, funcName
.find("::"));
265 if (nameClass
.get()->isame(s_self
.get()) ||
266 nameClass
.get()->isame(s_static
.get())) {
267 raise_warning("behavior of call_user_func(array('%s', '%s')) "
268 "is undefined", clsName
.data(), funcName
.data());
271 cls
= Unit::loadClass(clsName
.get());
276 const Func
* vm_decode_func_from_name(
277 const String
& funcName
,
284 StringData
*& invName
,
286 CallType lookupType
= this_
? CallType::ObjMethod
: CallType::ClsMethod
;
287 auto f
= lookupMethodCtx(cc
, funcName
.get(), ctx
, lookupType
);
288 if (f
&& (f
->attrs() & AttrStatic
)) {
289 // If we found a method and its static, null out this_
292 if (!this_
&& ar
&& ar
->func()->cls() && ar
->hasThis()) {
293 // If we did not find a static method AND this_ is null AND there is a
294 // frame ar, check if the current instance from ar is compatible
295 auto const obj
= ar
->getThis();
296 if (obj
->instanceof(cls
)) {
298 cls
= obj
->getVMClass();
300 if (flags
!= DecodeFlags::NoWarn
&& this_
) {
301 if (RuntimeOption::EvalWarnOnSkipFrameLookup
) {
303 "vm_decode_function() used to decode a method on $this, an "
304 "instance of %s, from the caller, %s",
306 ar
->func()->fullName()->data()
313 // If this_ is non-null AND we could not find a method, try
314 // looking up __call in cls's method table
315 f
= cls
->lookupMethod(s___call
.get());
316 assertx(!f
|| !(f
->attrs() & AttrStatic
));
318 if (!f
&& lookupType
== CallType::ClsMethod
) {
321 if (f
&& (cc
== cls
|| cc
->lookupMethod(f
->name()))) {
323 // Stash the original name into invName.
324 invName
= funcName
.get();
325 invName
->incRefCount();
327 // Bail out if we couldn't find the method or __call
328 if (flags
== DecodeFlags::Warn
) {
329 raise_invalid_argument_warning("function: method '%s' not found",
337 if (!this_
&& !f
->isStaticInPrologue()) {
338 if (flags
!= DecodeFlags::LookupOnly
) throw_missing_this(f
);
341 assertx(f
&& f
->preClass());
342 // If this_ is non-NULL, then this_ is the current instance and cls is
343 // the class of the current instance.
344 assertx(!this_
|| this_
->getVMClass() == cls
);
345 // If we are doing a forwarding call and this_ is null, set cls
346 // appropriately to propagate the current late bound class.
347 if (!this_
&& forwarding
&& ar
&& ar
->func()->cls()) {
348 auto const fwdCls
= ar
->hasThis() ?
349 ar
->getThis()->getVMClass() : ar
->getClass();
351 // Only forward the current late bound class if it is the same or
352 // a descendent of cls
353 if (fwdCls
->classof(cls
)) {
357 if (flags
!= DecodeFlags::NoWarn
&& fwdCls
) {
358 if (RuntimeOption::EvalWarnOnSkipFrameLookup
) {
360 "vm_decode_function() forwarded the calling context, %s",
361 fwdCls
->name()->data()
367 if (flags
!= DecodeFlags::NoWarn
&& !f
->isPublic()) {
368 if (RuntimeOption::EvalWarnOnSkipFrameLookup
) {
370 "vm_decode_function() used to decode a %s method: %s",
371 f
->attrs() & AttrPrivate
? "private" : "protected",
372 f
->fullName()->data()
380 std::pair
<Class
*, Func
*> decode_for_clsmeth(
381 const String
& clsName
,
382 const String
& funcName
,
384 StringData
*& invName
,
385 DecodeFlags flags
/* = DecodeFlags::Warn */) {
386 int pos
= funcName
.find("::");
387 bool nameContainsClass
=
388 (pos
!= 0 && pos
!= String::npos
&& pos
+ 2 < funcName
.size());
389 bool forwarding
= false;
390 HPHP::Class
* ctx
= nullptr;
391 if (ar
) ctx
= arGetContextClass(ar
);
392 auto cls
= vm_decode_class_from_name(
393 clsName
, funcName
, nameContainsClass
, ar
, forwarding
, ctx
, flags
);
395 if (flags
== DecodeFlags::Warn
) {
396 raise_invalid_argument_warning("function: class not found");
398 return std::make_pair(nullptr, nullptr);
401 // For clsmeth, we want to return the class user gave,
402 // not the class where func is associated with
404 ObjectData
* thiz
= nullptr;
405 auto const func
= vm_decode_func_from_name(
406 funcName
, ar
, forwarding
, thiz
, c
, ctx
, c
, invName
, flags
);
407 return std::make_pair(cls
, const_cast<Func
*>(func
));
411 vm_decode_function(const_variant_ref function
,
415 StringData
*& invName
,
417 DecodeFlags flags
/* = DecodeFlags::Warn */,
418 bool genericsAlreadyGiven
/* = false */) {
419 bool forwarding
= false;
423 if (function
.isFunc()) {
427 return function
.toFuncVal();
430 if (function
.isClsMeth()) {
433 auto const clsmeth
= function
.toClsMethVal();
434 cls
= clsmeth
->getCls();
435 return clsmeth
->getFunc();
438 if (function
.isString() || function
.isArray()) {
439 HPHP::Class
* ctx
= nullptr;
440 if (ar
) ctx
= arGetContextClass(ar
);
441 // Decode the 'function' parameter into this_, cls, name, pos, and
442 // nameContainsClass.
446 int pos
= String::npos
;
447 bool nameContainsClass
= false;
448 if (function
.isString()) {
449 // If 'function' is a string we simply assign it to name and
450 // leave this_ and cls set to NULL.
451 name
= function
.toString();
452 pos
= name
.find("::");
454 (pos
!= 0 && pos
!= String::npos
&& pos
+ 2 < name
.size());
456 // If 'function' is an array with exactly two indices 0 and 1, we
457 // assign the value at index 1 to name and we use the value at index
458 // 0 to populate cls (if the value is a string) or this_ and cls (if
459 // the value is an object).
460 assertx(function
.isArray());
461 Array arr
= function
.toArray();
462 if (!array_is_valid_callback(arr
)) {
463 if (flags
== DecodeFlags::Warn
) {
464 raise_invalid_argument_warning("function: not a valid callback array");
469 Variant elem0
= arr
[0];
470 Variant elem1
= arr
[1];
471 if (elem1
.isFunc()) {
472 if (elem0
.isObject()) {
473 this_
= elem0
.getObjectData();
474 cls
= this_
->getVMClass();
475 } else if (elem0
.isClass()) {
476 cls
= elem0
.toClassVal();
478 raise_error("calling an ill-formed array without resolved "
479 "object/class pointer");
482 return elem1
.toFuncVal();
485 assertx(elem1
.isString());
486 name
= elem1
.toString();
487 pos
= name
.find("::");
489 (pos
!= 0 && pos
!= String::npos
&& pos
+ 2 < name
.size());
490 if (elem0
.isString()) {
491 cls
= vm_decode_class_from_name(
492 elem0
.toString(), name
, nameContainsClass
, ar
, forwarding
, ctx
,
495 if (flags
== DecodeFlags::Warn
) {
496 raise_invalid_argument_warning("function: class not found");
500 } else if (elem0
.isClass()) {
501 cls
= elem0
.toClassVal();
503 assertx(elem0
.isObject());
504 this_
= elem0
.getObjectData();
505 cls
= this_
->getVMClass();
509 HPHP::Class
* cc
= cls
;
510 if (nameContainsClass
) {
511 String c
= name
.substr(0, pos
);
512 name
= name
.substr(pos
+ 2);
513 if (c
.get()->isame(s_self
.get())) {
522 } else if (c
.get()->isame(s_parent
.get())) {
525 } else if (ctx
&& ctx
->parent()) {
531 } else if (c
.get()->isame(s_static
.get())) {
532 if (ar
&& ar
->func()->cls()) {
534 cc
= ar
->getThis()->getVMClass();
539 if (flags
!= DecodeFlags::NoWarn
&& cc
) {
540 if (RuntimeOption::EvalWarnOnSkipFrameLookup
) {
542 "vm_decode_function() used to decode a LSB class "
549 cc
= Unit::loadClass(c
.get());
552 if (flags
== DecodeFlags::Warn
) {
553 raise_invalid_argument_warning("function: class not found");
558 if (!cls
->classof(cc
)) {
559 if (flags
== DecodeFlags::Warn
) {
560 raise_warning("call_user_func expects parameter 1 to be a valid "
561 "callback, class '%s' is not a subclass of '%s'",
562 cls
->preClass()->name()->data(),
563 cc
->preClass()->name()->data());
568 // If there is not a current instance, cc trumps cls.
575 HPHP::Func
* f
= HPHP::Unit::loadFunc(name
.get());
577 if (flags
== DecodeFlags::Warn
) {
578 raise_invalid_argument_warning("function: method '%s' not found",
583 assertx(f
&& f
->preClass() == nullptr);
584 if (f
->hasReifiedGenerics() && !genericsAlreadyGiven
) {
585 raise_invalid_argument_warning("You may not call the reified function '%s' "
586 "without reified arguments",
587 f
->fullName()->data());
593 return vm_decode_func_from_name(
594 name
, ar
, forwarding
, this_
, cls
, ctx
, cc
, invName
, flags
);
596 if (function
.isObject()) {
597 this_
= function
.asCObjRef().get();
600 const HPHP::Func
*f
= this_
->getVMClass()->lookupMethod(s___invoke
.get());
601 if (f
!= nullptr && f
->isStaticInPrologue()) {
602 // If __invoke is static, invoke it as such
603 cls
= this_
->getVMClass();
606 if (flags
== DecodeFlags::Warn
&& f
== nullptr) {
607 raise_warning("call_user_func() expects parameter 1 to be a valid "
608 "callback, object of class %s given (no __invoke "
609 "method found)", this_
->getVMClass()->name()->data());
613 if (flags
== DecodeFlags::Warn
) {
614 raise_invalid_argument_warning("function: not string, closure, or array");
619 Variant
vm_call_user_func(const_variant_ref function
, const Variant
& params
,
620 bool checkRef
/* = false */,
621 bool allowDynCallNoPointer
/* = false */) {
623 vm_decode_function(function
, ctx
);
624 if (ctx
.func
== nullptr || (!isContainer(params
) && !params
.isNull())) {
625 return uninit_null();
627 auto ret
= Variant::attach(
628 g_context
->invokeFunc(ctx
.func
, params
, ctx
.this_
, ctx
.cls
,
629 ctx
.invName
, ctx
.dynamic
, checkRef
,
630 allowDynCallNoPointer
)
636 invoke(const String
& function
, const Variant
& params
,
637 bool allowDynCallNoPointer
/* = false */) {
638 Func
* func
= Unit::loadFunc(function
.get());
639 if (func
&& (isContainer(params
) || params
.isNull())) {
640 auto ret
= Variant::attach(
641 g_context
->invokeFunc(func
, params
, nullptr, nullptr, nullptr, true,
642 false, allowDynCallNoPointer
)
647 throw ExtendedException("(1) call the function without enough arguments OR "
648 "(2) Unable to find function \"%s\" OR "
649 "(3) function was not in invoke table OR "
650 "(4) function was renamed to something else.",
654 Variant
invoke_static_method(const String
& s
, const String
& method
,
655 const Variant
& params
, bool fatal
/* = true */) {
656 HPHP::Class
* class_
= Unit::lookupClass(s
.get());
657 if (class_
== nullptr) {
658 o_invoke_failed(s
.data(), method
.data(), fatal
);
659 return uninit_null();
661 const HPHP::Func
* f
= class_
->lookupMethod(method
.get());
662 if (f
== nullptr || !f
->isStaticInPrologue() ||
663 (!isContainer(params
) && !params
.isNull())) {
664 o_invoke_failed(s
.data(), method
.data(), fatal
);
665 return uninit_null();
667 auto ret
= Variant::attach(
668 g_context
->invokeFunc(f
, params
, nullptr, class_
)
673 Variant
o_invoke_failed(const char *cls
, const char *meth
,
674 bool fatal
/* = true */) {
676 string msg
= "Unknown method ";
680 raise_fatal_error(msg
.c_str());
682 raise_warning("call_user_func to non-existent method %s::%s", cls
, meth
);
687 void throw_has_this_need_static(const Func
* f
) {
688 auto const msg
= folly::sformat(
689 "Static method {}() cannot be called on instance",
690 f
->fullName()->data()
692 SystemLib::throwBadMethodCallExceptionObject(msg
);
695 void throw_missing_this(const Func
* f
) {
696 auto const msg
= folly::sformat(
697 "Non-static method {}() cannot be called statically",
698 f
->fullName()->data()
700 SystemLib::throwBadMethodCallExceptionObject(msg
);
703 void NEVER_INLINE
throw_invalid_property_name(const String
& name
) {
705 raise_error("Cannot access empty property");
707 raise_error("Cannot access property started with '\\0'");
710 void throw_instance_method_fatal(const char *name
) {
711 raise_error("Non-static method %s() cannot be called statically", name
);
714 void throw_call_reified_func_without_generics(const Func
* f
) {
715 auto const msg
= folly::sformat(
716 "Cannot call the reified function '{}' without the reified generics",
717 f
->fullName()->data()
719 SystemLib::throwBadMethodCallExceptionObject(msg
);
722 void throw_iterator_not_valid() {
723 SystemLib::throwInvalidOperationExceptionObject(
724 "Iterator is not valid");
727 void throw_collection_property_exception() {
728 SystemLib::throwInvalidOperationExceptionObject(
729 "Cannot access a property on a collection");
732 void throw_invalid_collection_parameter() {
733 SystemLib::throwInvalidArgumentExceptionObject(
734 "Parameter must be an array or an instance of Traversable");
737 void throw_invalid_operation_exception(StringData
* str
) {
738 SystemLib::throwInvalidOperationExceptionObject(Variant
{str
});
741 void throw_division_by_zero_exception() {
742 SystemLib::throwDivisionByZeroExceptionObject();
745 void throw_collection_compare_exception() {
746 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithCollection
);
749 void throw_vec_compare_exception() {
750 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithVec
);
753 void throw_dict_compare_exception() {
754 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithDict
);
757 void throw_record_compare_exception() {
758 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithRecord
);
761 void throw_rec_non_rec_compare_exception() {
762 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithNonRecord
);
765 void throw_keyset_compare_exception() {
766 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithKeyset
);
769 void throw_clsmeth_compare_exception() {
770 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithClsMeth
);
773 void throw_param_is_not_container() {
774 static const string
msg("Parameter must be an array or collection");
775 SystemLib::throwInvalidArgumentExceptionObject(msg
);
778 void throw_invalid_inout_base() {
779 SystemLib::throwInvalidArgumentExceptionObject(
780 "Parameters marked inout must be contained in locals, vecs, dicts, "
781 "keysets, and arrays"
785 void throw_cannot_modify_immutable_object(const char* className
) {
786 auto msg
= folly::sformat(
787 "Cannot modify immutable object of type {}",
790 SystemLib::throwInvalidOperationExceptionObject(msg
);
793 void throw_cannot_modify_const_object(const char* className
) {
794 auto msg
= folly::sformat(
795 "Cannot modify const object of type {}",
798 SystemLib::throwInvalidOperationExceptionObject(msg
);
801 void throw_object_forbids_dynamic_props(const char* className
) {
802 auto msg
= folly::sformat(
803 "Class {} does not allow use of dynamic (non-declared) properties",
806 SystemLib::throwInvalidOperationExceptionObject(msg
);
809 void throw_cannot_modify_const_prop(const char* className
,
810 const char* propName
)
812 auto msg
= folly::sformat(
813 "Cannot modify const property {} of class {} after construction",
816 SystemLib::throwInvalidOperationExceptionObject(msg
);
819 void throw_cannot_modify_static_const_prop(const char* className
,
820 const char* propName
)
822 auto msg
= folly::sformat(
823 "Cannot modify static const property {} of class {}.",
826 SystemLib::throwInvalidOperationExceptionObject(msg
);
830 void throw_late_init_prop(const Class
* cls
,
831 const StringData
* propName
,
833 SystemLib::throwInvalidOperationExceptionObject(
835 "Accessing <<__LateInit>> {} '{}::{}' before initialization",
836 isSProp
? "static property" : "property",
844 void throw_parameter_wrong_type(TypedValue tv
,
846 unsigned int arg_num
,
847 DataType expected_type
) {
848 auto msg
= param_type_error_message(
849 callee
->name()->data(), arg_num
, expected_type
,
852 if (RuntimeOption::PHP7_EngineExceptions
) {
853 SystemLib::throwTypeErrorObject(msg
);
855 SystemLib::throwRuntimeExceptionObject(msg
);
858 void check_collection_cast_to_array() {
859 if (RuntimeOption::WarnOnCollectionToArray
) {
860 raise_warning("Casting a collection to an array is an expensive operation "
861 "and should be avoided where possible. To convert a "
862 "collection to an array without raising a warning, use the "
863 "toArray() method.");
867 Object
create_object_only(const String
& s
) {
868 return Object::attach(g_context
->createObjectOnly(s
.get()));
871 Object
init_object(const String
& s
, const Array
& params
, ObjectData
* o
) {
872 return Object
{g_context
->initObject(s
.get(), params
, o
)};
876 create_object(const String
& s
, const Array
& params
, bool init
/* = true */) {
877 return Object::attach(g_context
->createObject(s
.get(), params
, init
));
880 void throw_object(const Object
& e
) {
881 throw req::root
<Object
>(e
);
884 #if ((__GNUC__ != 4) || (__GNUC_MINOR__ != 8))
885 void throw_object(Object
&& e
) {
886 throw req::root
<Object
>(std::move(e
));
891 * This function is used when another thread is segfaulting---we just
892 * want to wait forever to give it a chance to write a stacktrace file
893 * (and maybe a core file).
895 void pause_forever() {
899 bool is_constructor_name(const char* fn
) {
900 auto len
= strlen(fn
);
901 const char construct
[] = "__construct";
902 auto clen
= sizeof(construct
) - 1;
904 if (len
>= clen
&& !strcasecmp(fn
+ len
- clen
, construct
)) {
905 if (len
== clen
|| (len
> clen
+ 2 &&
906 fn
[len
- clen
- 1] == ':' &&
907 fn
[len
- clen
- 2] == ':')) {
914 void throw_missing_arguments_nr(const char *fn
, int expected
, int got
) {
915 SystemLib::throwRuntimeExceptionObject(folly::sformat(
916 "{}() expects exactly {} parameter{}, {} given",
919 expected
== 1 ? "" : "s",
924 void raise_bad_type_warning(const char *fmt
, ...) {
928 string_vsnprintf(msg
, fmt
, ap
);
931 raise_warning("Invalid operand type was used: %s", msg
.c_str());
934 void raise_expected_array_warning(const char* fn
/*=nullptr*/) {
936 if (auto ar
= g_context
->getStackFrame()) {
937 fn
= ar
->m_func
->name()->data();
942 raise_bad_type_warning("%s expects array(s)", fn
);
945 void raise_expected_array_or_collection_warning(const char* fn
/*=nullptr*/) {
947 if (auto ar
= g_context
->getStackFrame()) {
948 fn
= ar
->m_func
->name()->data();
953 raise_bad_type_warning("%s expects array(s) or collection(s)", fn
);
956 void raise_invalid_argument_warning(const char *fmt
, ...) {
960 string_vsnprintf(msg
, fmt
, ap
);
962 raise_warning("Invalid argument: %s", msg
.c_str());
965 Variant
throw_fatal_unset_static_property(const char *s
, const char *prop
) {
966 raise_error("Attempt to unset static property %s::$%s", s
, prop
);
967 return uninit_null();
970 Variant
unserialize_ex(const char* str
, int len
,
971 VariableUnserializer::Type type
,
972 const Array
& options
/* = null_array */) {
973 if (str
== nullptr || len
<= 0) {
977 VariableUnserializer
vu(str
, len
, type
, true, options
);
980 v
= vu
.unserialize();
981 } catch (FatalErrorException
& e
) {
983 } catch (InvalidAllowedClassesException
& e
) {
985 "unserialize(): allowed_classes option should be array or boolean"
988 } catch (Exception
& e
) {
989 raise_notice("Unable to unserialize: [%.*s]. %s.",
990 std::min(len
, 1000), str
, e
.getMessage().c_str());
996 Variant
unserialize_ex(const String
& str
,
997 VariableUnserializer::Type type
,
998 const Array
& options
/* = null_array */) {
999 return unserialize_ex(str
.data(), str
.size(), type
, options
);
1002 String
concat3(const String
& s1
, const String
& s2
, const String
& s3
) {
1003 auto r1
= s1
.slice();
1004 auto r2
= s2
.slice();
1005 auto r3
= s3
.slice();
1006 auto len
= r1
.size() + r2
.size() + r3
.size();
1007 auto str
= String::attach(StringData::Make(len
));
1008 auto const r
= str
.mutableData();
1009 memcpy(r
, r1
.data(), r1
.size());
1010 memcpy(r
+ r1
.size(), r2
.data(), r2
.size());
1011 memcpy(r
+ r1
.size() + r2
.size(), r3
.data(), r3
.size());
1016 String
concat4(const String
& s1
, const String
& s2
, const String
& s3
,
1018 auto r1
= s1
.slice();
1019 auto r2
= s2
.slice();
1020 auto r3
= s3
.slice();
1021 auto r4
= s4
.slice();
1022 auto len
= r1
.size() + r2
.size() + r3
.size() + r4
.size();
1023 auto str
= String::attach(StringData::Make(len
));
1024 auto const r
= str
.mutableData();
1025 memcpy(r
, r1
.data(), r1
.size());
1026 memcpy(r
+ r1
.size(), r2
.data(), r2
.size());
1027 memcpy(r
+ r1
.size() + r2
.size(), r3
.data(), r3
.size());
1028 memcpy(r
+ r1
.size() + r2
.size() + r3
.size(), r4
.data(), r4
.size());
1033 static bool invoke_file_impl(Variant
& res
, const String
& path
, bool once
,
1034 const char *currentDir
,
1035 bool callByHPHPInvoke
) {
1037 auto const u
= lookupUnit(path
.get(), currentDir
, &initial
,
1038 Native::s_noNativeFuncs
, false);
1039 if (u
== nullptr) return false;
1040 if (!once
|| initial
) {
1041 *res
.asTypedValue() = g_context
->invokeUnit(u
, callByHPHPInvoke
);
1046 static NEVER_INLINE Variant
throw_missing_file(const char* file
) {
1047 throw PhpFileDoesNotExistException(file
);
1050 static Variant
invoke_file(const String
& s
,
1052 const char *currentDir
,
1053 bool callByHPHPInvoke
) {
1055 if (invoke_file_impl(r
, s
, once
, currentDir
, callByHPHPInvoke
)) {
1058 return throw_missing_file(s
.c_str());
1061 Variant
include_impl_invoke(const String
& file
, bool once
,
1062 const char *currentDir
, bool callByHPHPInvoke
) {
1063 if (FileUtil::isAbsolutePath(file
.toCppString())) {
1064 if (RuntimeOption::SandboxMode
|| !RuntimeOption::AlwaysUseRelativePath
) {
1066 return invoke_file(file
, once
, currentDir
, callByHPHPInvoke
);
1067 } catch(PhpFileDoesNotExistException
& e
) {}
1071 String
rel_path(FileUtil::relativePath(RuntimeOption::SourceRoot
,
1072 string(file
.data())));
1074 // Don't try/catch - We want the exception to be passed along
1075 return invoke_file(rel_path
, once
, currentDir
, callByHPHPInvoke
);
1076 } catch(PhpFileDoesNotExistException
& e
) {
1077 throw PhpFileDoesNotExistException(file
.c_str());
1080 // Don't try/catch - We want the exception to be passed along
1081 return invoke_file(file
, once
, currentDir
, callByHPHPInvoke
);
1086 * Used by include_impl. resolve_include() needs some way of checking the
1087 * existence of a file path, which for hphpc means attempting to invoke it.
1088 * This struct carries some context information needed for the invocation, as
1089 * well as a place for the return value of invoking the file.
1091 struct IncludeImplInvokeContext
{
1093 const char* currentDir
;
1095 Variant returnValue
;
1098 static bool include_impl_invoke_context(const String
& file
, void* ctx
) {
1099 struct IncludeImplInvokeContext
* context
= (IncludeImplInvokeContext
*)ctx
;
1100 bool invoked_file
= false;
1102 context
->returnValue
= include_impl_invoke(file
, context
->once
,
1103 context
->currentDir
);
1104 invoked_file
= true;
1105 } catch (PhpFileDoesNotExistException
& e
) {
1106 context
->returnValue
= false;
1108 return invoked_file
;
1112 * tryFile is a pointer to a function that resolve_include() will use to
1113 * determine if a path references a real file. ctx is a pointer to some context
1114 * information that will be passed through to tryFile. (It's a hacky closure)
1116 String
resolve_include(const String
& file
, const char* currentDir
,
1117 bool (*tryFile
)(const String
& file
, void*), void* ctx
) {
1118 const char* c_file
= file
.data();
1120 auto const getCwd
= [] () -> String
{
1121 if (LIKELY(!g_context
.isNull())) return g_context
->getCwd();
1122 return String(Process::CurrentWorkingDirectory
, CopyString
);
1125 if (!File::IsPlainFilePath(file
)) {
1126 // URIs don't have an include path
1127 if (tryFile(file
, ctx
)) {
1131 } else if (FileUtil::isAbsolutePath(file
.toCppString())) {
1132 String can_path
= FileUtil::canonicalize(file
);
1134 if (tryFile(can_path
, ctx
)) {
1138 } else if ((c_file
[0] == '.' && (c_file
[1] == '/' || (
1139 c_file
[1] == '.' && c_file
[2] == '/')))) {
1141 String
path(String(getCwd() + "/" + file
));
1142 String can_path
= FileUtil::canonicalize(path
);
1144 if (tryFile(can_path
, ctx
)) {
1149 if (!RequestInfo::s_requestInfo
.isNull()) {
1150 auto const& includePaths
= RID().getIncludePaths();
1152 for (auto const& includePath
: includePaths
) {
1154 auto const is_stream_wrapper
=
1155 includePath
.find("://") != std::string::npos
;
1157 if (!is_stream_wrapper
&& !FileUtil::isAbsolutePath(includePath
)) {
1158 path
+= (getCwd() + "/");
1161 path
+= includePath
;
1163 if (path
[path
.size() - 1] != '/') {
1170 if (!is_stream_wrapper
) {
1171 can_path
= FileUtil::canonicalize(path
);
1173 can_path
= String(path
.c_str());
1176 if (tryFile(can_path
, ctx
)) {
1182 if (FileUtil::isAbsolutePath(currentDir
)) {
1183 String
path(currentDir
);
1186 String can_path
= FileUtil::canonicalize(path
);
1188 if (tryFile(can_path
, ctx
)) {
1192 String
path(getCwd() + "/" + currentDir
+ file
);
1193 String can_path
= FileUtil::canonicalize(path
);
1195 if (tryFile(can_path
, ctx
)) {
1204 static Variant
include_impl(const String
& file
, bool once
,
1205 const char *currentDir
, bool required
,
1207 struct IncludeImplInvokeContext ctx
= {once
, currentDir
};
1208 String can_path
= resolve_include(file
, currentDir
,
1209 include_impl_invoke_context
, (void*)&ctx
);
1211 if (can_path
.isNull()) {
1214 raise_notice("Tried to invoke %s but file not found.", file
.data());
1217 String ms
= "Required file that does not exist: ";
1219 raise_fatal_error(ms
.data());
1224 return ctx
.returnValue
;
1227 Variant
require(const String
& file
,
1229 const char* currentDir
,
1231 return include_impl(file
, once
, currentDir
, true, raiseNotice
);
1234 bool function_exists(const String
& function_name
) {
1235 auto f
= Unit::lookupFunc(function_name
.get());
1236 return (f
!= nullptr) &&
1237 (f
->arFuncPtr() != Native::unimplementedWrapper
);
1240 ///////////////////////////////////////////////////////////////////////////////
1241 // debugger and code coverage instrumentation
1243 void throw_exception(const Object
& e
) {
1244 if (!e
.instanceof(SystemLib::s_ThrowableClass
)) {
1245 raise_error("Exceptions must implement the Throwable interface.");
1247 DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionThrownHook(e
.get()));
1248 throw req::root
<Object
>(e
);
1251 ///////////////////////////////////////////////////////////////////////////////