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/opaque-resource.h"
25 #include "hphp/runtime/base/request-injection-data.h"
26 #include "hphp/runtime/base/runtime-option.h"
27 #include "hphp/runtime/base/unit-cache.h"
28 #include "hphp/runtime/base/variable-serializer.h"
29 #include "hphp/runtime/base/variable-unserializer.h"
31 #include "hphp/runtime/debugger/debugger.h"
33 #include "hphp/runtime/ext/std/ext_std_closure.h"
34 #include "hphp/runtime/ext/std/ext_std_function.h"
35 #include "hphp/runtime/ext/string/ext_string.h"
37 #include "hphp/runtime/vm/event-hook.h"
38 #include "hphp/runtime/vm/func.h"
39 #include "hphp/runtime/vm/jit/translator-inline.h"
40 #include "hphp/runtime/vm/jit/translator.h"
41 #include "hphp/runtime/vm/method-lookup.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 ///////////////////////////////////////////////////////////////////////////////
67 s_offsetExists("offsetExists"),
68 s___invoke("__invoke"),
73 const StaticString
s_cmpWithCollection(
74 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
75 "a collection with an integer, double, string, array, or object"
77 const StaticString
s_cmpWithVec(
78 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
79 "a vec with a non-vec"
81 const StaticString
s_cmpWithDict(
82 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
85 const StaticString
s_cmpWithKeyset(
86 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
89 const StaticString
s_cmpWithClsMeth(
90 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
93 const StaticString
s_cmpWithRClsMeth(
94 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
95 "a reified class meth"
97 const StaticString
s_cmpWithRFunc(
98 "Cannot use relational comparison operators (<, <=, >, >=, <=>) with "
101 const StaticString
s_cmpWithOpaqueResource(
102 "Cannot use relational comparison operators (<, <=, >, >=, <=>) with "
105 const StaticString
s_cmpWithNonArr(
106 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
107 "a PHP array with a non-array"
109 const StaticString
s_cmpWithFunc(
110 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
114 ///////////////////////////////////////////////////////////////////////////////
116 bool array_is_valid_callback(const Array
& arr
) {
117 if (!arr
.isVec() && !arr
.isDict()) return false;
118 if (arr
.size() != 2 || !arr
.exists(int64_t(0)) || !arr
.exists(int64_t(1))) {
121 auto const elem0
= arr
.lookup(0);
122 if (!isStringType(elem0
.type()) && !isObjectType(elem0
.type()) &&
123 !isClassType(elem0
.type()) && !isLazyClassType(elem0
.type())) {
126 auto const elem1
= arr
.lookup(1);
127 if (!isStringType(elem1
.type()) && !isFuncType(elem1
.type())) {
134 s__invoke("__invoke"),
135 s_Closure__invoke("Closure::__invoke"),
138 bool is_callable(const Variant
& v
) {
140 vm_decode_function(v
, ctx
, DecodeFlags::LookupOnly
);
141 return ctx
.func
!= nullptr && !ctx
.func
->isAbstract();
144 bool is_callable(const Variant
& v
, bool syntax_only
, Variant
* name
) {
146 if (LIKELY(!syntax_only
)) {
147 ret
= is_callable(v
);
148 if (LIKELY(!name
)) return ret
;
151 auto const tv_func
= v
.asTypedValue();
152 if (isFuncType(tv_func
->m_type
)) {
153 auto func_name
= tv_func
->m_data
.pfunc
->fullName();
154 if (name
) *name
= Variant
{func_name
, Variant::PersistentStrInit
{}};
158 if (isClsMethType(tv_func
->m_type
)) {
159 auto const clsmeth
= tv_func
->m_data
.pclsmeth
;
162 clsmeth
->getCls()->nameStr(), "::", clsmeth
->getFunc()->nameStr());
167 if (isStringType(tv_func
->m_type
)) {
172 if (isVecType(tv_func
->m_type
) ||
173 isDictType(tv_func
->m_type
)) {
174 auto const arr
= Array(tv_func
->m_data
.parr
);
175 auto const clsname
= arr
.lookup(int64_t(0));
176 auto const mthname
= arr
.lookup(int64_t(1));
178 if (arr
.size() != 2 || !clsname
.is_init() ||
179 (!isStringType(mthname
.type()) && !isFuncType(mthname
.type()))) {
180 if (name
) *name
= s_Array
;
184 StringData
* clsString
= nullptr;
185 if (isObjectType(clsname
.type())) {
186 clsString
= clsname
.val().pobj
->getClassName().get();
187 } else if (isStringType(clsname
.type())) {
188 clsString
= clsname
.val().pstr
;
189 } else if (isLazyClassType(clsname
.type())) {
190 clsString
= const_cast<StringData
*>(clsname
.val().plazyclass
.name());
191 } else if (isClassType(clsname
.type())) {
192 clsString
= const_cast<StringData
*>(clsname
.val().pclass
->name());
194 if (name
) *name
= s_Array
;
198 if (isFuncType(mthname
.type())) {
200 *name
= Variant
{mthname
.val().pfunc
->fullName(),
201 Variant::PersistentStrInit
{}};
207 *name
= concat3(String
{clsString
},
209 String
{mthname
.val().pstr
});
214 if (tv_func
->m_type
== KindOfObject
) {
215 ObjectData
*d
= tv_func
->m_data
.pobj
;
216 const Func
* invoke
= d
->getVMClass()->lookupMethod(s__invoke
.get());
218 if (d
->instanceof(c_Closure::classof())) {
219 // Hack to stop the mangled name from showing up
220 *name
= s_Closure__invoke
;
222 *name
= d
->getClassName().asString() + "::__invoke";
225 return invoke
!= nullptr;
232 Class
* vm_decode_class_from_name(
233 const String
& clsName
,
234 const String
& funcName
,
235 bool nameContainsClass
,
240 Class
* cls
= nullptr;
241 if (clsName
.get()->isame(s_self
.get())) {
245 if (!nameContainsClass
) {
248 } else if (clsName
.get()->isame(s_parent
.get())) {
249 if (ctx
&& ctx
->parent()) {
252 if (!nameContainsClass
) {
255 } else if (clsName
.get()->isame(s_static
.get())) {
256 if (ar
&& ar
->func()->cls()) {
258 cls
= ar
->getThis()->getVMClass();
260 cls
= ar
->getClass();
262 if (flags
!= DecodeFlags::NoWarn
&& cls
) {
263 if (RuntimeOption::EvalWarnOnSkipFrameLookup
) {
265 "vm_decode_function() used to decode a LSB class "
273 if (flags
== DecodeFlags::Warn
&& nameContainsClass
) {
274 String nameClass
= funcName
.substr(0, funcName
.find("::"));
275 if (nameClass
.get()->isame(s_self
.get()) ||
276 nameClass
.get()->isame(s_static
.get())) {
277 raise_warning("behavior of call_user_func(array('%s', '%s')) "
278 "is undefined", clsName
.data(), funcName
.data());
281 cls
= Class::load(clsName
.get());
286 const Func
* vm_decode_func_from_name(
287 const String
& funcName
,
295 CallType lookupType
= this_
? CallType::ObjMethod
: CallType::ClsMethod
;
296 auto const moduleName
=
297 ar
? ar
->func()->unit()->moduleName() : (const StringData
*)nullptr;
298 auto const callCtx
= MemberLookupContext(ctx
, moduleName
);
299 auto f
= lookupMethodCtx(cc
, funcName
.get(), callCtx
, lookupType
,
300 MethodLookupErrorOptions::NoErrorOnModule
);
301 if (f
&& (f
->attrs() & AttrStatic
)) {
302 // If we found a method and its static, null out this_
305 if (!this_
&& ar
&& ar
->func()->cls() && ar
->hasThis()) {
306 // If we did not find a static method AND this_ is null AND there is a
307 // frame ar, check if the current instance from ar is compatible
308 auto const obj
= ar
->getThis();
309 if (obj
->instanceof(cls
)) {
311 cls
= obj
->getVMClass();
313 if (flags
!= DecodeFlags::NoWarn
&& this_
) {
314 if (RuntimeOption::EvalWarnOnSkipFrameLookup
) {
316 "vm_decode_function() used to decode a method on $this, an "
317 "instance of %s, from the caller, %s",
319 ar
->func()->fullName()->data()
325 if (lookupType
== CallType::ClsMethod
) {
328 // Bail out if we couldn't find the method
329 if (flags
== DecodeFlags::Warn
) {
330 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 bool checkMethCallerTarget(const Func
* meth
, const Class
* ctx
, bool error
) {
381 if (meth
->isStaticInPrologue()) {
382 if (!error
) return false;
383 SystemLib::throwInvalidArgumentExceptionObject(folly::sformat(
384 "meth_caller(): method {} is static",
385 meth
->fullName()->data()
389 if (meth
->isPublic() || ctx
== meth
->cls()) return true;
390 if (ctx
&& (meth
->attrs() & AttrProtected
) && ctx
->classof(meth
->cls())) {
394 // Executing this meth_caller() can still be an error but we won't know until
395 // we know precisely what instance it's being invoked on.
396 // XXX: Do we want this behavior?
397 if (meth
->hasPrivateAncestor() && meth
->cls()->classof(ctx
)) {
398 auto const cmeth
= ctx
->lookupMethod(meth
->name());
399 if (cmeth
&& cmeth
->cls() == ctx
&& (cmeth
->attrs() & AttrPrivate
)) {
405 SystemLib::throwInvalidArgumentExceptionObject(folly::sformat(
406 "meth_caller(): method {} cannot be called from this context",
407 meth
->fullName()->data()
413 void checkMethCaller(const Func
* func
, const Class
* ctx
) {
414 auto const cls
= Class::load(func
->methCallerClsName());
416 SystemLib::throwInvalidArgumentExceptionObject(folly::sformat(
417 "meth_caller(): class {} not found", func
->methCallerClsName()->data()
422 SystemLib::throwInvalidArgumentExceptionObject(folly::sformat(
423 "meth_caller(): class {} is a trait", func
->methCallerClsName()->data()
427 auto const meth
= [&] () -> const Func
* {
428 if (auto const m
= cls
->lookupMethod(func
->methCallerMethName())) return m
;
429 for (auto const i
: cls
->allInterfaces().range()) {
430 if (auto const m
= i
->lookupMethod(func
->methCallerMethName())) return m
;
435 SystemLib::throwInvalidArgumentExceptionObject(folly::sformat(
436 "meth_caller(): method {}::{} not found",
437 func
->methCallerClsName()->data(),
438 func
->methCallerMethName()->data()
442 checkMethCallerTarget(meth
, ctx
, true);
446 vm_decode_function(const_variant_ref function
,
451 DecodeFlags flags
/* = DecodeFlags::Warn */,
452 bool genericsAlreadyGiven
/* = false */) {
453 bool forwarding
= false;
456 if (function
.isFunc()) {
460 return function
.toFuncVal();
463 if (function
.isClsMeth()) {
466 auto const clsmeth
= function
.toClsMethVal();
467 cls
= clsmeth
->getCls();
468 return clsmeth
->getFunc();
471 if (function
.isString() || function
.isArray()) {
472 HPHP::Class
* ctx
= nullptr;
473 if (ar
) ctx
= arGetContextClass(ar
);
474 // Decode the 'function' parameter into this_, cls, name, pos, and
475 // nameContainsClass.
479 int pos
= String::npos
;
480 bool nameContainsClass
= false;
481 if (function
.isString()) {
482 // If 'function' is a string we simply assign it to name and
483 // leave this_ and cls set to NULL.
484 name
= function
.toString();
485 pos
= name
.find("::");
487 (pos
!= 0 && pos
!= String::npos
&& pos
+ 2 < name
.size());
489 // If 'function' is an array with exactly two indices 0 and 1, we
490 // assign the value at index 1 to name and we use the value at index
491 // 0 to populate cls (if the value is a string) or this_ and cls (if
492 // the value is an object).
493 assertx(function
.isArray());
494 Array arr
= function
.toArray();
495 if (!array_is_valid_callback(arr
)) {
496 if (flags
== DecodeFlags::Warn
) {
497 raise_invalid_argument_warning("function: not a valid callback array");
502 Variant elem0
= arr
[0];
503 Variant elem1
= arr
[1];
504 if (elem1
.isFunc()) {
505 if (elem0
.isObject()) {
506 this_
= elem0
.getObjectData();
507 cls
= this_
->getVMClass();
508 } else if (elem0
.isClass()) {
509 cls
= elem0
.toClassVal();
510 } else if (elem0
.isLazyClass()) {
511 cls
= Class::load(elem0
.toLazyClassVal().name());
513 raise_error("calling an ill-formed array without resolved "
514 "object/class pointer");
517 return elem1
.toFuncVal();
520 assertx(elem1
.isString());
521 name
= elem1
.toString();
522 pos
= name
.find("::");
524 (pos
!= 0 && pos
!= String::npos
&& pos
+ 2 < name
.size());
525 if (elem0
.isString() || elem0
.isLazyClass()) {
526 auto const clsName
= elem0
.isString() ?
527 elem0
.toString() : StrNR
{elem0
.toLazyClassVal().name()};
528 cls
= vm_decode_class_from_name(
529 clsName
, name
, nameContainsClass
, ar
, forwarding
, ctx
,
532 if (flags
== DecodeFlags::Warn
) {
533 raise_invalid_argument_warning("function: class not found");
537 } else if (elem0
.isClass()) {
538 cls
= elem0
.toClassVal();
540 assertx(elem0
.isObject());
541 this_
= elem0
.getObjectData();
542 cls
= this_
->getVMClass();
546 HPHP::Class
* cc
= cls
;
547 if (nameContainsClass
) {
548 String c
= name
.substr(0, pos
);
549 name
= name
.substr(pos
+ 2);
550 if (c
.get()->isame(s_self
.get())) {
559 } else if (c
.get()->isame(s_parent
.get())) {
562 } else if (ctx
&& ctx
->parent()) {
568 } else if (c
.get()->isame(s_static
.get())) {
569 if (ar
&& ar
->func()->cls()) {
571 cc
= ar
->getThis()->getVMClass();
576 if (flags
!= DecodeFlags::NoWarn
&& cc
) {
577 if (RuntimeOption::EvalWarnOnSkipFrameLookup
) {
579 "vm_decode_function() used to decode a LSB class "
586 cc
= Class::load(c
.get());
589 if (flags
== DecodeFlags::Warn
) {
590 raise_invalid_argument_warning("function: class not found");
595 if (!cls
->classof(cc
)) {
596 if (flags
== DecodeFlags::Warn
) {
597 raise_warning("call_user_func expects parameter 1 to be a valid "
598 "callback, class '%s' is not a subclass of '%s'",
599 cls
->preClass()->name()->data(),
600 cc
->preClass()->name()->data());
605 // If there is not a current instance, cc trumps cls.
612 HPHP::Func
* f
= HPHP::Func::load(name
.get());
614 if (flags
== DecodeFlags::Warn
) {
615 raise_invalid_argument_warning("function: method '%s' not found",
620 assertx(f
&& f
->preClass() == nullptr);
621 if (f
->hasReifiedGenerics() && !genericsAlreadyGiven
&&
622 !f
->getReifiedGenericsInfo().allGenericsSoft()) {
623 raise_invalid_argument_warning(
624 "You may not call the reified function '%s' "
625 "without reified arguments",
626 f
->fullName()->data());
632 return vm_decode_func_from_name(
633 name
, ar
, forwarding
, this_
, cls
, ctx
, cc
, flags
);
635 if (function
.isObject()) {
636 this_
= function
.asCObjRef().get();
639 const HPHP::Func
*f
= this_
->getVMClass()->lookupMethod(s___invoke
.get());
640 if (f
!= nullptr && f
->isStaticInPrologue()) {
641 // If __invoke is static, invoke it as such
642 cls
= this_
->getVMClass();
645 if (flags
== DecodeFlags::Warn
&& f
== nullptr) {
646 raise_warning("call_user_func() expects parameter 1 to be a valid "
647 "callback, object of class %s given (no __invoke "
648 "method found)", this_
->getVMClass()->name()->data());
652 if (flags
== DecodeFlags::Warn
) {
653 raise_invalid_argument_warning("function: not string, closure, or array");
658 Variant
vm_call_user_func(const_variant_ref function
, const Variant
& params
,
659 RuntimeCoeffects providedCoeffects
660 /* = RuntimeCoeffects::fixme() */,
661 bool checkRef
/* = false */,
662 bool allowDynCallNoPointer
/* = false */) {
664 vm_decode_function(function
, ctx
);
665 if (ctx
.func
== nullptr || (!isContainer(params
) && !params
.isNull())) {
666 return uninit_null();
668 return Variant::attach(
669 g_context
->invokeFunc(ctx
.func
, params
, ctx
.this_
, ctx
.cls
,
670 providedCoeffects
, ctx
.dynamic
,
671 checkRef
, allowDynCallNoPointer
)
676 invoke(const String
& function
, const Variant
& params
,
677 bool allowDynCallNoPointer
/* = false */) {
678 Func
* func
= Func::load(function
.get());
679 if (func
&& (isContainer(params
) || params
.isNull())) {
680 auto ret
= Variant::attach(
681 g_context
->invokeFunc(func
, params
, nullptr, nullptr,
682 RuntimeCoeffects::fixme(), true, false,
683 allowDynCallNoPointer
)
688 throw ExtendedException("(1) call the function without enough arguments OR "
689 "(2) Unable to find function \"%s\" OR "
690 "(3) function was not in invoke table OR "
691 "(4) function was renamed to something else.",
695 Variant
invoke_static_method(const String
& s
, const String
& method
,
696 const Variant
& params
, bool fatal
/* = true */) {
697 HPHP::Class
* class_
= Class::lookup(s
.get());
698 if (class_
== nullptr) {
699 o_invoke_failed(s
.data(), method
.data(), fatal
);
700 return uninit_null();
702 const HPHP::Func
* f
= class_
->lookupMethod(method
.get());
703 if (f
== nullptr || !f
->isStaticInPrologue() ||
704 (!isContainer(params
) && !params
.isNull())) {
705 o_invoke_failed(s
.data(), method
.data(), fatal
);
706 return uninit_null();
708 auto ret
= Variant::attach(
709 g_context
->invokeFunc(f
, params
, nullptr, class_
, RuntimeCoeffects::fixme())
714 Variant
o_invoke_failed(const char *cls
, const char *meth
,
715 bool fatal
/* = true */) {
717 string msg
= "Unknown method ";
721 raise_fatal_error(msg
.c_str());
723 raise_warning("call_user_func to non-existent method %s::%s", cls
, meth
);
728 void throw_has_this_need_static(const Func
* f
) {
729 auto const msg
= folly::sformat(
730 "Static method {}() cannot be called on instance",
731 f
->fullName()->data()
733 SystemLib::throwBadMethodCallExceptionObject(msg
);
736 void throw_missing_this(const Func
* f
) {
737 auto const msg
= folly::sformat(
738 "Non-static method {}() cannot be called statically",
739 f
->fullName()->data()
741 SystemLib::throwBadMethodCallExceptionObject(msg
);
744 void NEVER_INLINE
throw_invalid_property_name(const String
& name
) {
746 raise_error("Cannot access empty property");
748 raise_error("Cannot access property started with '\\0'");
751 void throw_instance_method_fatal(const char *name
) {
752 raise_error("Non-static method %s() cannot be called statically", name
);
755 void throw_call_reified_func_without_generics(const Func
* f
) {
756 auto const msg
= folly::sformat(
757 "Cannot call the reified function '{}' without the reified generics",
758 f
->fullName()->data()
760 SystemLib::throwBadMethodCallExceptionObject(msg
);
763 void throw_implicit_context_exception(std::string s
) {
764 SystemLib::throwInvalidOperationExceptionObject(s
);
767 void raise_implicit_context_warning(std::string s
) {
771 void throw_iterator_not_valid() {
772 SystemLib::throwInvalidOperationExceptionObject(
773 "Iterator is not valid");
776 void throw_collection_property_exception() {
777 SystemLib::throwInvalidOperationExceptionObject(
778 "Cannot access a property on a collection");
781 void throw_invalid_collection_parameter() {
782 SystemLib::throwInvalidArgumentExceptionObject(
783 "Parameter must be an array or an instance of Traversable");
786 void throw_invalid_operation_exception(StringData
* str
) {
787 SystemLib::throwInvalidOperationExceptionObject(Variant
{str
});
790 void throw_division_by_zero_exception() {
791 SystemLib::throwDivisionByZeroExceptionObject();
794 void throw_collection_compare_exception() {
795 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithCollection
);
798 void throw_vec_compare_exception() {
799 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithVec
);
802 void throw_dict_compare_exception() {
803 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithDict
);
806 void throw_rfunc_compare_exception() {
807 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithRFunc
);
810 void throw_opaque_resource_compare_exception() {
811 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithOpaqueResource
);
814 void throw_keyset_compare_exception() {
815 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithKeyset
);
818 void throw_clsmeth_compare_exception() {
819 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithClsMeth
);
822 void throw_rclsmeth_compare_exception() {
823 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithRClsMeth
);
826 void throw_func_compare_exception() {
827 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithFunc
);
830 void throw_param_is_not_container() {
831 static const string
msg("Parameter must be an array or collection");
832 SystemLib::throwInvalidArgumentExceptionObject(msg
);
835 void throw_invalid_inout_base() {
836 SystemLib::throwInvalidArgumentExceptionObject(
837 "Parameters marked inout must be contained in locals, vecs, dicts, "
838 "keysets, and arrays"
842 void throw_cannot_modify_immutable_object(const char* className
) {
843 auto msg
= folly::sformat(
844 "Cannot modify immutable object of type {}",
847 SystemLib::throwInvalidOperationExceptionObject(msg
);
850 void throw_cannot_modify_const_object(const char* className
) {
851 auto msg
= folly::sformat(
852 "Cannot modify const object of type {}",
855 SystemLib::throwInvalidOperationExceptionObject(msg
);
858 void throw_object_forbids_dynamic_props(const char* className
) {
859 auto msg
= folly::sformat(
860 "Class {} does not allow use of dynamic (non-declared) properties",
863 SystemLib::throwInvalidOperationExceptionObject(msg
);
866 void throw_cannot_modify_const_prop(const char* className
,
867 const char* propName
)
869 auto msg
= folly::sformat(
870 "Cannot modify const property {} of class {} after construction",
873 SystemLib::throwInvalidOperationExceptionObject(msg
);
876 void throw_cannot_modify_static_const_prop(const char* className
,
877 const char* propName
)
879 auto msg
= folly::sformat(
880 "Cannot modify static const property {} of class {}.",
883 SystemLib::throwInvalidOperationExceptionObject(msg
);
886 void throw_local_must_be_value_type(const char* locName
)
888 auto const msg
= folly::sformat("Local {} must be a value type.", locName
);
889 SystemLib::throwReadonlyViolationExceptionObject(msg
);
894 void throw_readonly_violation(const char* className
, const char* propName
,
897 string_printf(fmtMsg
, msg
, propName
, className
);
898 SystemLib::throwReadonlyViolationExceptionObject(fmtMsg
);
902 void throw_must_be_readonly(const char* className
, const char* propName
) {
903 throw_readonly_violation(className
, propName
, Strings::MUST_BE_READONLY
);
906 void throw_must_be_mutable(const char* className
, const char* propName
) {
907 throw_readonly_violation(className
, propName
, Strings::MUST_BE_MUTABLE
);
910 void throw_must_be_enclosed_in_readonly(const char* className
, const char* propName
) {
911 throw_readonly_violation(className
, propName
, Strings::MUST_BE_ENCLOSED_IN_READONLY
);
914 void throw_must_be_value_type(const char* className
, const char* propName
) {
915 throw_readonly_violation(className
, propName
, Strings::MUST_BE_VALUE_TYPE
);
918 void throw_cannot_modify_readonly_collection() {
919 SystemLib::throwReadonlyViolationExceptionObject(
920 Strings::READONLY_COLLECTIONS_CANNOT_BE_MODIFIED
924 bool readonlyLocalShouldThrow(TypedValue tv
, ReadonlyOp op
) {
925 if (op
== ReadonlyOp::CheckROCOW
|| op
== ReadonlyOp::CheckMutROCOW
) {
926 vmMInstrState().roProp
= true;
927 if (type(tv
) == KindOfObject
) return true;
932 void checkReadonly(const TypedValue
* tv
,
934 const StringData
* name
,
938 if ((op
== ReadonlyOp::CheckMutROCOW
&& readonly
) || op
== ReadonlyOp::CheckROCOW
) {
939 vmMInstrState().roProp
= true;
942 if (op
== ReadonlyOp::CheckMutROCOW
|| op
== ReadonlyOp::CheckROCOW
) {
943 if (type(tv
) == KindOfObject
) {
944 throw_must_be_value_type(cls
->name()->data(), name
->data());
946 } else if (op
== ReadonlyOp::Mutable
) {
948 throw_must_be_mutable(cls
->name()->data(), name
->data());
950 throw_must_be_enclosed_in_readonly(cls
->name()->data(), name
->data());
953 } else if (op
== ReadonlyOp::Readonly
|| op
== ReadonlyOp::CheckROCOW
) {
954 throw_must_be_readonly(cls
->name()->data(), name
->data());
959 void throw_late_init_prop(const Class
* cls
,
960 const StringData
* propName
,
962 SystemLib::throwInvalidOperationExceptionObject(
964 "Accessing <<__LateInit>> {} '{}::{}' before initialization",
965 isSProp
? "static property" : "property",
973 void throw_parameter_wrong_type(TypedValue tv
,
975 unsigned int arg_num
,
976 const StringData
* expected_type
) {
977 auto const msg
= param_type_error_message(
978 callee
->name()->data(), arg_num
, expected_type
->data(), tv
);
979 if (RuntimeOption::PHP7_EngineExceptions
) {
980 SystemLib::throwTypeErrorObject(msg
);
982 SystemLib::throwRuntimeExceptionObject(msg
);
985 void check_collection_cast_to_array() {
986 if (RuntimeOption::WarnOnCollectionToArray
) {
987 raise_warning("Casting a collection to an array is an expensive operation "
988 "and should be avoided where possible. To convert a "
989 "collection to an array without raising a warning, use the "
990 "toArray() method.");
994 Object
create_object_only(const String
& s
) {
995 return Object::attach(g_context
->createObjectOnly(s
.get()));
998 Object
init_object(const String
& s
, const Array
& params
, ObjectData
* o
) {
999 return Object
{g_context
->initObject(s
.get(), params
, o
)};
1003 create_object(const String
& s
, const Array
& params
, bool init
/* = true */) {
1004 return Object::attach(g_context
->createObject(s
.get(), params
, init
));
1007 void throw_object(const Object
& e
) {
1008 throw req::root
<Object
>(e
);
1011 #if ((__GNUC__ != 4) || (__GNUC_MINOR__ != 8))
1012 void throw_object(Object
&& e
) {
1013 throw req::root
<Object
>(std::move(e
));
1018 * This function is used when another thread is segfaulting---we just
1019 * want to wait forever to give it a chance to write a stacktrace file
1020 * (and maybe a core file).
1022 void pause_forever() {
1023 for (;;) sleep(300);
1026 bool is_constructor_name(const char* fn
) {
1027 auto len
= strlen(fn
);
1028 const char construct
[] = "__construct";
1029 auto clen
= sizeof(construct
) - 1;
1031 if (len
>= clen
&& !strcasecmp(fn
+ len
- clen
, construct
)) {
1032 if (len
== clen
|| (len
> clen
+ 2 &&
1033 fn
[len
- clen
- 1] == ':' &&
1034 fn
[len
- clen
- 2] == ':')) {
1041 void throw_missing_arguments_nr(const char *fn
, int expected
, int got
) {
1042 SystemLib::throwRuntimeExceptionObject(folly::sformat(
1043 "{}() expects exactly {} parameter{}, {} given",
1046 expected
== 1 ? "" : "s",
1051 void raise_bad_type_warning(const char *fmt
, ...) {
1055 string_vsnprintf(msg
, fmt
, ap
);
1058 raise_warning("Invalid operand type was used: %s", msg
.c_str());
1061 void raise_expected_array_warning(const char* fn
/*=nullptr*/) {
1063 fn
= fromLeaf([&](const BTFrame
& frm
) {
1064 return frm
.func()->name()->data();
1065 }, backtrace_detail::true_pred
, "(unknown)");
1067 raise_bad_type_warning("%s expects array(s)", fn
);
1070 void raise_expected_array_or_collection_warning(const char* fn
/*=nullptr*/) {
1072 fn
= fromLeaf([&](const BTFrame
& frm
) {
1073 return frm
.func()->name()->data();
1074 }, backtrace_detail::true_pred
, "(unknown)");
1076 raise_bad_type_warning("%s expects array(s) or collection(s)", fn
);
1079 void raise_invalid_argument_warning(const char *fmt
, ...) {
1083 string_vsnprintf(msg
, fmt
, ap
);
1085 raise_warning("Invalid argument: %s", msg
.c_str());
1088 Variant
throw_fatal_unset_static_property(const char *s
, const char *prop
) {
1089 raise_error("Attempt to unset static property %s::$%s", s
, prop
);
1090 return uninit_null();
1093 Variant
unserialize_ex(const char* str
, int len
,
1094 VariableUnserializer::Type type
,
1095 const Array
& options
/* = null_array */,
1096 bool pure
/* = false */) {
1097 if (str
== nullptr || len
<= 0) {
1101 VariableUnserializer
vu(str
, len
, type
,
1102 /* allowUnknownSerializableClass = */ true,
1104 if (pure
) vu
.setPure();
1107 v
= vu
.unserialize();
1108 } catch (FatalErrorException
& e
) {
1110 } catch (InvalidAllowedClassesException
& e
) {
1112 "unserialize(): allowed_classes option should be array or boolean"
1115 } catch (Exception
& e
) {
1116 raise_notice("Unable to unserialize: [%.*s]. %s.",
1117 std::min(len
, 1000), str
, e
.getMessage().c_str());
1123 Variant
unserialize_ex(const String
& str
,
1124 VariableUnserializer::Type type
,
1125 const Array
& options
/* = null_array */,
1126 bool pure
/* = false */) {
1127 return unserialize_ex(str
.data(), str
.size(), type
, options
, pure
);
1130 String
concat3(const String
& s1
, const String
& s2
, const String
& s3
) {
1131 auto r1
= s1
.slice();
1132 auto r2
= s2
.slice();
1133 auto r3
= s3
.slice();
1134 auto len
= r1
.size() + r2
.size() + r3
.size();
1135 auto str
= String::attach(StringData::Make(len
));
1136 auto const r
= str
.mutableData();
1137 memcpy(r
, r1
.data(), r1
.size());
1138 memcpy(r
+ r1
.size(), r2
.data(), r2
.size());
1139 memcpy(r
+ r1
.size() + r2
.size(), r3
.data(), r3
.size());
1144 String
concat4(const String
& s1
, const String
& s2
, const String
& s3
,
1146 auto r1
= s1
.slice();
1147 auto r2
= s2
.slice();
1148 auto r3
= s3
.slice();
1149 auto r4
= s4
.slice();
1150 auto len
= r1
.size() + r2
.size() + r3
.size() + r4
.size();
1151 auto str
= String::attach(StringData::Make(len
));
1152 auto const r
= str
.mutableData();
1153 memcpy(r
, r1
.data(), r1
.size());
1154 memcpy(r
+ r1
.size(), r2
.data(), r2
.size());
1155 memcpy(r
+ r1
.size() + r2
.size(), r3
.data(), r3
.size());
1156 memcpy(r
+ r1
.size() + r2
.size() + r3
.size(), r4
.data(), r4
.size());
1161 static bool invoke_file_impl(Variant
& res
, const String
& path
, bool once
,
1162 const char *currentDir
,
1163 bool callByHPHPInvoke
) {
1165 auto const u
= lookupUnit(path
.get(), currentDir
, &initial
,
1166 Native::s_noNativeFuncs
, false);
1167 if (u
== nullptr) return false;
1168 if (!once
|| initial
) {
1169 *res
.asTypedValue() = g_context
->invokeUnit(u
, callByHPHPInvoke
);
1174 static NEVER_INLINE Variant
throw_missing_file(const char* file
) {
1175 throw PhpFileDoesNotExistException(file
);
1178 static Variant
invoke_file(const String
& s
,
1180 const char *currentDir
,
1181 bool callByHPHPInvoke
) {
1183 if (invoke_file_impl(r
, s
, once
, currentDir
, callByHPHPInvoke
)) {
1186 return throw_missing_file(s
.c_str());
1189 Variant
include_impl_invoke(const String
& file
, bool once
,
1190 const char *currentDir
, bool callByHPHPInvoke
) {
1191 if (FileUtil::isAbsolutePath(file
.toCppString())) {
1192 if (RuntimeOption::SandboxMode
|| !RuntimeOption::AlwaysUseRelativePath
) {
1194 return invoke_file(file
, once
, currentDir
, callByHPHPInvoke
);
1195 } catch(PhpFileDoesNotExistException
& e
) {}
1199 String
rel_path(FileUtil::relativePath(RuntimeOption::SourceRoot
,
1200 string(file
.data())));
1202 // Don't try/catch - We want the exception to be passed along
1203 return invoke_file(rel_path
, once
, currentDir
, callByHPHPInvoke
);
1204 } catch(PhpFileDoesNotExistException
& e
) {
1205 throw PhpFileDoesNotExistException(file
.c_str());
1208 // Don't try/catch - We want the exception to be passed along
1209 return invoke_file(file
, once
, currentDir
, callByHPHPInvoke
);
1214 * Used by include_impl. resolve_include() needs some way of checking the
1215 * existence of a file path, which for hphpc means attempting to invoke it.
1216 * This struct carries some context information needed for the invocation, as
1217 * well as a place for the return value of invoking the file.
1219 struct IncludeImplInvokeContext
{
1221 const char* currentDir
;
1223 Variant returnValue
;
1226 static bool include_impl_invoke_context(const String
& file
, void* ctx
) {
1227 struct IncludeImplInvokeContext
* context
= (IncludeImplInvokeContext
*)ctx
;
1228 bool invoked_file
= false;
1230 context
->returnValue
= include_impl_invoke(file
, context
->once
,
1231 context
->currentDir
);
1232 invoked_file
= true;
1233 } catch (PhpFileDoesNotExistException
& e
) {
1234 context
->returnValue
= false;
1236 return invoked_file
;
1240 * tryFile is a pointer to a function that resolve_include() will use to
1241 * determine if a path references a real file. ctx is a pointer to some context
1242 * information that will be passed through to tryFile. (It's a hacky closure)
1244 String
resolve_include(const String
& file
, const char* currentDir
,
1245 bool (*tryFile
)(const String
& file
, void*), void* ctx
) {
1246 const char* c_file
= file
.data();
1248 auto const getCwd
= [] () -> String
{
1249 if (LIKELY(!g_context
.isNull())) return g_context
->getCwd();
1250 return String(Process::CurrentWorkingDirectory
, CopyString
);
1253 if (!File::IsPlainFilePath(file
)) {
1254 // URIs don't have an include path
1255 if (tryFile(file
, ctx
)) {
1259 } else if (FileUtil::isAbsolutePath(file
.toCppString())) {
1260 String can_path
= FileUtil::canonicalize(file
);
1262 if (tryFile(can_path
, ctx
)) {
1266 } else if ((c_file
[0] == '.' && (c_file
[1] == '/' || (
1267 c_file
[1] == '.' && c_file
[2] == '/')))) {
1269 String
path(String(getCwd() + "/" + file
));
1270 String can_path
= FileUtil::canonicalize(path
);
1272 if (tryFile(can_path
, ctx
)) {
1277 if (!RequestInfo::s_requestInfo
.isNull()) {
1278 auto const& includePaths
= RID().getIncludePaths();
1280 for (auto const& includePath
: includePaths
) {
1282 auto const is_stream_wrapper
=
1283 includePath
.find("://") != std::string::npos
;
1285 if (!is_stream_wrapper
&& !FileUtil::isAbsolutePath(includePath
)) {
1286 path
+= (getCwd() + "/");
1289 path
+= includePath
;
1291 if (path
[path
.size() - 1] != '/') {
1298 if (!is_stream_wrapper
) {
1299 can_path
= FileUtil::canonicalize(path
);
1301 can_path
= String(path
.c_str());
1304 if (tryFile(can_path
, ctx
)) {
1310 if (FileUtil::isAbsolutePath(currentDir
)) {
1311 String
path(currentDir
);
1314 String can_path
= FileUtil::canonicalize(path
);
1316 if (tryFile(can_path
, ctx
)) {
1320 String
path(getCwd() + "/" + currentDir
+ file
);
1321 String can_path
= FileUtil::canonicalize(path
);
1323 if (tryFile(can_path
, ctx
)) {
1332 static Variant
include_impl(const String
& file
, bool once
,
1333 const char *currentDir
, bool required
,
1335 struct IncludeImplInvokeContext ctx
= {once
, currentDir
};
1336 String can_path
= resolve_include(file
, currentDir
,
1337 include_impl_invoke_context
, (void*)&ctx
);
1339 if (can_path
.isNull()) {
1342 raise_notice("Tried to invoke %s but file not found.", file
.data());
1345 String ms
= "Required file that does not exist: ";
1347 raise_fatal_error(ms
.data());
1352 return ctx
.returnValue
;
1355 Variant
require(const String
& file
,
1357 const char* currentDir
,
1359 return include_impl(file
, once
, currentDir
, true, raiseNotice
);
1362 bool function_exists(const String
& function_name
) {
1363 auto f
= Func::lookup(function_name
.get());
1364 return (f
!= nullptr) &&
1365 (f
->arFuncPtr() != Native::unimplementedWrapper
);
1368 ///////////////////////////////////////////////////////////////////////////////
1369 // debugger and code coverage instrumentation
1371 void throw_exception(const Object
& e
) {
1372 if (!e
.instanceof(SystemLib::s_ThrowableClass
)) {
1373 raise_error("Exceptions must implement the Throwable interface.");
1375 DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionThrownHook(e
.get()));
1376 throw req::root
<Object
>(e
);
1379 ///////////////////////////////////////////////////////////////////////////////