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/externals.h"
24 #include "hphp/runtime/base/file-util.h"
25 #include "hphp/runtime/base/request-injection-data.h"
26 #include "hphp/runtime/base/runtime-option.h"
27 #include "hphp/runtime/base/strings.h"
28 #include "hphp/runtime/base/unit-cache.h"
29 #include "hphp/runtime/base/variable-serializer.h"
30 #include "hphp/runtime/base/variable-unserializer.h"
32 #include "hphp/runtime/debugger/debugger.h"
34 #include "hphp/runtime/ext/std/ext_std_closure.h"
35 #include "hphp/runtime/ext/std/ext_std_function.h"
36 #include "hphp/runtime/ext/string/ext_string.h"
38 #include "hphp/runtime/vm/event-hook.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/repo.h"
43 #include "hphp/runtime/vm/unit-util.h"
44 #include "hphp/runtime/vm/unit.h"
46 #include "hphp/system/systemlib.h"
48 #include "hphp/util/logger.h"
49 #include "hphp/util/process.h"
50 #include "hphp/util/string-vsnprintf.h"
51 #include "hphp/util/text-util.h"
53 #include <folly/Format.h>
55 #include <boost/format.hpp>
63 ///////////////////////////////////////////////////////////////////////////////
67 s_offsetExists("offsetExists"),
69 s___callStatic("__callStatic"),
70 s___invoke("__invoke"),
75 const StaticString
s_cmpWithCollection(
76 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
77 "a collection with an integer, double, string, array, or object"
79 const StaticString
s_cmpWithVec(
80 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
81 "a vec with a non-vec"
83 const StaticString
s_cmpWithDict(
84 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
87 const StaticString
s_cmpWithKeyset(
88 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
92 ///////////////////////////////////////////////////////////////////////////////
94 bool array_is_valid_callback(const Array
& arr
) {
95 if (!arr
.isPHPArray() && !arr
.isVecArray()) return false;
96 if (arr
.size() != 2 || !arr
.exists(int64_t(0)) || !arr
.exists(int64_t(1))) {
99 auto const elem0
= arr
.rvalAt(0).unboxed();
100 if (!isStringType(elem0
.type()) && !isObjectType(elem0
.type())) {
103 auto const elem1
= arr
.rvalAt(1).unboxed();
104 if (!isStringType(elem1
.type())) {
111 s__invoke("__invoke"),
112 s_Closure__invoke("Closure::__invoke"),
115 bool is_callable(const Variant
& v
) {
116 ObjectData
* obj
= nullptr;
117 HPHP::Class
* cls
= nullptr;
118 StringData
* invName
= nullptr;
120 const HPHP::Func
* f
= vm_decode_function(v
, GetCallerFrame(), false, obj
, cls
,
122 DecodeFlags::LookupOnly
);
123 if (invName
!= nullptr) {
126 return f
!= nullptr && !f
->isAbstract();
129 bool is_callable(const Variant
& v
, bool syntax_only
, RefData
* name
) {
131 if (LIKELY(!syntax_only
)) {
132 ret
= is_callable(v
);
133 if (LIKELY(!name
)) return ret
;
136 auto const tv_func
= v
.asCell();
137 if (isStringType(tv_func
->m_type
)) {
138 if (name
) *name
->var() = v
;
142 if (isArrayType(tv_func
->m_type
) || isVecType(tv_func
->m_type
)) {
143 auto const arr
= Array(tv_func
->m_data
.parr
);
144 auto const clsname
= arr
.rvalAt(int64_t(0)).unboxed();
145 auto const mthname
= arr
.rvalAt(int64_t(1)).unboxed();
147 if (arr
.size() != 2 ||
148 clsname
.is_dummy() ||
149 !isStringType(mthname
.type())) {
150 if (name
) *name
->var() = array_string
;
154 StringData
* clsString
= nullptr;
155 if (isObjectType(clsname
.type())) {
156 clsString
= clsname
.val().pobj
->getClassName().get();
157 } else if (isStringType(clsname
.type())) {
158 clsString
= clsname
.val().pstr
;
160 if (name
) *name
->var() = array_string
;
165 *name
->var() = concat3(String
{clsString
},
167 String
{mthname
.val().pstr
});
172 if (tv_func
->m_type
== KindOfObject
) {
173 ObjectData
*d
= tv_func
->m_data
.pobj
;
174 const Func
* invoke
= d
->getVMClass()->lookupMethod(s__invoke
.get());
176 if (d
->instanceof(c_Closure::classof())) {
177 // Hack to stop the mangled name from showing up
178 *name
->var() = s_Closure__invoke
;
180 *name
->var() = d
->getClassName().asString() + "::__invoke";
183 return invoke
!= nullptr;
190 vm_decode_function(const Variant
& function
,
195 StringData
*& invName
,
197 DecodeFlags flags
/* = DecodeFlags::Warn */) {
200 if (function
.isString() || function
.isArray()) {
201 HPHP::Class
* ctx
= nullptr;
202 if (ar
) ctx
= arGetContextClass(ar
);
203 // Decode the 'function' parameter into this_, cls, name, pos, and
204 // nameContainsClass.
208 int pos
= String::npos
;
209 bool nameContainsClass
= false;
210 if (function
.isString()) {
211 // If 'function' is a string we simply assign it to name and
212 // leave this_ and cls set to NULL.
213 name
= function
.toString();
214 pos
= name
.find("::");
216 (pos
!= 0 && pos
!= String::npos
&& pos
+ 2 < name
.size());
218 // If 'function' is an array with exactly two indices 0 and 1, we
219 // assign the value at index 1 to name and we use the value at index
220 // 0 to populate cls (if the value is a string) or this_ and cls (if
221 // the value is an object).
222 assert(function
.isArray());
223 Array arr
= function
.toArray();
224 if (!array_is_valid_callback(arr
)) {
225 if (flags
== DecodeFlags::Warn
) {
226 throw_invalid_argument("function: not a valid callback array");
230 Variant elem1
= arr
[1];
231 name
= elem1
.toString();
232 pos
= name
.find("::");
234 (pos
!= 0 && pos
!= String::npos
&& pos
+ 2 < name
.size());
235 Variant elem0
= arr
[0];
236 if (elem0
.isString()) {
237 String sclass
= elem0
.toString();
238 if (sclass
.get()->isame(s_self
.get())) {
242 if (!nameContainsClass
) {
245 } else if (sclass
.get()->isame(s_parent
.get())) {
246 if (ctx
&& ctx
->parent()) {
249 if (!nameContainsClass
) {
252 } else if (sclass
.get()->isame(s_static
.get())) {
253 if (ar
&& ar
->func()->cls()) {
255 cls
= ar
->getThis()->getVMClass();
257 cls
= ar
->getClass();
261 if (flags
== DecodeFlags::Warn
&& nameContainsClass
) {
262 String nameClass
= name
.substr(0, pos
);
263 if (nameClass
.get()->isame(s_self
.get()) ||
264 nameClass
.get()->isame(s_static
.get())) {
265 raise_warning("behavior of call_user_func(array('%s', '%s')) "
266 "is undefined", sclass
.data(), name
.data());
269 cls
= Unit::loadClass(sclass
.get());
272 if (flags
== DecodeFlags::Warn
) {
273 throw_invalid_argument("function: class not found");
278 assert(elem0
.isObject());
279 this_
= elem0
.getObjectData();
280 cls
= this_
->getVMClass();
284 HPHP::Class
* cc
= cls
;
285 if (nameContainsClass
) {
286 String c
= name
.substr(0, pos
);
287 name
= name
.substr(pos
+ 2);
288 if (c
.get()->isame(s_self
.get())) {
297 } else if (c
.get()->isame(s_parent
.get())) {
300 } else if (ctx
&& ctx
->parent()) {
306 } else if (c
.get()->isame(s_static
.get())) {
307 if (ar
&& ar
->func()->cls()) {
309 cc
= ar
->getThis()->getVMClass();
315 cc
= Unit::loadClass(c
.get());
318 if (flags
== DecodeFlags::Warn
) {
319 throw_invalid_argument("function: class not found");
324 if (!cls
->classof(cc
)) {
325 if (flags
== DecodeFlags::Warn
) {
326 raise_warning("call_user_func expects parameter 1 to be a valid "
327 "callback, class '%s' is not a subclass of '%s'",
328 cls
->preClass()->name()->data(),
329 cc
->preClass()->name()->data());
334 // If there is not a current instance, cc trumps cls.
341 HPHP::Func
* f
= HPHP::Unit::loadFunc(name
.get());
343 if (flags
== DecodeFlags::Warn
) {
344 throw_invalid_argument("function: method '%s' not found",
349 assert(f
&& f
->preClass() == nullptr);
353 CallType lookupType
= this_
? CallType::ObjMethod
: CallType::ClsMethod
;
354 auto f
= lookupMethodCtx(cc
, name
.get(), ctx
, lookupType
);
355 if (f
&& (f
->attrs() & AttrStatic
)) {
356 // If we found a method and its static, null out this_
359 if (!this_
&& ar
&& ar
->func()->cls() && ar
->hasThis()) {
360 // If we did not find a static method AND this_ is null AND there is a
361 // frame ar, check if the current instance from ar is compatible
362 auto const obj
= ar
->getThis();
363 if (obj
->instanceof(cls
)) {
365 cls
= obj
->getVMClass();
370 // If this_ is non-null AND we could not find a method, try
371 // looking up __call in cls's method table
372 f
= cls
->lookupMethod(s___call
.get());
373 assert(!f
|| !(f
->attrs() & AttrStatic
));
375 if (!f
&& lookupType
== CallType::ClsMethod
) {
376 f
= cls
->lookupMethod(s___callStatic
.get());
377 assert(!f
|| (f
->attrs() & AttrStatic
));
380 if (f
&& (cc
== cls
|| cc
->lookupMethod(f
->name()))) {
381 // We found __call or __callStatic!
382 // Stash the original name into invName.
383 invName
= name
.get();
384 invName
->incRefCount();
386 // Bail out if we couldn't find the method or __call
387 if (flags
== DecodeFlags::Warn
) {
388 throw_invalid_argument("function: method '%s' not found",
396 if (!this_
&& !f
->isStaticInPrologue()) {
397 if (flags
== DecodeFlags::Warn
) raise_missing_this(f
);
398 if (flags
!= DecodeFlags::LookupOnly
&& f
->attrs() & AttrRequiresThis
) {
403 assert(f
&& f
->preClass());
404 // If this_ is non-NULL, then this_ is the current instance and cls is
405 // the class of the current instance.
406 assert(!this_
|| this_
->getVMClass() == cls
);
407 // If we are doing a forwarding call and this_ is null, set cls
408 // appropriately to propagate the current late bound class.
409 if (!this_
&& forwarding
&& ar
&& ar
->func()->cls()) {
410 auto const fwdCls
= ar
->hasThis() ?
411 ar
->getThis()->getVMClass() : ar
->getClass();
413 // Only forward the current late bound class if it is the same or
414 // a descendent of cls
415 if (fwdCls
->classof(cls
)) {
422 if (function
.isObject()) {
423 this_
= function
.asCObjRef().get();
426 const HPHP::Func
*f
= this_
->getVMClass()->lookupMethod(s___invoke
.get());
427 if (f
!= nullptr && f
->isStaticInPrologue()) {
428 // If __invoke is static, invoke it as such
429 cls
= this_
->getVMClass();
432 if (flags
== DecodeFlags::Warn
&& f
== nullptr) {
433 raise_warning("call_user_func() expects parameter 1 to be a valid "
434 "callback, object of class %s given (no __invoke "
435 "method found)", this_
->getVMClass()->name()->data());
439 if (flags
== DecodeFlags::Warn
) {
440 throw_invalid_argument("function: not string, closure, or array");
445 Variant
vm_call_user_func(const Variant
& function
, const Variant
& params
,
446 bool forwarding
/* = false */,
447 bool checkRef
/* = false */) {
448 ObjectData
* obj
= nullptr;
449 Class
* cls
= nullptr;
451 StringData
* invName
= nullptr;
453 const Func
* f
= vm_decode_function(function
, cf(), forwarding
,
454 obj
, cls
, invName
, dynamic
);
455 if (f
== nullptr || (!isContainer(params
) && !params
.isNull())) {
456 return uninit_null();
458 auto ret
= Variant::attach(
459 g_context
->invokeFunc(f
, params
, obj
, cls
,
460 nullptr, invName
, ExecutionContext::InvokeCuf
,
461 false, dynamic
, checkRef
)
463 if (UNLIKELY(ret
.getRawType() == KindOfRef
)) {
464 tvUnbox(*ret
.asTypedValue());
469 static Variant
invoke_failed(const char *func
,
470 bool fatal
/* = true */) {
472 throw ExtendedException("(1) call the function without enough arguments OR "
473 "(2) Unable to find function \"%s\" OR "
474 "(3) function was not in invoke table OR "
475 "(4) function was renamed to something else.",
478 raise_warning("call_user_func to non-existent function %s", func
);
483 invoke(const String
& function
, const Variant
& params
, strhash_t
/*hash*/,
484 bool /*tryInterp*/, bool fatal
, bool useWeakTypes
= false) {
485 Func
* func
= Unit::loadFunc(function
.get());
486 if (func
&& (isContainer(params
) || params
.isNull())) {
487 auto ret
= Variant::attach(
488 g_context
->invokeFunc(func
, params
, nullptr, nullptr,
489 nullptr, nullptr, ExecutionContext::InvokeNormal
,
492 if (UNLIKELY(ret
.getRawType() == KindOfRef
)) {
493 tvUnbox(*ret
.asTypedValue());
497 return invoke_failed(function
.c_str(), fatal
);
500 // Declared in externals.h. If you're considering calling this
501 // function for some new code, please reconsider.
502 Variant
invoke(const char *function
, const Variant
& params
, strhash_t hash
/* = -1 */,
503 bool tryInterp
/* = true */, bool fatal
/* = true */,
504 bool useWeakTypes
/* = false */) {
505 String
funcName(function
, CopyString
);
506 return invoke(funcName
, params
, hash
, tryInterp
, fatal
, useWeakTypes
);
509 Variant
invoke_static_method(const String
& s
, const String
& method
,
510 const Variant
& params
, bool fatal
/* = true */) {
511 HPHP::Class
* class_
= Unit::lookupClass(s
.get());
512 if (class_
== nullptr) {
513 o_invoke_failed(s
.data(), method
.data(), fatal
);
514 return uninit_null();
516 const HPHP::Func
* f
= class_
->lookupMethod(method
.get());
517 if (f
== nullptr || !(f
->attrs() & AttrStatic
) ||
518 (!isContainer(params
) && !params
.isNull())) {
519 o_invoke_failed(s
.data(), method
.data(), fatal
);
520 return uninit_null();
522 auto ret
= Variant::attach(
523 g_context
->invokeFunc(f
, params
, nullptr, class_
)
525 if (UNLIKELY(ret
.getRawType() == KindOfRef
)) {
526 tvUnbox(*ret
.asTypedValue());
531 Variant
o_invoke_failed(const char *cls
, const char *meth
,
532 bool fatal
/* = true */) {
534 string msg
= "Unknown method ";
538 raise_fatal_error(msg
.c_str());
540 raise_warning("call_user_func to non-existent method %s::%s", cls
, meth
);
545 template<class EF
, class NF
>
546 void missing_this_check_helper(const Func
* f
, EF ef
, NF nf
) {
547 if (f
->attrs() & AttrRequiresThis
) {
552 if (RuntimeOption::EvalRaiseMissingThis
&& !f
->isStatic()) {
558 void raise_missing_this(const Func
* f
) {
559 missing_this_check_helper(
562 raise_error("Non-static method %s() cannot be called statically",
563 f
->fullName()->data());
567 "Non-static method %s() should not be called statically";
569 if (RuntimeOption::PHP7_DeprecationWarnings
) {
570 raise_deprecated(msg
, f
->fullName()->data());
572 raise_strict_warning(msg
, f
->fullName()->data());
577 bool needs_missing_this_check(const Func
* f
) {
579 missing_this_check_helper(
587 void NEVER_INLINE
raise_null_object_prop() {
588 raise_notice("Trying to get property of non-object");
591 void NEVER_INLINE
throw_null_get_object_prop() {
592 raise_error("Trying to get property of non-object");
595 void NEVER_INLINE
throw_invalid_property_name(const String
& name
) {
597 raise_error("Cannot access empty property");
599 raise_error("Cannot access property started with '\\0'");
602 void throw_instance_method_fatal(const char *name
) {
603 if (!strstr(name
, "::__destruct")) {
604 raise_error("Non-static method %s() cannot be called statically", name
);
608 void throw_iterator_not_valid() {
609 SystemLib::throwInvalidOperationExceptionObject(
610 "Iterator is not valid");
613 void throw_collection_property_exception() {
614 SystemLib::throwInvalidOperationExceptionObject(
615 "Cannot access a property on a collection");
618 void throw_invalid_collection_parameter() {
619 SystemLib::throwInvalidArgumentExceptionObject(
620 "Parameter must be an array or an instance of Traversable");
623 void throw_invalid_operation_exception(StringData
* str
) {
624 SystemLib::throwInvalidOperationExceptionObject(Variant
{str
});
627 void throw_arithmetic_error(StringData
* str
) {
628 SystemLib::throwArithmeticErrorObject(Variant
{str
});
631 void throw_division_by_zero_error(StringData
*str
) {
632 SystemLib::throwDivisionByZeroErrorObject(Variant
{str
});
635 void throw_collection_compare_exception() {
636 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithCollection
);
639 void throw_vec_compare_exception() {
640 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithVec
);
643 void throw_dict_compare_exception() {
644 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithDict
);
647 void throw_keyset_compare_exception() {
648 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithKeyset
);
651 void throw_param_is_not_container() {
652 static const string
msg("Parameter must be an array or collection");
653 SystemLib::throwInvalidArgumentExceptionObject(msg
);
656 void throw_invalid_inout_base() {
657 SystemLib::throwInvalidArgumentExceptionObject(
658 "Parameters marked inout must be contained in locals, vecs, dicts, "
659 "keysets, and arrays"
663 void throw_cannot_modify_immutable_object(const char* className
) {
664 auto msg
= folly::sformat(
665 "Cannot modify immutable object of type {}",
668 SystemLib::throwInvalidOperationExceptionObject(msg
);
671 void throw_object_forbids_dynamic_props(const char* className
) {
672 auto msg
= folly::sformat(
673 "Class {} does not allow use of dynamic (non-declared) properties",
676 SystemLib::throwInvalidOperationExceptionObject(msg
);
679 void throw_cannot_modify_immutable_prop(const char* className
,
680 const char* propName
)
682 auto msg
= folly::sformat(
683 "Cannot modify immutable property {} of class {} after construction",
686 SystemLib::throwInvalidOperationExceptionObject(msg
);
689 void throw_cannot_bind_immutable_prop(const char* className
,
690 const char* propName
)
692 auto msg
= folly::sformat(
693 "Cannot bind immutable property {} of class {}",
696 SystemLib::throwInvalidOperationExceptionObject(msg
);
699 void check_collection_compare(const ObjectData
* obj
) {
700 if (obj
&& obj
->isCollection()) throw_collection_compare_exception();
703 void check_collection_compare(const ObjectData
* obj1
, const ObjectData
* obj2
) {
704 if (obj1
&& obj2
&& (obj1
->isCollection() || obj2
->isCollection())) {
705 throw_collection_compare_exception();
709 void check_collection_cast_to_array() {
710 if (RuntimeOption::WarnOnCollectionToArray
) {
711 raise_warning("Casting a collection to an array is an expensive operation "
712 "and should be avoided where possible. To convert a "
713 "collection to an array without raising a warning, use the "
714 "toArray() method.");
718 Object
create_object_only(const String
& s
) {
719 return Object::attach(g_context
->createObjectOnly(s
.get()));
722 Object
init_object(const String
& s
, const Array
& params
, ObjectData
* o
) {
723 return Object
{g_context
->initObject(s
.get(), params
, o
)};
727 create_object(const String
& s
, const Array
& params
, bool init
/* = true */) {
728 return Object::attach(g_context
->createObject(s
.get(), params
, init
));
731 void throw_object(const Object
& e
) {
732 throw req::root
<Object
>(e
);
735 #if ((__GNUC__ != 4) || (__GNUC_MINOR__ != 8))
736 void throw_object(Object
&& e
) {
737 throw req::root
<Object
>(std::move(e
));
742 * This function is used when another thread is segfaulting---we just
743 * want to wait forever to give it a chance to write a stacktrace file
744 * (and maybe a core file).
746 void pause_forever() {
750 bool is_constructor_name(const char* fn
) {
751 auto len
= strlen(fn
);
752 const char construct
[] = "__construct";
753 auto clen
= sizeof(construct
) - 1;
755 if (len
>= clen
&& !strcasecmp(fn
+ len
- clen
, construct
)) {
756 if (len
== clen
|| (len
> clen
+ 2 &&
757 fn
[len
- clen
- 1] == ':' &&
758 fn
[len
- clen
- 2] == ':')) {
765 void throw_wrong_argument_count_nr(const char *fn
, int expected
, int got
,
766 const char *expectDesc
,
768 TypedValue
*rv
/* = nullptr */) {
770 rv
->m_data
.num
= 0LL;
771 rv
->m_type
= KindOfNull
;
775 msg
= (boost::format(Strings::MISSING_ARGUMENT
) %
776 fn
% expectDesc
% got
).str();
778 msg
= (boost::format(Strings::MISSING_ARGUMENTS
) %
779 fn
% expectDesc
% expected
% got
).str();
785 if (is_constructor_name(fn
)) {
786 SystemLib::throwExceptionObject(msg
);
792 void throw_missing_arguments_nr(const char *fn
, int expected
, int got
,
794 TypedValue
*rv
/* = nullptr */) {
795 throw_wrong_argument_count_nr(fn
, expected
, got
, "exactly", level
, rv
);
798 void throw_toomany_arguments_nr(const char *fn
, int expected
, int got
,
800 TypedValue
*rv
/* = nullptr */) {
801 throw_wrong_argument_count_nr(fn
, expected
, got
, "exactly", level
, rv
);
804 void throw_wrong_arguments_nr(const char *fn
, int count
, int cmin
, int cmax
,
806 TypedValue
*rv
/* = nullptr */) {
807 if (cmin
>= 0 && count
< cmin
) {
809 throw_wrong_argument_count_nr(fn
, cmin
, count
, "at least", level
, rv
);
811 throw_wrong_argument_count_nr(fn
, cmin
, count
, "exactly", level
, rv
);
815 if (cmax
>= 0 && count
> cmax
) {
817 throw_wrong_argument_count_nr(fn
, cmax
, count
, "at most", level
, rv
);
819 throw_wrong_argument_count_nr(fn
, cmax
, count
, "exactly", level
, rv
);
824 rv
->m_data
.num
= 0LL;
825 rv
->m_type
= KindOfNull
;
830 void throw_bad_type_exception(const char *fmt
, ...) {
834 string_vsnprintf(msg
, fmt
, ap
);
837 raise_warning("Invalid operand type was used: %s", msg
.c_str());
840 void throw_expected_array_exception(const char* fn
/*=nullptr*/) {
842 if (auto ar
= g_context
->getStackFrame()) {
843 fn
= ar
->m_func
->name()->data();
848 throw_bad_type_exception("%s expects array(s)", fn
);
851 void throw_expected_array_or_collection_exception(const char* fn
/*=nullptr*/) {
853 if (auto ar
= g_context
->getStackFrame()) {
854 fn
= ar
->m_func
->name()->data();
859 throw_bad_type_exception("%s expects array(s) or collection(s)", fn
);
862 void throw_invalid_argument(const char *fmt
, ...) {
866 string_vsnprintf(msg
, fmt
, ap
);
868 raise_warning("Invalid argument: %s", msg
.c_str());
871 Variant
throw_fatal_unset_static_property(const char *s
, const char *prop
) {
872 raise_error("Attempt to unset static property %s::$%s", s
, prop
);
873 return uninit_null();
876 Variant
unserialize_ex(const char* str
, int len
,
877 VariableUnserializer::Type type
,
878 const Array
& options
/* = null_array */) {
879 if (str
== nullptr || len
<= 0) {
883 VariableUnserializer
vu(str
, len
, type
, true, options
);
886 v
= vu
.unserialize();
887 } catch (FatalErrorException
&e
) {
889 } catch (InvalidAllowedClassesException
&e
) {
891 "unserialize(): allowed_classes option should be array or boolean"
894 } catch (Exception
&e
) {
895 raise_notice("Unable to unserialize: [%.1000s]. %s.", str
,
896 e
.getMessage().c_str());
902 Variant
unserialize_ex(const String
& str
,
903 VariableUnserializer::Type type
,
904 const Array
& options
/* = null_array */) {
905 return unserialize_ex(str
.data(), str
.size(), type
, options
);
908 String
concat3(const String
& s1
, const String
& s2
, const String
& s3
) {
909 auto r1
= s1
.slice();
910 auto r2
= s2
.slice();
911 auto r3
= s3
.slice();
912 auto len
= r1
.size() + r2
.size() + r3
.size();
913 auto str
= String::attach(StringData::Make(len
));
914 auto const r
= str
.mutableData();
915 memcpy(r
, r1
.data(), r1
.size());
916 memcpy(r
+ r1
.size(), r2
.data(), r2
.size());
917 memcpy(r
+ r1
.size() + r2
.size(), r3
.data(), r3
.size());
922 String
concat4(const String
& s1
, const String
& s2
, const String
& s3
,
924 auto r1
= s1
.slice();
925 auto r2
= s2
.slice();
926 auto r3
= s3
.slice();
927 auto r4
= s4
.slice();
928 auto len
= r1
.size() + r2
.size() + r3
.size() + r4
.size();
929 auto str
= String::attach(StringData::Make(len
));
930 auto const r
= str
.mutableData();
931 memcpy(r
, r1
.data(), r1
.size());
932 memcpy(r
+ r1
.size(), r2
.data(), r2
.size());
933 memcpy(r
+ r1
.size() + r2
.size(), r3
.data(), r3
.size());
934 memcpy(r
+ r1
.size() + r2
.size() + r3
.size(), r4
.data(), r4
.size());
939 static bool invoke_file_impl(Variant
& res
, const String
& path
, bool once
,
940 const char *currentDir
) {
942 auto const u
= lookupUnit(path
.get(), currentDir
, &initial
);
943 if (u
== nullptr) return false;
944 if (!once
|| initial
) {
945 *res
.asTypedValue() = g_context
->invokeUnit(u
);
950 static NEVER_INLINE Variant
throw_missing_file(const char* file
) {
951 throw PhpFileDoesNotExistException(file
);
954 static Variant
invoke_file(const String
& s
,
956 const char *currentDir
) {
958 if (invoke_file_impl(r
, s
, once
, currentDir
)) {
961 return throw_missing_file(s
.c_str());
964 Variant
include_impl_invoke(const String
& file
, bool once
,
965 const char *currentDir
) {
966 if (FileUtil::isAbsolutePath(file
.toCppString())) {
967 if (RuntimeOption::SandboxMode
|| !RuntimeOption::AlwaysUseRelativePath
) {
969 return invoke_file(file
, once
, currentDir
);
970 } catch(PhpFileDoesNotExistException
&e
) {}
974 String
rel_path(FileUtil::relativePath(RuntimeOption::SourceRoot
,
975 string(file
.data())));
977 // Don't try/catch - We want the exception to be passed along
978 return invoke_file(rel_path
, once
, currentDir
);
979 } catch(PhpFileDoesNotExistException
&e
) {
980 throw PhpFileDoesNotExistException(file
.c_str());
983 // Don't try/catch - We want the exception to be passed along
984 return invoke_file(file
, once
, currentDir
);
989 * Used by include_impl. resolve_include() needs some way of checking the
990 * existence of a file path, which for hphpc means attempting to invoke it.
991 * This struct carries some context information needed for the invocation, as
992 * well as a place for the return value of invoking the file.
994 struct IncludeImplInvokeContext
{
996 const char* currentDir
;
1001 static bool include_impl_invoke_context(const String
& file
, void* ctx
) {
1002 struct IncludeImplInvokeContext
* context
= (IncludeImplInvokeContext
*)ctx
;
1003 bool invoked_file
= false;
1005 context
->returnValue
= include_impl_invoke(file
, context
->once
,
1006 context
->currentDir
);
1007 invoked_file
= true;
1008 } catch (PhpFileDoesNotExistException
& e
) {
1009 context
->returnValue
= false;
1011 return invoked_file
;
1015 * tryFile is a pointer to a function that resolve_include() will use to
1016 * determine if a path references a real file. ctx is a pointer to some context
1017 * information that will be passed through to tryFile. (It's a hacky closure)
1019 String
resolve_include(const String
& file
, const char* currentDir
,
1020 bool (*tryFile
)(const String
& file
, void*), void* ctx
) {
1021 const char* c_file
= file
.data();
1023 if (!File::IsPlainFilePath(file
)) {
1024 // URIs don't have an include path
1025 if (tryFile(file
, ctx
)) {
1029 } else if (FileUtil::isAbsolutePath(file
.toCppString())) {
1030 String can_path
= FileUtil::canonicalize(file
);
1032 if (tryFile(can_path
, ctx
)) {
1036 } else if ((c_file
[0] == '.' && (c_file
[1] == '/' || (
1037 c_file
[1] == '.' && c_file
[2] == '/')))) {
1039 String
path(String(g_context
->getCwd() + "/" + file
));
1040 String can_path
= FileUtil::canonicalize(path
);
1042 if (tryFile(can_path
, ctx
)) {
1047 auto const& includePaths
= RID().getIncludePaths();
1049 for (auto const& includePath
: includePaths
) {
1051 auto const is_stream_wrapper
=
1052 includePath
.find("://") != std::string::npos
;
1054 if (!is_stream_wrapper
&& !FileUtil::isAbsolutePath(includePath
)) {
1055 path
+= (g_context
->getCwd() + "/");
1058 path
+= includePath
;
1060 if (path
[path
.size() - 1] != '/') {
1067 if (!is_stream_wrapper
) {
1068 can_path
= FileUtil::canonicalize(path
);
1070 can_path
= String(path
.c_str());
1073 if (tryFile(can_path
, ctx
)) {
1078 if (FileUtil::isAbsolutePath(currentDir
)) {
1079 String
path(currentDir
);
1082 String can_path
= FileUtil::canonicalize(path
);
1084 if (tryFile(can_path
, ctx
)) {
1088 String
path(g_context
->getCwd() + "/" + currentDir
+ file
);
1089 String can_path
= FileUtil::canonicalize(path
);
1091 if (tryFile(can_path
, ctx
)) {
1100 static Variant
include_impl(const String
& file
, bool once
,
1101 const char *currentDir
, bool required
,
1103 struct IncludeImplInvokeContext ctx
= {once
, currentDir
};
1104 String can_path
= resolve_include(file
, currentDir
,
1105 include_impl_invoke_context
, (void*)&ctx
);
1107 if (can_path
.isNull()) {
1110 raise_notice("Tried to invoke %s but file not found.", file
.data());
1113 String ms
= "Required file that does not exist: ";
1115 raise_fatal_error(ms
.data());
1120 return ctx
.returnValue
;
1123 Variant
require(const String
& file
,
1125 const char* currentDir
,
1127 return include_impl(file
, once
, currentDir
, true, raiseNotice
);
1130 bool function_exists(const String
& function_name
) {
1131 auto f
= Unit::lookupFunc(function_name
.get());
1132 return (f
!= nullptr) &&
1133 (f
->builtinFuncPtr() != Native::unimplementedWrapper
);
1136 ///////////////////////////////////////////////////////////////////////////////
1137 // debugger and code coverage instrumentation
1139 void throw_exception(const Object
& e
) {
1140 if (!e
.instanceof(SystemLib::s_ThrowableClass
)) {
1141 raise_error("Exceptions must implement the Throwable interface.");
1143 DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionThrownHook(e
.get()));
1144 throw req::root
<Object
>(e
);
1147 ///////////////////////////////////////////////////////////////////////////////