Make lookupPhpFile and lookupIncludeRoot traffic in Unit* instead of PhpFile*
[hiphop-php.git] / hphp / runtime / base / builtin-functions.cpp
blob9749eefc7b720cfb456f4270b76b5d16a70b3ebf
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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/type-conversions.h"
20 #include "hphp/runtime/base/code-coverage.h"
21 #include "hphp/runtime/base/externals.h"
22 #include "hphp/runtime/base/variable-serializer.h"
23 #include "hphp/runtime/base/variable-unserializer.h"
24 #include "hphp/runtime/base/runtime-option.h"
25 #include "hphp/runtime/base/execution-context.h"
26 #include "hphp/runtime/base/strings.h"
27 #include "hphp/runtime/base/file-repository.h"
28 #include "hphp/runtime/debugger/debugger.h"
29 #include "hphp/runtime/ext/ext_process.h"
30 #include "hphp/runtime/ext/ext_function.h"
31 #include "hphp/runtime/ext/ext_file.h"
32 #include "hphp/runtime/ext/ext_collections.h"
33 #include "hphp/runtime/ext/ext_string.h"
34 #include "hphp/util/logger.h"
35 #include "hphp/util/process.h"
36 #include "hphp/runtime/vm/repo.h"
37 #include "hphp/runtime/vm/jit/translator.h"
38 #include "hphp/runtime/vm/jit/translator-inline.h"
39 #include "hphp/runtime/vm/unit.h"
40 #include "hphp/runtime/vm/unit-util.h"
41 #include "hphp/runtime/vm/event-hook.h"
42 #include "hphp/system/systemlib.h"
43 #include "folly/Format.h"
44 #include "hphp/util/text-util.h"
45 #include "hphp/util/string-vsnprintf.h"
46 #include "hphp/runtime/base/file-util.h"
47 #include "hphp/runtime/base/container-functions.h"
48 #include "hphp/runtime/base/request-injection-data.h"
50 #include <limits>
51 #include <algorithm>
53 namespace HPHP {
55 using std::string;
57 ///////////////////////////////////////////////////////////////////////////////
58 // static strings
60 const StaticString
61 s_offsetExists("offsetExists"),
62 s___autoload("__autoload"),
63 s___call("__call"),
64 s___callStatic("__callStatic"),
65 s___invoke("__invoke"),
66 s_exception("exception"),
67 s_previous("previous"),
68 s_self("self"),
69 s_parent("parent"),
70 s_static("static"),
71 s_class("class"),
72 s_function("function"),
73 s_constant("constant"),
74 s_type("type"),
75 s_failure("failure");
77 ///////////////////////////////////////////////////////////////////////////////
79 typedef smart::unique_ptr<CufIter> SmartCufIterPtr;
81 bool array_is_valid_callback(const Array& arr) {
82 if (arr.size() != 2 || !arr.exists(int64_t(0)) || !arr.exists(int64_t(1))) {
83 return false;
85 Variant elem0 = arr.rvalAt(int64_t(0));
86 if (!elem0.isString() && !elem0.isObject()) {
87 return false;
89 Variant elem1 = arr.rvalAt(int64_t(1));
90 if (!elem1.isString()) {
91 return false;
93 return true;
96 const HPHP::Func*
97 vm_decode_function(const Variant& function,
98 ActRec* ar,
99 bool forwarding,
100 ObjectData*& this_,
101 HPHP::Class*& cls,
102 StringData*& invName,
103 bool warn /* = true */) {
104 invName = nullptr;
105 if (function.isString() || function.isArray()) {
106 HPHP::Class* ctx = nullptr;
107 if (ar) ctx = arGetContextClass(ar);
108 // Decode the 'function' parameter into this_, cls, name, pos, and
109 // nameContainsClass.
110 this_ = nullptr;
111 cls = nullptr;
112 String name;
113 int pos = String::npos;
114 bool nameContainsClass = false;
115 if (function.isString()) {
116 // If 'function' is a string we simply assign it to name and
117 // leave this_ and cls set to NULL.
118 name = function.toString();
119 pos = name.find("::");
120 nameContainsClass =
121 (pos != 0 && pos != String::npos && pos + 2 < name.size());
122 } else {
123 // If 'function' is an array with exactly two indices 0 and 1, we
124 // assign the value at index 1 to name and we use the value at index
125 // 0 to populate cls (if the value is a string) or this_ and cls (if
126 // the value is an object).
127 assert(function.isArray());
128 Array arr = function.toArray();
129 if (!array_is_valid_callback(arr)) {
130 if (warn) {
131 throw_invalid_argument("function: not a valid callback array");
133 return nullptr;
135 Variant elem1 = arr.rvalAt(int64_t(1));
136 name = elem1.toString();
137 pos = name.find("::");
138 nameContainsClass =
139 (pos != 0 && pos != String::npos && pos + 2 < name.size());
140 Variant elem0 = arr.rvalAt(int64_t(0));
141 if (elem0.isString()) {
142 String sclass = elem0.toString();
143 if (sclass.get()->isame(s_self.get())) {
144 if (ctx) {
145 cls = ctx;
147 if (!nameContainsClass) {
148 forwarding = true;
150 } else if (sclass.get()->isame(s_parent.get())) {
151 if (ctx && ctx->parent()) {
152 cls = ctx->parent();
154 if (!nameContainsClass) {
155 forwarding = true;
157 } else if (sclass.get()->isame(s_static.get())) {
158 if (ar) {
159 if (ar->hasThis()) {
160 cls = ar->getThis()->getVMClass();
161 } else if (ar->hasClass()) {
162 cls = ar->getClass();
165 } else {
166 if (warn && nameContainsClass) {
167 String nameClass = name.substr(0, pos);
168 if (nameClass.get()->isame(s_self.get()) ||
169 nameClass.get()->isame(s_static.get())) {
170 raise_warning("behavior of call_user_func(array('%s', '%s')) "
171 "is undefined", sclass.data(), name.data());
174 cls = Unit::loadClass(sclass.get());
176 if (!cls) {
177 if (warn) {
178 throw_invalid_argument("function: class not found");
180 return nullptr;
182 } else {
183 assert(elem0.isObject());
184 this_ = elem0.getObjectData();
185 cls = this_->getVMClass();
189 HPHP::Class* cc = cls;
190 if (nameContainsClass) {
191 String c = name.substr(0, pos);
192 name = name.substr(pos + 2);
193 if (c.get()->isame(s_self.get())) {
194 if (cls) {
195 cc = cls;
196 } else if (ctx) {
197 cc = ctx;
199 if (!this_) {
200 forwarding = true;
202 } else if (c.get()->isame(s_parent.get())) {
203 if (cls) {
204 cc = cls->parent();
205 } else if (ctx && ctx->parent()) {
206 cc = ctx->parent();
208 if (!this_) {
209 forwarding = true;
211 } else if (c.get()->isame(s_static.get())) {
212 if (ar) {
213 if (ar->hasThis()) {
214 cc = ar->getThis()->getVMClass();
215 } else if (ar->hasClass()) {
216 cc = ar->getClass();
219 } else {
220 cc = Unit::loadClass(c.get());
222 if (!cc) {
223 if (warn) {
224 throw_invalid_argument("function: class not found");
226 return nullptr;
228 if (cls) {
229 if (!cls->classof(cc)) {
230 if (warn) {
231 raise_warning("call_user_func expects parameter 1 to be a valid "
232 "callback, class '%s' is not a subclass of '%s'",
233 cls->preClass()->name()->data(),
234 cc->preClass()->name()->data());
236 return nullptr;
239 // If there is not a current instance, cc trumps cls.
240 if (!this_) {
241 cls = cc;
244 if (!cls) {
245 HPHP::Func* f = HPHP::Unit::loadFunc(name.get());
246 if (!f) {
247 if (warn) {
248 throw_invalid_argument("function: method '%s' not found",
249 name.data());
251 return nullptr;
253 assert(f && f->preClass() == nullptr);
254 return f;
256 assert(cls);
257 CallType lookupType = this_ ? CallType::ObjMethod : CallType::ClsMethod;
258 const HPHP::Func* f =
259 g_context->lookupMethodCtx(cc, name.get(), ctx, lookupType);
260 if (f && (f->attrs() & AttrStatic)) {
261 // If we found a method and its static, null out this_
262 this_ = nullptr;
263 } else {
264 if (!this_ && ar) {
265 // If we did not find a static method AND this_ is null AND there is a
266 // frame ar, check if the current instance from ar is compatible
267 ObjectData* obj = ar->hasThis() ? ar->getThis() : nullptr;
268 if (obj && obj->instanceof(cls)) {
269 this_ = obj;
270 cls = obj->getVMClass();
273 if (!f) {
274 if (this_) {
275 // If this_ is non-null AND we could not find a method, try
276 // looking up __call in cls's method table
277 f = cls->lookupMethod(s___call.get());
278 assert(!f || !(f->attrs() & AttrStatic));
280 if (!f && lookupType == CallType::ClsMethod) {
281 f = cls->lookupMethod(s___callStatic.get());
282 assert(!f || (f->attrs() & AttrStatic));
283 this_ = nullptr;
285 if (f && (cc == cls || cc->lookupMethod(f->name()))) {
286 // We found __call or __callStatic!
287 // Stash the original name into invName.
288 invName = name.get();
289 invName->incRefCount();
290 } else {
291 // Bail out if we couldn't find the method or __call
292 if (warn) {
293 throw_invalid_argument("function: method '%s' not found",
294 name.data());
296 return nullptr;
301 if (f->isClosureBody() && !this_) {
302 if (warn) {
303 raise_warning("cannot invoke closure body without closure object");
305 return nullptr;
308 assert(f && f->preClass());
309 // If this_ is non-NULL, then this_ is the current instance and cls is
310 // the class of the current instance.
311 assert(!this_ || this_->getVMClass() == cls);
312 // If we are doing a forwarding call and this_ is null, set cls
313 // appropriately to propagate the current late bound class.
314 if (!this_ && forwarding && ar) {
315 HPHP::Class* fwdCls = nullptr;
316 ObjectData* obj = ar->hasThis() ? ar->getThis() : nullptr;
317 if (obj) {
318 fwdCls = obj->getVMClass();
319 } else if (ar->hasClass()) {
320 fwdCls = ar->getClass();
322 // Only forward the current late bound class if it is the same or
323 // a descendent of cls
324 if (fwdCls && fwdCls->classof(cls)) {
325 cls = fwdCls;
328 return f;
330 if (function.isObject()) {
331 this_ = function.asCObjRef().get();
332 cls = nullptr;
333 const HPHP::Func *f = this_->getVMClass()->lookupMethod(s___invoke.get());
334 if (f != nullptr &&
335 ((f->attrs() & AttrStatic) && !f->isClosureBody())) {
336 // If __invoke is static, invoke it as such
337 cls = this_->getVMClass();
338 this_ = nullptr;
340 if (warn && f == nullptr) {
341 raise_warning("call_user_func() expects parameter 1 to be a valid "
342 "callback, object of class %s given (no __invoke "
343 "method found)", this_->getVMClass()->name()->data());
345 return f;
347 if (warn) {
348 throw_invalid_argument("function: not string, closure, or array");
350 return nullptr;
353 Variant vm_call_user_func(const Variant& function, const Variant& params,
354 bool forwarding /* = false */) {
355 ObjectData* obj = nullptr;
356 Class* cls = nullptr;
357 CallerFrame cf;
358 StringData* invName = nullptr;
359 const Func* f = vm_decode_function(function, cf(), forwarding,
360 obj, cls, invName);
361 if (f == nullptr || (!isContainer(params) && !params.isNull())) {
362 return uninit_null();
364 Variant ret;
365 g_context->invokeFunc((TypedValue*)&ret, f, params, obj, cls,
366 nullptr, invName, ExecutionContext::InvokeCuf);
367 return ret;
371 * Helper method from converting between a PHP function and a CufIter.
373 static bool vm_decode_function_cufiter(const Variant& function,
374 SmartCufIterPtr& cufIter) {
375 ObjectData* obj = nullptr;
376 Class* cls = nullptr;
377 CallerFrame cf;
378 StringData* invName = nullptr;
379 // Don't warn here, let the caller decide what to do if the func is nullptr.
380 const HPHP::Func* func = vm_decode_function(function, cf(), false,
381 obj, cls, invName, false);
382 if (func == nullptr) {
383 return false;
386 cufIter = smart::make_unique<CufIter>();
387 cufIter->setFunc(func);
388 cufIter->setName(invName);
389 if (obj) {
390 cufIter->setCtx(obj);
391 obj->incRefCount();
392 } else {
393 cufIter->setCtx(cls);
396 return true;
400 * Wraps calling an (autoload) PHP function from a CufIter.
402 static Variant vm_call_user_func_cufiter(const CufIter& cufIter,
403 const Array& params) {
404 ObjectData* obj = nullptr;
405 HPHP::Class* cls = nullptr;
406 StringData* invName = cufIter.name();
407 const HPHP::Func* f = cufIter.func();
408 if (cufIter.ctx()) {
409 if (uintptr_t(cufIter.ctx()) & 1) {
410 cls = (Class*)(uintptr_t(cufIter.ctx()) & ~1);
411 } else {
412 obj = (ObjectData*)cufIter.ctx();
415 assert(!obj || !cls);
416 if (invName) {
417 invName->incRefCount();
419 Variant ret;
420 g_context->invokeFunc((TypedValue*)&ret, f, params, obj, cls,
421 nullptr, invName, ExecutionContext::InvokeCuf);
422 return ret;
425 Variant invoke(const String& function, const Variant& params,
426 strhash_t hash /* = -1 */, bool tryInterp /* = true */,
427 bool fatal /* = true */) {
428 Func* func = Unit::loadFunc(function.get());
429 if (func && (isContainer(params) || params.isNull())) {
430 Variant ret;
431 g_context->invokeFunc(ret.asTypedValue(), func, params);
432 return ret;
434 return invoke_failed(function.c_str(), fatal);
437 Variant invoke(const char *function, const Variant& params, strhash_t hash /* = -1 */,
438 bool tryInterp /* = true */, bool fatal /* = true */) {
439 String funcName(function, CopyString);
440 return invoke(funcName, params, hash, tryInterp, fatal);
443 Variant invoke_static_method(const String& s, const String& method,
444 const Variant& params, bool fatal /* = true */) {
445 HPHP::Class* class_ = Unit::lookupClass(s.get());
446 if (class_ == nullptr) {
447 o_invoke_failed(s.data(), method.data(), fatal);
448 return uninit_null();
450 const HPHP::Func* f = class_->lookupMethod(method.get());
451 if (f == nullptr || !(f->attrs() & AttrStatic) ||
452 (!isContainer(params) && !params.isNull())) {
453 o_invoke_failed(s.data(), method.data(), fatal);
454 return uninit_null();
456 Variant ret;
457 g_context->invokeFunc((TypedValue*)&ret, f, params, nullptr, class_);
458 return ret;
461 Variant invoke_failed(const Variant& func,
462 bool fatal /* = true */) {
463 if (func.isObject()) {
464 return o_invoke_failed(func.toCObjRef()->o_getClassName().c_str(),
465 "__invoke", fatal);
466 } else {
467 return invoke_failed(func.toString().c_str(), fatal);
471 Variant invoke_failed(const char *func,
472 bool fatal /* = true */) {
473 if (fatal) {
474 throw InvalidFunctionCallException(func);
475 } else {
476 raise_warning("call_user_func to non-existent function %s", func);
477 return false;
481 Variant o_invoke_failed(const char *cls, const char *meth,
482 bool fatal /* = true */) {
483 if (fatal) {
484 string msg = "Unknown method ";
485 msg += cls;
486 msg += "::";
487 msg += meth;
488 throw FatalErrorException(msg.c_str());
489 } else {
490 raise_warning("call_user_func to non-existent method %s::%s", cls, meth);
491 return false;
495 void NEVER_INLINE raise_null_object_prop() {
496 raise_notice("Trying to get property of non-object");
499 void NEVER_INLINE throw_null_get_object_prop() {
500 raise_error("Trying to get property of non-object");
503 void NEVER_INLINE throw_null_object_prop() {
504 raise_error("Trying to set property of non-object");
507 void NEVER_INLINE throw_invalid_property_name(const String& name) {
508 if (!name.size()) {
509 throw EmptyObjectPropertyException();
510 } else {
511 throw NullStartObjectPropertyException();
515 void throw_instance_method_fatal(const char *name) {
516 if (!strstr(name, "::__destruct")) {
517 raise_error("Non-static method %s() cannot be called statically", name);
521 void throw_iterator_not_valid() {
522 Object e(SystemLib::AllocInvalidOperationExceptionObject(
523 "Iterator is not valid"));
524 throw e;
527 void throw_collection_modified() {
528 Object e(SystemLib::AllocInvalidOperationExceptionObject(
529 "Collection was modified during iteration"));
530 throw e;
533 void throw_collection_property_exception() {
534 Object e(SystemLib::AllocInvalidOperationExceptionObject(
535 "Cannot access a property on a collection"));
536 throw e;
539 void throw_collection_compare_exception() {
540 static const string msg(
541 "Cannot use relational comparison operators (<, <=, >, >=) to compare "
542 "a collection with an integer, double, string, array, or object");
543 Object e(SystemLib::AllocInvalidOperationExceptionObject(msg));
544 throw e;
547 void throw_param_is_not_container() {
548 static const string msg("Parameter must be an array or collection");
549 Object e(SystemLib::AllocInvalidArgumentExceptionObject(msg));
550 throw e;
553 void throw_cannot_modify_immutable_object(const char* className) {
554 auto msg = folly::format(
555 "Cannot modify immutable object of type {}",
556 className
557 ).str();
558 Object e(SystemLib::AllocInvalidOperationExceptionObject(msg));
559 throw e;
562 void check_collection_compare(ObjectData* obj) {
563 if (obj && obj->isCollection()) throw_collection_compare_exception();
566 void check_collection_compare(ObjectData* obj1, ObjectData* obj2) {
567 if (obj1 && obj2 && (obj1->isCollection() || obj2->isCollection())) {
568 throw_collection_compare_exception();
572 void check_collection_cast_to_array() {
573 if (RuntimeOption::WarnOnCollectionToArray) {
574 raise_warning("Casting a collection to an array is an expensive operation "
575 "and should be avoided where possible. To convert a "
576 "collection to an array without raising a warning, use the "
577 "toArray() method.");
581 Object create_object_only(const String& s) {
582 return g_context->createObjectOnly(s.get());
585 Object create_object(const String& s, const Array& params, bool init /* = true */) {
586 return g_context->createObject(s.get(), params, init);
590 * This function is used when another thread is segfaulting---we just
591 * want to wait forever to give it a chance to write a stacktrace file
592 * (and maybe a core file).
594 void pause_forever() {
595 for (;;) sleep(300);
598 void throw_missing_min_arguments_nr(const char *fn, int expected, int got,
599 int level /* = 0 */,
600 TypedValue *rv /* = nullptr */) {
601 if (rv != nullptr) {
602 rv->m_data.num = 0LL;
603 rv->m_type = KindOfNull;
605 if (level == 2 || RuntimeOption::ThrowMissingArguments) {
606 if (expected == 1) {
607 raise_error(Strings::MISSING_MIN_ARGUMENT, fn, got);
608 } else {
609 raise_error(Strings::MISSING_MIN_ARGUMENTS, fn, expected, got);
611 } else {
612 if (expected == 1) {
613 raise_warning(Strings::MISSING_MIN_ARGUMENT, fn, got);
614 } else {
615 raise_warning(Strings::MISSING_MIN_ARGUMENTS, fn, expected, got);
620 void throw_missing_arguments_nr(const char *fn, int expected, int got,
621 int level /* = 0 */,
622 TypedValue *rv /* = nullptr */) {
623 if (rv != nullptr) {
624 rv->m_data.num = 0LL;
625 rv->m_type = KindOfNull;
627 if (level == 2 || RuntimeOption::ThrowMissingArguments) {
628 if (expected == 1) {
629 raise_error(Strings::MISSING_ARGUMENT, fn, "exactly", got);
630 } else {
631 raise_error(Strings::MISSING_ARGUMENTS, fn, "exactly", expected, got);
633 } else {
634 if (expected == 1) {
635 raise_warning(Strings::MISSING_ARGUMENT, fn, "exactly", got);
636 } else {
637 raise_warning(Strings::MISSING_ARGUMENTS, fn, "exactly", expected, got);
642 void throw_toomany_arguments_nr(const char *fn, int num, int level /* = 0 */,
643 TypedValue *rv /* = nullptr */) {
644 if (rv != nullptr) {
645 rv->m_data.num = 0LL;
646 rv->m_type = KindOfNull;
648 if (level == 2 || RuntimeOption::ThrowTooManyArguments) {
649 raise_error("Too many arguments for %s(), expected %d", fn, num);
650 } else if (level == 1 || RuntimeOption::WarnTooManyArguments) {
651 raise_warning("Too many arguments for %s(), expected %d", fn, num);
655 void throw_wrong_arguments_nr(const char *fn, int count, int cmin, int cmax,
656 int level /* = 0 */,
657 TypedValue *rv /* = nullptr */) {
658 if (rv != nullptr) {
659 rv->m_data.num = 0LL;
660 rv->m_type = KindOfNull;
662 if (cmin >= 0 && count < cmin && cmin != cmax) {
663 throw_missing_min_arguments_nr(fn, cmin, count, level);
664 return;
666 if (cmin >= 0 && count < cmin) {
667 throw_missing_arguments_nr(fn, cmin, count, level);
668 return;
670 if (cmax >= 0 && count > cmax) {
671 throw_toomany_arguments_nr(fn, cmax, level);
672 return;
674 assert(false);
677 void throw_bad_type_exception(const char *fmt, ...) {
678 va_list ap;
679 va_start(ap, fmt);
680 string msg;
681 string_vsnprintf(msg, fmt, ap);
682 va_end(ap);
684 if (RuntimeOption::ThrowBadTypeExceptions) {
685 throw InvalidOperandException(msg.c_str());
688 raise_warning("Invalid operand type was used: %s", msg.c_str());
691 void throw_expected_array_exception() {
692 const char* fn = "(unknown)";
693 ActRec *ar = g_context->getStackFrame();
694 if (ar) {
695 fn = ar->m_func->name()->data();
697 throw_bad_type_exception("%s expects array(s)", fn);
700 void throw_expected_array_or_collection_exception() {
701 const char* fn = "(unknown)";
702 ActRec *ar = g_context->getStackFrame();
703 if (ar) {
704 fn = ar->m_func->name()->data();
706 throw_bad_type_exception("%s expects array(s) or collection(s)", fn);
709 void throw_invalid_argument(const char *fmt, ...) {
710 va_list ap;
711 va_start(ap, fmt);
712 string msg;
713 string_vsnprintf(msg, fmt, ap);
714 va_end(ap);
716 if (RuntimeOption::ThrowInvalidArguments) {
717 throw InvalidArgumentException(msg.c_str());
720 raise_warning("Invalid argument: %s", msg.c_str());
723 Variant throw_fatal_unset_static_property(const char *s, const char *prop) {
724 raise_error("Attempt to unset static property %s::$%s", s, prop);
725 return uninit_null();
728 Exception* generate_request_timeout_exception() {
729 Exception* ret = nullptr;
730 ThreadInfo *info = ThreadInfo::s_threadInfo.getNoCheck();
731 RequestInjectionData &data = info->m_reqInjectionData;
733 bool cli = RuntimeOption::ClientExecutionMode();
734 std::string exceptionMsg = cli ?
735 "Maximum execution time of " :
736 "entire web request took longer than ";
737 exceptionMsg += folly::to<std::string>(data.getTimeout());
738 exceptionMsg += cli ? " seconds exceeded" : " seconds and timed out";
739 Array exceptionStack;
740 if (RuntimeOption::InjectedStackTrace) {
741 exceptionStack = g_context->debugBacktrace(false, true, true);
743 ret = new RequestTimeoutException(exceptionMsg, exceptionStack);
744 return ret;
747 Exception* generate_memory_exceeded_exception() {
748 Array exceptionStack;
749 if (RuntimeOption::InjectedStackTrace) {
750 exceptionStack = g_context->debugBacktrace(false, true, true);
752 return new RequestMemoryExceededException(
753 "request has exceeded memory limit", exceptionStack);
756 Variant unserialize_ex(const char* str, int len,
757 VariableUnserializer::Type type,
758 const Array& class_whitelist /* = null_array */) {
759 if (str == nullptr || len <= 0) {
760 return false;
763 VariableUnserializer vu(str, len, type, true, class_whitelist);
764 Variant v;
765 try {
766 v = vu.unserialize();
767 } catch (FatalErrorException &e) {
768 throw;
769 } catch (Exception &e) {
770 raise_notice("Unable to unserialize: [%s]. %s.", str,
771 e.getMessage().c_str());
772 return false;
774 return v;
777 Variant unserialize_ex(const String& str,
778 VariableUnserializer::Type type,
779 const Array& class_whitelist /* = null_array */) {
780 return unserialize_ex(str.data(), str.size(), type, class_whitelist);
783 String concat3(const String& s1, const String& s2, const String& s3) {
784 StringSlice r1 = s1.slice();
785 StringSlice r2 = s2.slice();
786 StringSlice r3 = s3.slice();
787 int len = r1.len + r2.len + r3.len;
788 StringData* str = StringData::Make(len);
789 auto const r = str->bufferSlice();
790 memcpy(r.ptr, r1.ptr, r1.len);
791 memcpy(r.ptr + r1.len, r2.ptr, r2.len);
792 memcpy(r.ptr + r1.len + r2.len, r3.ptr, r3.len);
793 str->setSize(len);
794 return str;
797 String concat4(const String& s1, const String& s2, const String& s3,
798 const String& s4) {
799 StringSlice r1 = s1.slice();
800 StringSlice r2 = s2.slice();
801 StringSlice r3 = s3.slice();
802 StringSlice r4 = s4.slice();
803 int len = r1.len + r2.len + r3.len + r4.len;
804 StringData* str = StringData::Make(len);
805 auto const r = str->bufferSlice();
806 memcpy(r.ptr, r1.ptr, r1.len);
807 memcpy(r.ptr + r1.len, r2.ptr, r2.len);
808 memcpy(r.ptr + r1.len + r2.len, r3.ptr, r3.len);
809 memcpy(r.ptr + r1.len + r2.len + r3.len, r4.ptr, r4.len);
810 str->setSize(len);
811 return str;
814 Variant include_impl_invoke(const String& file, bool once,
815 const char *currentDir) {
816 if (file[0] == '/') {
817 if (RuntimeOption::SandboxMode || !RuntimeOption::AlwaysUseRelativePath) {
818 try {
819 return invoke_file(file, once, currentDir);
820 } catch(PhpFileDoesNotExistException &e) {}
823 String rel_path(FileUtil::relativePath(RuntimeOption::SourceRoot,
824 string(file.data())));
826 // Don't try/catch - We want the exception to be passed along
827 return invoke_file(rel_path, once, currentDir);
828 } else {
829 // Don't try/catch - We want the exception to be passed along
830 return invoke_file(file, once, currentDir);
834 Variant invoke_file(const String& s, bool once, const char *currentDir) {
835 Variant r;
836 if (invoke_file_impl(r, s, once, currentDir)) {
837 return r;
839 return throw_missing_file(s.c_str());
842 bool invoke_file_impl(Variant &res, const String& path, bool once,
843 const char *currentDir) {
844 bool initial;
845 auto const u = g_context->lookupPhpFile(path.get(),
846 currentDir, &initial);
847 if (u == nullptr) return false;
848 if (!once || initial) {
849 g_context->invokeUnit((TypedValue*)(&res), u);
851 return true;
855 * Used by include_impl. resolve_include() needs some way of checking the
856 * existence of a file path, which for hphpc means attempting to invoke it.
857 * This struct carries some context information needed for the invocation, as
858 * well as a place for the return value of invoking the file.
860 struct IncludeImplInvokeContext {
861 bool once;
862 const char* currentDir;
864 Variant returnValue;
867 static bool include_impl_invoke_context(const String& file, void* ctx) {
868 struct IncludeImplInvokeContext* context = (IncludeImplInvokeContext*)ctx;
869 bool invoked_file = false;
870 try {
871 context->returnValue = include_impl_invoke(file, context->once,
872 context->currentDir);
873 invoked_file = true;
874 } catch (PhpFileDoesNotExistException& e) {
875 context->returnValue = false;
877 return invoked_file;
881 * tryFile is a pointer to a function that resolve_include() will use to
882 * determine if a path references a real file. ctx is a pointer to some context
883 * information that will be passed through to tryFile. (It's a hacky closure)
885 String resolve_include(const String& file, const char* currentDir,
886 bool (*tryFile)(const String& file, void*), void* ctx) {
887 const char* c_file = file.data();
889 if (!File::IsPlainFilePath(file)) {
890 // URIs don't have an include path
891 if (tryFile(file, ctx)) {
892 return file;
895 } else if (c_file[0] == '/') {
896 String can_path = FileUtil::canonicalize(file);
898 if (tryFile(can_path, ctx)) {
899 return can_path;
902 } else if ((c_file[0] == '.' && (c_file[1] == '/' || (
903 c_file[1] == '.' && c_file[2] == '/')))) {
905 String path(String(g_context->getCwd() + "/" + file));
906 String can_path = FileUtil::canonicalize(path);
908 if (tryFile(can_path, ctx)) {
909 return can_path;
912 } else {
913 auto includePaths = ThreadInfo::s_threadInfo.getNoCheck()->
914 m_reqInjectionData.getIncludePaths();
915 unsigned int path_count = includePaths.size();
917 for (int i = 0; i < (int)path_count; i++) {
918 String path("");
919 String includePath(includePaths[i]);
921 if (includePath[0] != '/') {
922 path += (g_context->getCwd() + "/");
925 path += includePath;
927 if (path[path.size() - 1] != '/') {
928 path += "/";
931 path += file;
932 String can_path = FileUtil::canonicalize(path);
934 if (tryFile(can_path, ctx)) {
935 return can_path;
939 if (currentDir[0] == '/') {
940 String path(currentDir);
941 path += "/";
942 path += file;
943 String can_path = FileUtil::canonicalize(path);
945 if (tryFile(can_path, ctx)) {
946 return can_path;
948 } else {
949 String path(g_context->getCwd() + "/" + currentDir + file);
950 String can_path = FileUtil::canonicalize(path);
952 if (tryFile(can_path, ctx)) {
953 return can_path;
958 return String();
961 static Variant include_impl(const String& file, bool once,
962 const char *currentDir, bool required,
963 bool raiseNotice) {
964 struct IncludeImplInvokeContext ctx = {once, currentDir};
965 String can_path = resolve_include(file, currentDir,
966 include_impl_invoke_context, (void*)&ctx);
968 if (can_path.isNull()) {
969 // Failure
970 if (raiseNotice) {
971 raise_notice("Tried to invoke %s but file not found.", file.data());
973 if (required) {
974 String ms = "Required file that does not exist: ";
975 ms += file;
976 throw FatalErrorException(ms.data());
978 return false;
981 return ctx.returnValue;
984 Variant include(const String& file, bool once /* = false */,
985 const char *currentDir /* = NULL */,
986 bool raiseNotice /*= true*/) {
987 return include_impl(file, once, currentDir, false, raiseNotice);
990 Variant require(const String& file, bool once /* = false */,
991 const char *currentDir /* = NULL */,
992 bool raiseNotice /*= true*/) {
993 return include_impl(file, once, currentDir, true, raiseNotice);
996 ///////////////////////////////////////////////////////////////////////////////
997 // class AutoloadHandler
999 IMPLEMENT_REQUEST_LOCAL(AutoloadHandler, AutoloadHandler::s_instance);
1001 void AutoloadHandler::requestInit() {
1002 assert(m_map.get() == nullptr);
1003 assert(m_map_root.get() == nullptr);
1004 assert(m_loading.get() == nullptr);
1005 m_spl_stack_inited = false;
1006 new (&m_handlers) smart::deque<HandlerBundle>();
1009 void AutoloadHandler::requestShutdown() {
1010 m_map.reset();
1011 m_map_root.reset();
1012 m_loading.reset();
1013 // m_spl_stack_inited will be re-initialized by the next requestInit
1014 // m_handlers will be re-initialized by the next requestInit
1017 bool AutoloadHandler::setMap(const Array& map, const String& root) {
1018 this->m_map = map;
1019 this->m_map_root = root;
1020 return true;
1023 class ClassExistsChecker {
1024 public:
1025 ClassExistsChecker() {}
1026 bool operator()(const String& name) const {
1027 return Unit::lookupClass(name.get()) != nullptr;
1031 class ConstantExistsChecker {
1032 public:
1033 bool operator()(const String& name) const {
1034 return Unit::lookupCns(name.get()) != nullptr;
1038 template <class T>
1039 AutoloadHandler::Result AutoloadHandler::loadFromMap(const String& clsName,
1040 const String& kind,
1041 bool toLower,
1042 const T &checkExists) {
1043 assert(!m_map.isNull());
1045 // always normalize name before autoloading
1046 const String& name = normalizeNS(clsName);
1048 while (true) {
1049 const Variant& type_map = m_map.get()->get(kind);
1050 auto const typeMapCell = type_map.asCell();
1051 if (typeMapCell->m_type != KindOfArray) return Failure;
1052 String canonicalName = toLower ? f_strtolower(name) : name;
1053 const Variant& file = typeMapCell->m_data.parr->get(canonicalName);
1054 bool ok = false;
1055 Variant err{Variant::NullInit()};
1056 if (file.isString()) {
1057 String fName = file.toCStrRef().get();
1058 if (fName.get()->data()[0] != '/') {
1059 if (!m_map_root.empty()) {
1060 fName = m_map_root + fName;
1063 try {
1064 VMRegAnchor _;
1065 bool initial;
1066 auto const ec = g_context.getNoCheck();
1067 Unit* u = ec->evalInclude(fName.get(), nullptr, &initial);
1068 if (u) {
1069 if (initial) {
1070 TypedValue retval;
1071 ec->invokeFunc(&retval, u->getMain(), init_null_variant,
1072 nullptr, nullptr, nullptr, nullptr,
1073 ExecutionContext::InvokePseudoMain);
1074 tvRefcountedDecRef(&retval);
1076 ok = true;
1078 } catch (Exception& e) {
1079 err = e.getMessage();
1080 } catch (Object& e) {
1081 err = e;
1082 } catch (...) {
1083 err = String("Unknown Exception");
1086 if (ok && checkExists(name)) {
1087 return Success;
1089 const Variant& func = m_map.get()->get(s_failure);
1090 if (func.isNull()) return Failure;
1091 // can throw, otherwise
1092 // - true means the map was updated. try again
1093 // - false means we should stop applying autoloaders (only affects classes)
1094 // - anything else means keep going
1095 Variant action = vm_call_user_func(func,
1096 make_packed_array(kind, name, err));
1097 auto const actionCell = action.asCell();
1098 if (actionCell->m_type == KindOfBoolean) {
1099 if (actionCell->m_data.num) continue;
1100 return StopAutoloading;
1102 return ContinueAutoloading;
1106 bool AutoloadHandler::autoloadFunc(StringData* name) {
1107 return !m_map.isNull() &&
1108 loadFromMap(name, s_function, true, function_exists) != Failure;
1111 bool AutoloadHandler::autoloadConstant(StringData* name) {
1112 return !m_map.isNull() &&
1113 loadFromMap(name, s_constant, false, ConstantExistsChecker()) != Failure;
1116 bool AutoloadHandler::autoloadType(const String& name) {
1117 return !m_map.isNull() &&
1118 loadFromMap(name, s_type, true,
1119 [] (const String& name) {
1120 return Unit::GetNamedEntity(name.get())->
1121 getCachedTypeAlias() != nullptr;
1123 ) != Failure;
1127 * invokeHandler returns true if any autoload handlers were executed,
1128 * false otherwise. When this function returns true, it is the caller's
1129 * responsibility to check if the given class or interface exists.
1131 bool AutoloadHandler::invokeHandler(const String& clsName,
1132 bool forceSplStack /* = false */) {
1134 if (clsName.empty()) return false;
1136 const String& className = normalizeNS(clsName);
1138 if (!m_map.isNull()) {
1139 ClassExistsChecker ce;
1140 Result res = loadFromMap(className, s_class, true, ce);
1141 if (res == ContinueAutoloading) {
1142 if (ce(className)) return true;
1143 } else {
1144 if (res != Failure) return res == Success;
1147 // If we end up in a recursive autoload loop where we try to load the
1148 // same class twice, just fail the load to mimic PHP as many frameworks
1149 // rely on it unless we are forcing a restart (due to spl_autoload_call)
1150 // in which case autoload is allowed to be reentrant.
1151 if (!forceSplStack) {
1152 if (m_loading.exists(className)) { return false; }
1153 m_loading.add(className, className);
1154 } else {
1155 // We can still overflow the stack if there is a loop when using
1156 // spl_autoload_call directly, but this behavior matches the reference
1157 // implementation.
1158 m_loading.append(className);
1161 // Make sure state is cleaned up from this load; autoloading of arbitrary
1162 // code below can throw
1163 SCOPE_EXIT {
1164 String l_className = m_loading.pop();
1165 assert(l_className == className);
1168 Array params = PackedArrayInit(1).append(className).toArray();
1169 if (!m_spl_stack_inited && !forceSplStack) {
1170 if (function_exists(s___autoload)) {
1171 invoke(s___autoload, params, -1, true, false);
1172 return true;
1174 return false;
1176 if (!m_spl_stack_inited || m_handlers.empty()) {
1177 return false;
1179 Object autoloadException;
1180 for (const HandlerBundle& hb : m_handlers) {
1181 try {
1182 vm_call_user_func_cufiter(*hb.m_cufIter, params);
1183 } catch (Object& ex) {
1184 assert(ex.instanceof(SystemLib::s_ExceptionClass));
1185 if (autoloadException.isNull()) {
1186 autoloadException = ex;
1187 } else {
1188 Object cur = ex;
1189 Variant next = cur->o_get(s_previous, false, s_exception);
1190 while (next.isObject()) {
1191 cur = next.toObject();
1192 next = cur->o_get(s_previous, false, s_exception);
1194 cur->o_set(s_previous, autoloadException, s_exception);
1195 autoloadException = ex;
1198 if (Unit::lookupClass(className.get()) != nullptr) {
1199 break;
1202 if (!autoloadException.isNull()) {
1203 throw autoloadException;
1205 return true;
1208 Array AutoloadHandler::getHandlers() {
1209 if (!m_spl_stack_inited) {
1210 return Array();
1213 PackedArrayInit handlers(m_handlers.size());
1215 for (const HandlerBundle& hb : m_handlers) {
1216 handlers.append(hb.m_handler);
1219 return handlers.toArray();
1222 bool AutoloadHandler::CompareBundles::operator()(
1223 const HandlerBundle& hb) {
1224 auto const& lhs = *m_cufIter;
1225 auto const& rhs = *hb.m_cufIter;
1227 if (lhs.ctx() != rhs.ctx()) {
1228 // We only consider ObjectData* for equality (not a Class*) so if either is
1229 // an object these are not considered equal.
1230 if (!(uintptr_t(lhs.ctx()) & 1) || !(uintptr_t(rhs.ctx()) & 1)) {
1231 return false;
1235 return lhs.func() == rhs.func();
1238 bool AutoloadHandler::addHandler(const Variant& handler, bool prepend) {
1239 SmartCufIterPtr cufIter = nullptr;
1240 if (!vm_decode_function_cufiter(handler, cufIter)) {
1241 return false;
1244 m_spl_stack_inited = true;
1246 // Zend doesn't modify the order of the list if the handler is already
1247 // registered.
1248 auto const& compareBundles = CompareBundles(cufIter.get());
1249 if (std::find_if(m_handlers.begin(), m_handlers.end(), compareBundles) !=
1250 m_handlers.end()) {
1251 return true;
1254 if (!prepend) {
1255 m_handlers.emplace_back(handler, cufIter);
1256 } else {
1257 m_handlers.emplace_front(handler, cufIter);
1260 return true;
1263 bool AutoloadHandler::isRunning() {
1264 return !m_loading.empty();
1267 void AutoloadHandler::removeHandler(const Variant& handler) {
1268 SmartCufIterPtr cufIter = nullptr;
1269 if (!vm_decode_function_cufiter(handler, cufIter)) {
1270 return;
1273 // Use find_if instead of remove_if since we know there can only be one match
1274 // in the vector.
1275 auto const& compareBundles = CompareBundles(cufIter.get());
1276 auto it = std::find_if(m_handlers.begin(), m_handlers.end(), compareBundles);
1277 if (it != m_handlers.end()) {
1278 m_handlers.erase(it);
1282 void AutoloadHandler::removeAllHandlers() {
1283 m_spl_stack_inited = false;
1284 m_handlers.clear();
1287 bool function_exists(const String& function_name) {
1288 auto f = HPHP::Unit::lookupFunc(function_name.get());
1289 return (f != nullptr) &&
1290 (f->builtinFuncPtr() != Native::unimplementedWrapper);
1293 ///////////////////////////////////////////////////////////////////////////////
1294 // debugger and code coverage instrumentation
1296 void throw_exception(const Object& e) {
1297 if (!e.instanceof(SystemLib::s_ExceptionClass)) {
1298 raise_error("Exceptions must be valid objects derived from the "
1299 "Exception base class");
1301 DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionThrownHook(e.get()));
1302 throw e;
1305 ///////////////////////////////////////////////////////////////////////////////