make PHP_SAPI dynamic based on execution mode
[hiphop-php.git] / hphp / runtime / base / builtin_functions.cpp
blob61d8e3b345df2cba7f98c8879a3b33773d16166e
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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_class.h"
31 #include "hphp/runtime/ext/ext_function.h"
32 #include "hphp/runtime/ext/ext_file.h"
33 #include "hphp/runtime/ext/ext_collections.h"
34 #include "hphp/util/logger.h"
35 #include "hphp/util/util.h"
36 #include "hphp/util/process.h"
37 #include "hphp/runtime/vm/repo.h"
38 #include "hphp/runtime/vm/jit/translator.h"
39 #include "hphp/runtime/vm/jit/translator-inline.h"
40 #include "hphp/runtime/vm/unit.h"
41 #include "hphp/runtime/vm/event_hook.h"
42 #include "hphp/system/lib/systemlib.h"
44 #include <limits>
46 using namespace HPHP::MethodLookup;
48 namespace HPHP {
49 ///////////////////////////////////////////////////////////////////////////////
50 // static strings
52 const StaticString s_offsetExists("offsetExists");
53 const StaticString s___autoload("__autoload");
54 const StaticString s___call("__call");
55 const StaticString s___callStatic("__callStatic");
56 const StaticString s_exception("exception");
57 const StaticString s_previous("previous");
59 const StaticString s_self("self");
60 const StaticString s_parent("parent");
61 const StaticString s_static("static");
62 const StaticString s_class("class");
63 const StaticString s_function("function");
64 const StaticString s_constant("constant");
65 const StaticString s_type("type");
66 const StaticString s_failure("failure");
68 const StaticString s_Traversable("Traversable");
69 const StaticString s_KeyedTraversable("KeyedTraversable");
70 const StaticString s_Indexish("Indexish");
72 ///////////////////////////////////////////////////////////////////////////////
74 bool array_is_valid_callback(CArrRef arr) {
75 if (arr.size() != 2 || !arr.exists(int64_t(0)) || !arr.exists(int64_t(1))) {
76 return false;
78 Variant elem0 = arr.rvalAt(int64_t(0));
79 if (!elem0.isString() && !elem0.isObject()) {
80 return false;
82 Variant elem1 = arr.rvalAt(int64_t(1));
83 if (!elem1.isString()) {
84 return false;
86 return true;
89 const HPHP::Func*
90 vm_decode_function(CVarRef function,
91 ActRec* ar,
92 bool forwarding,
93 ObjectData*& this_,
94 HPHP::Class*& cls,
95 StringData*& invName,
96 bool warn /* = true */) {
97 invName = nullptr;
98 if (function.isString() || function.isArray()) {
99 HPHP::Class* ctx = nullptr;
100 if (ar) ctx = arGetContextClass(ar);
101 // Decode the 'function' parameter into this_, cls, name, pos, and
102 // nameContainsClass.
103 this_ = nullptr;
104 cls = nullptr;
105 String name;
106 int pos = String::npos;
107 bool nameContainsClass = false;
108 if (function.isString()) {
109 // If 'function' is a string we simply assign it to name and
110 // leave this_ and cls set to NULL.
111 name = function.toString();
112 pos = name.find("::");
113 nameContainsClass =
114 (pos != 0 && pos != String::npos && pos + 2 < name.size());
115 } else {
116 // If 'function' is an array with exactly two indices 0 and 1, we
117 // assign the value at index 1 to name and we use the value at index
118 // 0 to populate cls (if the value is a string) or this_ and cls (if
119 // the value is an object).
120 assert(function.isArray());
121 Array arr = function.toArray();
122 if (!array_is_valid_callback(arr)) {
123 if (warn) {
124 throw_invalid_argument("function: not a valid callback array");
126 return nullptr;
128 Variant elem1 = arr.rvalAt(int64_t(1));
129 name = elem1.toString();
130 pos = name.find("::");
131 nameContainsClass =
132 (pos != 0 && pos != String::npos && pos + 2 < name.size());
133 Variant elem0 = arr.rvalAt(int64_t(0));
134 if (elem0.isString()) {
135 String sclass = elem0.toString();
136 if (sclass->isame(s_self.get())) {
137 if (ctx) {
138 cls = ctx;
140 if (!nameContainsClass) {
141 forwarding = true;
143 } else if (sclass->isame(s_parent.get())) {
144 if (ctx && ctx->parent()) {
145 cls = ctx->parent();
147 if (!nameContainsClass) {
148 forwarding = true;
150 } else if (sclass->isame(s_static.get())) {
151 if (ar) {
152 if (ar->hasThis()) {
153 cls = ar->getThis()->getVMClass();
154 } else if (ar->hasClass()) {
155 cls = ar->getClass();
158 } else {
159 if (warn && nameContainsClass) {
160 String nameClass = name.substr(0, pos);
161 if (nameClass->isame(s_self.get()) ||
162 nameClass->isame(s_parent.get()) ||
163 nameClass->isame(s_static.get())) {
164 raise_warning("behavior of call_user_func(array('%s', '%s')) "
165 "is undefined", sclass->data(), name->data());
168 cls = Unit::loadClass(sclass.get());
170 if (!cls) {
171 if (warn) {
172 throw_invalid_argument("function: class not found");
174 return nullptr;
176 } else {
177 assert(elem0.isObject());
178 this_ = elem0.getObjectData();
179 cls = this_->getVMClass();
183 HPHP::Class* cc = cls;
184 if (nameContainsClass) {
185 String c = name.substr(0, pos);
186 name = name.substr(pos + 2);
187 if (c->isame(s_self.get())) {
188 if (cls) {
189 cc = cls;
190 } else if (ctx) {
191 cc = ctx;
193 if (!this_) {
194 forwarding = true;
196 } else if (c->isame(s_parent.get())) {
197 if (cls) {
198 cc = cls->parent();
199 } else if (ctx && ctx->parent()) {
200 cc = ctx->parent();
202 if (!this_) {
203 forwarding = true;
205 } else if (c->isame(s_static.get())) {
206 if (ar) {
207 if (ar->hasThis()) {
208 cc = ar->getThis()->getVMClass();
209 } else if (ar->hasClass()) {
210 cc = ar->getClass();
213 } else {
214 cc = Unit::loadClass(c.get());
216 if (!cc) {
217 if (warn) {
218 throw_invalid_argument("function: class not found");
220 return nullptr;
222 if (cls) {
223 if (!cls->classof(cc)) {
224 if (warn) {
225 raise_warning("call_user_func expects parameter 1 to be a valid "
226 "callback, class '%s' is not a subclass of '%s'",
227 cls->preClass()->name()->data(),
228 cc->preClass()->name()->data());
230 return nullptr;
233 // If there is not a current instance, cc trumps cls.
234 if (!this_) {
235 cls = cc;
238 if (!cls) {
239 HPHP::Func* f = HPHP::Unit::loadFunc(name.get());
240 if (!f) {
241 if (warn) {
242 throw_invalid_argument("function: method '%s' not found",
243 name->data());
245 return nullptr;
247 assert(f && f->preClass() == nullptr);
248 return f;
250 assert(cls);
251 CallType lookupType = this_ ? ObjMethod : ClsMethod;
252 const HPHP::Func* f =
253 g_vmContext->lookupMethodCtx(cc, name.get(), ctx, lookupType);
254 if (f && (f->attrs() & AttrStatic)) {
255 // If we found a method and its static, null out this_
256 this_ = nullptr;
257 } else {
258 if (!this_ && ar) {
259 // If we did not find a static method AND this_ is null AND there is a
260 // frame ar, check if the current instance from ar is compatible
261 ObjectData* obj = ar->hasThis() ? ar->getThis() : nullptr;
262 if (obj && obj->instanceof(cls)) {
263 this_ = obj;
264 cls = obj->getVMClass();
267 if (!f) {
268 if (this_) {
269 // If this_ is non-null AND we could not find a method, try
270 // looking up __call in cls's method table
271 f = cls->lookupMethod(s___call.get());
272 assert(!f || !(f->attrs() & AttrStatic));
274 if (!f && lookupType == ClsMethod) {
275 f = cls->lookupMethod(s___callStatic.get());
276 assert(!f || (f->attrs() & AttrStatic));
277 this_ = nullptr;
279 if (f) {
280 // We found __call or __callStatic!
281 // Stash the original name into invName.
282 invName = name.get();
283 invName->incRefCount();
284 } else {
285 // Bail out if we couldn't find the method or __call
286 if (warn) {
287 throw_invalid_argument("function: method '%s' not found",
288 name->data());
290 return nullptr;
294 assert(f && f->preClass());
295 // If this_ is non-NULL, then this_ is the current instance and cls is
296 // the class of the current instance.
297 assert(!this_ || this_->getVMClass() == cls);
298 // If we are doing a forwarding call and this_ is null, set cls
299 // appropriately to propagate the current late bound class.
300 if (!this_ && forwarding && ar) {
301 HPHP::Class* fwdCls = nullptr;
302 ObjectData* obj = ar->hasThis() ? ar->getThis() : nullptr;
303 if (obj) {
304 fwdCls = obj->getVMClass();
305 } else if (ar->hasClass()) {
306 fwdCls = ar->getClass();
308 // Only forward the current late bound class if it is the same or
309 // a descendent of cls
310 if (fwdCls && fwdCls->classof(cls)) {
311 cls = fwdCls;
314 return f;
316 if (function.isObject()) {
317 static StringData* invokeStr = StringData::GetStaticString("__invoke");
318 this_ = function.asCObjRef().get();
319 cls = nullptr;
320 const HPHP::Func *f = this_->getVMClass()->lookupMethod(invokeStr);
321 if (f != nullptr &&
322 ((f->attrs() & AttrStatic) && !f->isClosureBody())) {
323 // If __invoke is static, invoke it as such
324 cls = this_->getVMClass();
325 this_ = nullptr;
327 return f;
329 if (warn) {
330 throw_invalid_argument("function: not string, closure, or array");
332 return nullptr;
335 Variant vm_call_user_func(CVarRef function, CArrRef params,
336 bool forwarding /* = false */) {
337 ObjectData* obj = nullptr;
338 HPHP::Class* cls = nullptr;
339 HPHP::Transl::CallerFrame cf;
340 StringData* invName = nullptr;
341 const HPHP::Func* f = vm_decode_function(function, cf(), forwarding,
342 obj, cls, invName);
343 if (f == nullptr) {
344 return uninit_null();
346 Variant ret;
347 g_vmContext->invokeFunc((TypedValue*)&ret, f, params, obj, cls,
348 nullptr, invName);
349 return ret;
352 Variant invoke(CStrRef function, CArrRef params, strhash_t hash /* = -1 */,
353 bool tryInterp /* = true */, bool fatal /* = true */) {
354 Func* func = Unit::loadFunc(function.get());
355 if (func) {
356 Variant ret;
357 g_vmContext->invokeFunc(ret.asTypedValue(), func, params);
358 return ret;
360 return invoke_failed(function.c_str(), params, fatal);
363 Variant invoke(const char *function, CArrRef params, strhash_t hash /* = -1*/,
364 bool tryInterp /* = true */, bool fatal /* = true */) {
365 String funcName(function, CopyString);
366 return invoke(funcName, params, hash, tryInterp, fatal);
369 Variant invoke_static_method(CStrRef s, CStrRef method, CArrRef params,
370 bool fatal /* = true */) {
371 HPHP::Class* class_ = Unit::lookupClass(s.get());
372 if (class_ == nullptr) {
373 o_invoke_failed(s.data(), method.data(), fatal);
374 return uninit_null();
376 const HPHP::Func* f = class_->lookupMethod(method.get());
377 if (f == nullptr || !(f->attrs() & AttrStatic)) {
378 o_invoke_failed(s.data(), method.data(), fatal);
379 return uninit_null();
381 Variant ret;
382 g_vmContext->invokeFunc((TypedValue*)&ret, f, params, nullptr, class_);
383 return ret;
386 Variant invoke_failed(CVarRef func, CArrRef params,
387 bool fatal /* = true */) {
388 if (func.isObject()) {
389 return o_invoke_failed(
390 func.objectForCall()->o_getClassName().c_str(),
391 "__invoke", fatal);
392 } else {
393 return invoke_failed(func.toString().c_str(), params, fatal);
397 Variant invoke_failed(const char *func, CArrRef params,
398 bool fatal /* = true */) {
399 if (fatal) {
400 throw InvalidFunctionCallException(func);
401 } else {
402 raise_warning("call_user_func to non-existent function %s", func);
403 return false;
407 Variant o_invoke_failed(const char *cls, const char *meth,
408 bool fatal /* = true */) {
409 if (fatal) {
410 string msg = "Unknown method ";
411 msg += cls;
412 msg += "::";
413 msg += meth;
414 throw FatalErrorException(msg.c_str());
415 } else {
416 raise_warning("call_user_func to non-existent method %s::%s", cls, meth);
417 return false;
421 void NEVER_INLINE raise_null_object_prop() {
422 raise_notice("Trying to get property of non-object");
425 void NEVER_INLINE throw_null_get_object_prop() {
426 raise_error("Trying to get property of non-object");
429 void NEVER_INLINE throw_null_object_prop() {
430 raise_error("Trying to set property of non-object");
433 void NEVER_INLINE throw_invalid_property_name(CStrRef name) {
434 if (!name.size()) {
435 throw EmptyObjectPropertyException();
436 } else {
437 throw NullStartObjectPropertyException();
441 void throw_instance_method_fatal(const char *name) {
442 if (!strstr(name, "::__destruct")) {
443 raise_error("Non-static method %s() cannot be called statically", name);
447 void throw_iterator_not_valid() {
448 Object e(SystemLib::AllocInvalidOperationExceptionObject(
449 "Iterator is not valid"));
450 throw e;
453 void throw_collection_modified() {
454 Object e(SystemLib::AllocInvalidOperationExceptionObject(
455 "Collection was modified during iteration"));
456 throw e;
459 void throw_collection_property_exception() {
460 Object e(SystemLib::AllocInvalidOperationExceptionObject(
461 "Cannot access a property on a collection"));
462 throw e;
465 void throw_collection_compare_exception() {
466 static const string msg(
467 "Cannot use relational comparison operators (<, <=, >, >=) to compare "
468 "a collection with an integer, double, string, array, or object");
469 if (RuntimeOption::StrictCollections) {
470 Object e(SystemLib::AllocRuntimeExceptionObject(msg));
471 throw e;
473 raise_warning(msg);
476 void check_collection_compare(ObjectData* obj) {
477 if (obj && obj->isCollection()) throw_collection_compare_exception();
480 void check_collection_compare(ObjectData* obj1, ObjectData* obj2) {
481 if (obj1 && obj2 && (obj1->isCollection() || obj2->isCollection())) {
482 throw_collection_compare_exception();
486 void check_collection_cast_to_array() {
487 if (RuntimeOption::WarnOnCollectionToArray) {
488 raise_warning("Casting a collection to an array is an expensive operation "
489 "and should be avoided where possible. To convert a "
490 "collection to an array without raising a warning, use the "
491 "toArray() method.");
495 Object create_object_only(CStrRef s) {
496 return g_vmContext->createObjectOnly(s.get());
499 Object create_object(CStrRef s, CArrRef params, bool init /* = true */) {
500 return g_vmContext->createObject(s.get(), params, init);
504 * This function is used when another thread is segfaulting---we just
505 * want to wait forever to give it a chance to write a stacktrace file
506 * (and maybe a core file).
508 void pause_forever() {
509 for (;;) sleep(300);
512 ssize_t check_request_surprise(ThreadInfo *info) {
513 RequestInjectionData &p = info->m_reqInjectionData;
514 bool do_timedout, do_memExceeded, do_signaled;
516 ssize_t flags = p.fetchAndClearFlags();
517 do_timedout = (flags & RequestInjectionData::TimedOutFlag) &&
518 !p.getDebugger();
519 do_memExceeded = (flags & RequestInjectionData::MemExceededFlag);
520 do_signaled = (flags & RequestInjectionData::SignaledFlag);
522 // Start with any pending exception that might be on the thread.
523 Exception* pendingException = info->m_pendingException;
524 info->m_pendingException = nullptr;
526 if (do_timedout && !pendingException) {
527 pendingException = generate_request_timeout_exception();
529 if (do_memExceeded && !pendingException) {
530 pendingException = generate_memory_exceeded_exception();
532 if (do_signaled) f_pcntl_signal_dispatch();
534 if (pendingException) {
535 pendingException->throwException();
537 return flags;
540 void throw_missing_arguments_nr(const char *fn, int expected, int got,
541 int level /* = 0 */) {
542 if (level == 2 || RuntimeOption::ThrowMissingArguments) {
543 if (expected == 1) {
544 raise_error(Strings::MISSING_ARGUMENT, fn, got);
545 } else {
546 raise_error(Strings::MISSING_ARGUMENTS, fn, expected, got);
548 } else {
549 if (expected == 1) {
550 raise_warning(Strings::MISSING_ARGUMENT, fn, got);
551 } else {
552 raise_warning(Strings::MISSING_ARGUMENTS, fn, expected, got);
557 void throw_toomany_arguments_nr(const char *fn, int num, int level /* = 0 */) {
558 if (level == 2 || RuntimeOption::ThrowTooManyArguments) {
559 raise_error("Too many arguments for %s(), expected %d", fn, num);
560 } else if (level == 1 || RuntimeOption::WarnTooManyArguments) {
561 raise_warning("Too many arguments for %s(), expected %d", fn, num);
565 void throw_wrong_arguments_nr(const char *fn, int count, int cmin, int cmax,
566 int level /* = 0 */) {
567 if (cmin >= 0 && count < cmin) {
568 throw_missing_arguments_nr(fn, cmin, count, level);
569 return;
571 if (cmax >= 0 && count > cmax) {
572 throw_toomany_arguments_nr(fn, cmax, level);
573 return;
575 assert(false);
578 void throw_bad_type_exception(const char *fmt, ...) {
579 va_list ap;
580 va_start(ap, fmt);
581 string msg;
582 Util::string_vsnprintf(msg, fmt, ap);
583 va_end(ap);
585 if (RuntimeOption::ThrowBadTypeExceptions) {
586 throw InvalidOperandException(msg.c_str());
589 raise_warning("Invalid operand type was used: %s", msg.c_str());
592 void throw_bad_array_exception() {
593 const char* fn = "(unknown)";
594 ActRec *ar = g_vmContext->getStackFrame();
595 if (ar) {
596 fn = ar->m_func->name()->data();
598 throw_bad_type_exception("%s expects array(s)", fn);
601 void throw_invalid_argument(const char *fmt, ...) {
602 va_list ap;
603 va_start(ap, fmt);
604 string msg;
605 Util::string_vsnprintf(msg, fmt, ap);
606 va_end(ap);
608 if (RuntimeOption::ThrowInvalidArguments) {
609 throw InvalidArgumentException(msg.c_str());
612 raise_warning("Invalid argument: %s", msg.c_str());
615 Variant throw_fatal_unset_static_property(const char *s, const char *prop) {
616 raise_error("Attempt to unset static property %s::$%s", s, prop);
617 return uninit_null();
620 void throw_infinite_recursion_exception() {
621 if (!RuntimeOption::NoInfiniteRecursionDetection) {
622 // Reset profiler otherwise it might recurse further causing segfault
623 DECLARE_THREAD_INFO
624 info->m_profiler = nullptr;
625 throw UncatchableException("infinite recursion detected");
628 Exception* generate_request_timeout_exception() {
629 Exception* ret = nullptr;
630 ThreadInfo *info = ThreadInfo::s_threadInfo.getNoCheck();
631 RequestInjectionData &data = info->m_reqInjectionData;
632 if (data.timeoutSeconds > 0) {
633 // This extra checking is needed, because there may be a race condition
634 // a TimeoutThread sets flag "true" right after an old request finishes and
635 // right before a new requets resets "started". In this case, we flag
636 // "timedout" back to "false".
637 if (time(0) - data.started >= data.timeoutSeconds) {
638 std::string exceptionMsg = "entire web request took longer than ";
639 exceptionMsg += boost::lexical_cast<std::string>(data.timeoutSeconds);
640 exceptionMsg += " seconds and timed out";
641 ArrayHolder exceptionStack;
642 if (RuntimeOption::InjectedStackTrace) {
643 exceptionStack = g_vmContext->debugBacktrace(false, true, true).get();
645 ret = new FatalErrorException(exceptionMsg, exceptionStack.get());
648 return ret;
651 Exception* generate_memory_exceeded_exception() {
652 ArrayHolder exceptionStack;
653 if (RuntimeOption::InjectedStackTrace) {
654 exceptionStack = g_vmContext->debugBacktrace(false, true, true).get();
656 return new FatalErrorException(
657 "request has exceeded memory limit", exceptionStack.get());
660 void throw_call_non_object() {
661 throw_call_non_object(nullptr);
664 void throw_call_non_object(const char *methodName) {
665 std::string msg;
667 if (methodName == nullptr) {
668 msg = "Call to a member function on a non-object";
669 } else {
670 Util::string_printf(msg,
671 "Call to a member function %s() on a non-object",
672 methodName);
675 if (RuntimeOption::ThrowExceptionOnBadMethodCall) {
676 Object e(SystemLib::AllocBadMethodCallExceptionObject(String(msg)));
677 throw e;
680 throw FatalErrorException(msg.c_str());
683 void throw_unexpected_argument_type(int argNum, const char *fnName,
684 const char *expected, CVarRef val) {
685 const char *otype = nullptr;
686 switch (val.getType()) {
687 case KindOfUninit:
688 case KindOfNull: otype = "null"; break;
689 case KindOfBoolean: otype = "bool"; break;
690 case KindOfInt64: otype = "int"; break;
691 case KindOfDouble: otype = "double"; break;
692 case KindOfStaticString:
693 case KindOfString: otype = "string"; break;
694 case KindOfArray: otype = "array"; break;
695 case KindOfObject:
696 otype = val.getObjectData()->o_getClassName().c_str();
697 break;
698 default:
699 assert(false);
701 raise_recoverable_error
702 ("Argument %d passed to %s must be an instance of %s, %s given",
703 argNum, fnName, expected, otype);
706 Object f_clone(CVarRef v) {
707 if (v.isObject()) {
708 Object clone = Object(v.toObject()->clone());
709 clone->t___clone();
710 return clone;
712 raise_error("Cannot clone non-object");
713 return Object();
716 String f_serialize(CVarRef value) {
717 switch (value.getType()) {
718 case KindOfUninit:
719 case KindOfNull:
720 return "N;";
721 case KindOfBoolean:
722 return value.getBoolean() ? "b:1;" : "b:0;";
723 case KindOfInt64: {
724 StringBuffer sb;
725 sb.append("i:");
726 sb.append(value.getInt64());
727 sb.append(';');
728 return sb.detach();
730 case KindOfStaticString:
731 case KindOfString: {
732 StringData *str = value.getStringData();
733 StringBuffer sb;
734 sb.append("s:");
735 sb.append(str->size());
736 sb.append(":\"");
737 sb.append(str->data(), str->size());
738 sb.append("\";");
739 return sb.detach();
741 case KindOfArray: {
742 ArrayData *arr = value.getArrayData();
743 if (arr->empty()) return "a:0:{}";
744 // fall-through
746 case KindOfObject:
747 case KindOfDouble: {
748 VariableSerializer vs(VariableSerializer::Serialize);
749 return vs.serialize(value, true);
751 default:
752 assert(false);
753 break;
755 return "";
758 Variant unserialize_ex(const char* str, int len,
759 VariableUnserializer::Type type,
760 CArrRef class_whitelist /* = null_array */) {
761 if (str == nullptr || len <= 0) {
762 return false;
765 VariableUnserializer vu(str, len, type, false, class_whitelist);
766 Variant v;
767 try {
768 v = vu.unserialize();
769 } catch (FatalErrorException &e) {
770 throw;
771 } catch (Exception &e) {
772 raise_notice("Unable to unserialize: [%s]. %s.", str,
773 e.getMessage().c_str());
774 return false;
776 return v;
779 Variant unserialize_ex(CStrRef str,
780 VariableUnserializer::Type type,
781 CArrRef class_whitelist /* = null_array */) {
782 return unserialize_ex(str.data(), str.size(), type, class_whitelist);
785 String concat3(CStrRef s1, CStrRef s2, CStrRef s3) {
786 StringSlice r1 = s1.slice();
787 StringSlice r2 = s2.slice();
788 StringSlice r3 = s3.slice();
789 int len = r1.len + r2.len + r3.len;
790 StringData* str = NEW(StringData)(len);
791 MutableSlice r = str->mutableSlice();
792 memcpy(r.ptr, r1.ptr, r1.len);
793 memcpy(r.ptr + r1.len, r2.ptr, r2.len);
794 memcpy(r.ptr + r1.len + r2.len, r3.ptr, r3.len);
795 str->setSize(len);
796 return str;
799 String concat4(CStrRef s1, CStrRef s2, CStrRef s3, CStrRef s4) {
800 StringSlice r1 = s1.slice();
801 StringSlice r2 = s2.slice();
802 StringSlice r3 = s3.slice();
803 StringSlice r4 = s4.slice();
804 int len = r1.len + r2.len + r3.len + r4.len;
805 StringData* str = NEW(StringData)(len);
806 MutableSlice r = str->mutableSlice();
807 memcpy(r.ptr, r1.ptr, r1.len);
808 memcpy(r.ptr + r1.len, r2.ptr, r2.len);
809 memcpy(r.ptr + r1.len + r2.len, r3.ptr, r3.len);
810 memcpy(r.ptr + r1.len + r2.len + r3.len, r4.ptr, r4.len);
811 str->setSize(len);
812 return str;
815 String concat5(CStrRef s1, CStrRef s2, CStrRef s3, CStrRef s4, CStrRef s5) {
816 int len1 = s1.size();
817 int len2 = s2.size();
818 int len3 = s3.size();
819 int len4 = s4.size();
820 int len5 = s5.size();
821 int len = len1 + len2 + len3 + len4 + len5;
822 String s = String(len, ReserveString);
823 char *buf = s.mutableSlice().ptr;
824 memcpy(buf, s1.data(), len1);
825 memcpy(buf + len1, s2.data(), len2);
826 memcpy(buf + len1 + len2, s3.data(), len3);
827 memcpy(buf + len1 + len2 + len3, s4.data(), len4);
828 memcpy(buf + len1 + len2 + len3 + len4, s5.data(), len5);
829 return s.setSize(len);
832 String concat6(CStrRef s1, CStrRef s2, CStrRef s3, CStrRef s4, CStrRef s5,
833 CStrRef s6) {
834 int len1 = s1.size();
835 int len2 = s2.size();
836 int len3 = s3.size();
837 int len4 = s4.size();
838 int len5 = s5.size();
839 int len6 = s6.size();
840 int len = len1 + len2 + len3 + len4 + len5 + len6;
841 String s = String(len, ReserveString);
842 char *buf = s.mutableSlice().ptr;
843 memcpy(buf, s1.data(), len1);
844 memcpy(buf + len1, s2.data(), len2);
845 memcpy(buf + len1 + len2, s3.data(), len3);
846 memcpy(buf + len1 + len2 + len3, s4.data(), len4);
847 memcpy(buf + len1 + len2 + len3 + len4, s5.data(), len5);
848 memcpy(buf + len1 + len2 + len3 + len4 + len5, s6.data(), len6);
849 return s.setSize(len);
852 bool empty(CVarRef v, bool offset) {
853 return empty(v, Variant(offset));
855 bool empty(CVarRef v, int64_t offset) {
856 Variant::TypedValueAccessor tva = v.getTypedAccessor();
857 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
858 return empty(Variant::GetArrayData(tva)->get(offset));
860 return empty(v, VarNR(offset));
862 bool empty(CVarRef v, double offset) {
863 return empty(v, VarNR(offset));
865 bool empty(CVarRef v, CArrRef offset) {
866 return empty(v, VarNR(offset));
868 bool empty(CVarRef v, CObjRef offset) {
869 return empty(v, VarNR(offset));
872 bool empty(CVarRef v, CStrRef offset, bool isString /* = false */) {
873 Variant::TypedValueAccessor tva = v.getTypedAccessor();
874 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
875 return empty(Variant::GetAsArray(tva).
876 rvalAtRef(offset, AccessFlags::IsKey(isString)));
878 return empty(v, VarNR(offset));
881 bool empty(CVarRef v, CVarRef offset) {
882 Variant::TypedValueAccessor tva = v.getTypedAccessor();
883 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
884 return empty(Variant::GetAsArray(tva).rvalAtRef(offset));
886 if (Variant::GetAccessorType(tva) == KindOfObject) {
887 ObjectData* obj = Variant::GetObjectData(tva);
888 if (obj->isCollection()) {
889 return collectionOffsetEmpty(obj, offset);
890 } else {
891 if (!Variant::GetArrayAccess(tva)->
892 o_invoke_few_args(s_offsetExists, 1, offset)) {
893 return true;
895 return empty(v.rvalAt(offset));
897 } else if (Variant::IsString(tva)) {
898 uint64_t pos = offset.toInt64();
899 if (pos >= (uint64_t)Variant::GetStringData(tva)->size()) {
900 return true;
903 return empty(v.rvalAt(offset));
906 bool isset(CArrRef v, int64_t offset) {
907 return isset(v.rvalAtRef(offset));
909 bool isset(CArrRef v, CArrRef offset) {
910 return isset(v, VarNR(offset));
912 bool isset(CArrRef v, CObjRef offset) {
913 return isset(v, VarNR(offset));
915 bool isset(CArrRef v, CStrRef offset, bool isString /* = false */) {
916 return isset(v.rvalAtRef(offset, AccessFlags::IsKey(isString)));
918 bool isset(CArrRef v, CVarRef offset) {
919 return isset(v.rvalAtRef(offset));
922 bool isset(CVarRef v, bool offset) {
923 return isset(v, VarNR(offset));
925 bool isset(CVarRef v, int64_t offset) {
926 Variant::TypedValueAccessor tva = v.getTypedAccessor();
927 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
928 return isset(Variant::GetArrayData(tva)->get(offset));
930 if (Variant::GetAccessorType(tva) == KindOfObject) {
931 ObjectData* obj = Variant::GetObjectData(tva);
932 if (obj->isCollection()) {
933 return collectionOffsetIsset(obj, offset);
934 } else {
935 return Variant::GetArrayAccess(tva)->
936 o_invoke_few_args(s_offsetExists, 1, offset);
939 if (Variant::IsString(tva)) {
940 return (uint64_t)offset < (uint64_t)Variant::GetStringData(tva)->size();
942 return false;
944 bool isset(CVarRef v, double offset) {
945 return isset(v, VarNR(offset));
947 bool isset(CVarRef v, CArrRef offset) {
948 return isset(v, VarNR(offset));
950 bool isset(CVarRef v, CObjRef offset) {
951 return isset(v, VarNR(offset));
953 bool isset(CVarRef v, CVarRef offset) {
954 Variant::TypedValueAccessor tva = v.getTypedAccessor();
955 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
956 return isset(Variant::GetAsArray(tva).rvalAtRef(offset));
958 if (Variant::GetAccessorType(tva) == KindOfObject) {
959 ObjectData* obj = Variant::GetObjectData(tva);
960 if (obj->isCollection()) {
961 return collectionOffsetIsset(obj, offset);
962 } else {
963 return Variant::GetArrayAccess(tva)->
964 o_invoke_few_args(s_offsetExists, 1, offset);
967 if (Variant::IsString(tva)) {
968 uint64_t pos = offset.toInt64();
969 return pos < (uint64_t)Variant::GetStringData(tva)->size();
971 return false;
974 bool isset(CVarRef v, CStrRef offset, bool isString /* = false */) {
975 Variant::TypedValueAccessor tva = v.getTypedAccessor();
976 if (LIKELY(Variant::GetAccessorType(tva) == KindOfArray)) {
977 return isset(Variant::GetAsArray(tva).rvalAtRef(
978 offset, AccessFlags::IsKey(isString)));
980 if (Variant::GetAccessorType(tva) == KindOfObject ||
981 Variant::IsString(tva)) {
982 return isset(v, Variant(offset));
984 return false;
987 bool interface_supports_array(const StringData* s) {
988 return (s->isame(s_Traversable.get()) ||
989 s->isame(s_KeyedTraversable.get()) ||
990 s->isame(s_Indexish.get()));
993 bool interface_supports_array(const std::string& n) {
994 const char* s = n.c_str();
995 return ((n.size() == 11 && !strcasecmp(s, "Traversable")) ||
996 (n.size() == 16 && !strcasecmp(s, "KeyedTraversable")) ||
997 (n.size() == 8 && !strcasecmp(s, "Indexish")));
1000 String get_source_filename(litstr path, bool dir_component /* = false */) {
1001 String ret;
1002 if (path[0] == '/') {
1003 ret = path;
1004 } else {
1005 ret = RuntimeOption::SourceRoot + path;
1008 if (dir_component) {
1009 return f_dirname(ret);
1010 } else {
1011 return ret;
1015 Variant include_impl_invoke(CStrRef file, bool once, const char *currentDir) {
1016 if (file[0] == '/') {
1017 if (RuntimeOption::SandboxMode || !RuntimeOption::AlwaysUseRelativePath) {
1018 try {
1019 return invoke_file(file, once, currentDir);
1020 } catch(PhpFileDoesNotExistException &e) {}
1023 String rel_path(Util::relativePath(RuntimeOption::SourceRoot,
1024 string(file.data())));
1026 // Don't try/catch - We want the exception to be passed along
1027 return invoke_file(rel_path, once, currentDir);
1028 } else {
1029 // Don't try/catch - We want the exception to be passed along
1030 return invoke_file(file, once, currentDir);
1034 Variant invoke_file(CStrRef s, bool once, const char *currentDir) {
1035 Variant r;
1036 if (invoke_file_impl(r, s, once, currentDir)) {
1037 return r;
1039 return throw_missing_file(s.c_str());
1042 bool invoke_file_impl(Variant &res, CStrRef path, bool once,
1043 const char *currentDir) {
1044 bool initial;
1045 HPHP::Eval::PhpFile* efile =
1046 g_vmContext->lookupPhpFile(path.get(), currentDir, &initial);
1047 HPHP::Unit* u = nullptr;
1048 if (efile) u = efile->unit();
1049 if (u == nullptr) {
1050 return false;
1052 if (!once || initial) {
1053 g_vmContext->invokeUnit((TypedValue*)(&res), u);
1055 return true;
1059 * Used by include_impl. resolve_include() needs some way of checking the
1060 * existence of a file path, which for hphpc means attempting to invoke it.
1061 * This struct carries some context information needed for the invocation, as
1062 * well as a place for the return value of invoking the file.
1064 struct IncludeImplInvokeContext {
1065 bool once;
1066 const char* currentDir;
1068 Variant returnValue;
1071 static bool include_impl_invoke_context(CStrRef file, void* ctx) {
1072 struct IncludeImplInvokeContext* context = (IncludeImplInvokeContext*)ctx;
1073 bool invoked_file = false;
1074 try {
1075 context->returnValue = include_impl_invoke(file, context->once,
1076 context->currentDir);
1077 invoked_file = true;
1078 } catch (PhpFileDoesNotExistException& e) {
1079 context->returnValue = false;
1081 return invoked_file;
1085 * tryFile is a pointer to a function that resolve_include() will use to
1086 * determine if a path references a real file. ctx is a pointer to some context
1087 * information that will be passed through to tryFile. (It's a hacky closure)
1089 String resolve_include(CStrRef file, const char* currentDir,
1090 bool (*tryFile)(CStrRef file, void*), void* ctx) {
1091 const char* c_file = file->data();
1093 if (c_file[0] == '/') {
1094 String can_path(Util::canonicalize(file.c_str(), file.size()),
1095 AttachString);
1097 if (tryFile(can_path, ctx)) {
1098 return can_path;
1101 } else if ((c_file[0] == '.' && (c_file[1] == '/' || (
1102 c_file[1] == '.' && c_file[2] == '/')))) {
1104 String path(String(g_context->getCwd() + "/" + file));
1105 String can_path(Util::canonicalize(path.c_str(), path.size()),
1106 AttachString);
1108 if (tryFile(can_path, ctx)) {
1109 return can_path;
1112 } else {
1113 Array includePaths = g_context->getIncludePathArray();
1114 unsigned int path_count = includePaths.size();
1116 for (int i = 0; i < (int)path_count; i++) {
1117 String path("");
1118 String includePath = includePaths[i];
1120 if (includePath[0] != '/') {
1121 path += (g_context->getCwd() + "/");
1124 path += includePath;
1126 if (path[path.size() - 1] != '/') {
1127 path += "/";
1130 path += file;
1131 String can_path(Util::canonicalize(path.c_str(), path.size()),
1132 AttachString);
1134 if (tryFile(can_path, ctx)) {
1135 return can_path;
1139 if (currentDir[0] == '/') {
1140 String path(currentDir);
1141 path += "/";
1142 path += file;
1143 String can_path(Util::canonicalize(path.c_str(), path.size()),
1144 AttachString);
1146 if (tryFile(can_path, ctx)) {
1147 return can_path;
1149 } else {
1150 String path(g_context->getCwd() + "/" + currentDir + file);
1151 String can_path(Util::canonicalize(path.c_str(), path.size()),
1152 AttachString);
1154 if (tryFile(can_path, ctx)) {
1155 return can_path;
1160 return String();
1163 static Variant include_impl(CStrRef file, bool once,
1164 const char *currentDir, bool required,
1165 bool raiseNotice) {
1166 struct IncludeImplInvokeContext ctx = {once, currentDir};
1167 String can_path = resolve_include(file, currentDir,
1168 include_impl_invoke_context, (void*)&ctx);
1170 if (can_path.isNull()) {
1171 // Failure
1172 if (raiseNotice) {
1173 raise_notice("Tried to invoke %s but file not found.", file->data());
1175 if (required) {
1176 String ms = "Required file that does not exist: ";
1177 ms += file;
1178 throw FatalErrorException(ms.data());
1180 return false;
1183 return ctx.returnValue;
1186 Variant include(CStrRef file, bool once /* = false */,
1187 const char *currentDir /* = NULL */,
1188 bool raiseNotice /*= true*/) {
1189 return include_impl(file, once, currentDir, false, raiseNotice);
1192 Variant require(CStrRef file, bool once /* = false */,
1193 const char *currentDir /* = NULL */,
1194 bool raiseNotice /*= true*/) {
1195 return include_impl(file, once, currentDir, true, raiseNotice);
1198 ///////////////////////////////////////////////////////////////////////////////
1199 // class Limits
1201 IMPLEMENT_REQUEST_LOCAL(AutoloadHandler, AutoloadHandler::s_instance);
1203 void AutoloadHandler::requestInit() {
1204 m_running = false;
1205 m_handlers.reset();
1206 m_map.reset();
1207 m_map_root.reset();
1210 void AutoloadHandler::requestShutdown() {
1211 m_handlers.reset();
1212 m_map.reset();
1213 m_map_root.reset();
1216 bool AutoloadHandler::setMap(CArrRef map, CStrRef root) {
1217 this->m_map = map;
1218 this->m_map_root = root;
1219 return true;
1222 class ClassExistsChecker {
1223 public:
1224 ClassExistsChecker() {}
1225 bool operator()(CStrRef name) const {
1226 return Unit::lookupClass(name.get()) != nullptr;
1230 class ConstantExistsChecker {
1231 public:
1232 bool operator()(CStrRef name) const {
1233 return Unit::lookupCns(name.get()) != nullptr;
1237 template <class T>
1238 AutoloadHandler::Result AutoloadHandler::loadFromMap(CStrRef name,
1239 CStrRef kind,
1240 bool toLower,
1241 const T &checkExists) {
1242 assert(!m_map.isNull());
1243 while (true) {
1244 CVarRef &type_map = m_map.get()->get(kind);
1245 Variant::TypedValueAccessor tva = type_map.getTypedAccessor();
1246 if (Variant::GetAccessorType(tva) != KindOfArray) return Failure;
1247 String canonicalName = toLower ? StringUtil::ToLower(name) : name;
1248 CVarRef &file = Variant::GetArrayData(tva)->get(canonicalName);
1249 bool ok = false;
1250 if (file.isString()) {
1251 String fName = file.toCStrRef().get();
1252 if (fName.get()->data()[0] != '/') {
1253 if (!m_map_root.empty()) {
1254 fName = m_map_root + fName;
1257 try {
1258 Transl::VMRegAnchor _;
1259 bool initial;
1260 VMExecutionContext* ec = g_vmContext;
1261 Unit* u = ec->evalInclude(fName.get(), nullptr, &initial);
1262 if (u) {
1263 if (initial) {
1264 TypedValue retval;
1265 ec->invokeFunc(&retval, u->getMain(), null_array,
1266 nullptr, nullptr, nullptr, nullptr,
1267 ExecutionContext::InvokePseudoMain);
1268 tvRefcountedDecRef(&retval);
1270 ok = true;
1272 } catch (...) {}
1274 if (ok && checkExists(name)) {
1275 return Success;
1277 CVarRef &func = m_map.get()->get(s_failure);
1278 if (!isset(func)) return Failure;
1279 // can throw, otherwise
1280 // - true means the map was updated. try again
1281 // - false means we should stop applying autoloaders (only affects classes)
1282 // - anything else means keep going
1283 Variant action = vm_call_user_func(func, CREATE_VECTOR2(kind, name));
1284 tva = action.getTypedAccessor();
1285 if (Variant::GetAccessorType(tva) == KindOfBoolean) {
1286 if (Variant::GetBoolean(tva)) continue;
1287 return StopAutoloading;
1289 return ContinueAutoloading;
1293 bool AutoloadHandler::autoloadFunc(StringData* name) {
1294 return !m_map.isNull() &&
1295 loadFromMap(name, s_function, true, function_exists) != Failure;
1298 bool AutoloadHandler::autoloadConstant(StringData* name) {
1299 return !m_map.isNull() &&
1300 loadFromMap(name, s_constant, false, ConstantExistsChecker()) != Failure;
1303 bool AutoloadHandler::autoloadType(CStrRef name) {
1304 return !m_map.isNull() &&
1305 loadFromMap(name, s_type, true,
1306 [] (CStrRef name) {
1307 return !!Unit::GetNamedEntity(name.get())->getCachedNameDef();
1309 ) != Failure;
1313 * invokeHandler returns true if any autoload handlers were executed,
1314 * false otherwise. When this function returns true, it is the caller's
1315 * responsibility to check if the given class or interface exists.
1317 bool AutoloadHandler::invokeHandler(CStrRef className,
1318 bool forceSplStack /* = false */) {
1319 if (!m_map.isNull()) {
1320 ClassExistsChecker ce;
1321 Result res = loadFromMap(className, s_class, true, ce);
1322 if (res == ContinueAutoloading) {
1323 if (ce(className)) return true;
1324 } else {
1325 if (res != Failure) return res == Success;
1328 Array params(ArrayInit(1, ArrayInit::vectorInit).set(className).create());
1329 bool l_running = m_running;
1330 m_running = true;
1331 if (m_handlers.isNull() && !forceSplStack) {
1332 if (function_exists(s___autoload)) {
1333 invoke(s___autoload, params, -1, true, false);
1334 m_running = l_running;
1335 return true;
1337 m_running = l_running;
1338 return false;
1340 if (empty(m_handlers)) {
1341 m_running = l_running;
1342 return false;
1344 Object autoloadException;
1345 for (ArrayIter iter(m_handlers); iter; ++iter) {
1346 try {
1347 vm_call_user_func(iter.second(), params);
1348 } catch (Object& ex) {
1349 assert(ex.instanceof(SystemLib::s_ExceptionClass));
1350 if (autoloadException.isNull()) {
1351 autoloadException = ex;
1352 } else {
1353 Object cur = ex;
1354 Variant next = cur->o_get(s_previous, false, s_exception);
1355 while (next.isObject()) {
1356 cur = next.toObject();
1357 next = cur->o_get(s_previous, false, s_exception);
1359 cur->o_set(s_previous, autoloadException, s_exception);
1360 autoloadException = ex;
1363 if (Unit::lookupClass(className.get()) != nullptr) {
1364 break;
1367 m_running = l_running;
1368 if (!autoloadException.isNull()) {
1369 throw autoloadException;
1371 return true;
1374 bool AutoloadHandler::addHandler(CVarRef handler, bool prepend) {
1375 String name = getSignature(handler);
1376 if (name.isNull()) return false;
1378 if (m_handlers.isNull()) {
1379 m_handlers = Array::Create();
1382 if (!prepend) {
1383 // The following ensures that the handler is added at the end
1384 m_handlers.remove(name, true);
1385 m_handlers.add(name, handler, true);
1386 } else {
1387 // This adds the handler at the beginning
1388 m_handlers = CREATE_MAP1(name, handler) + m_handlers;
1390 return true;
1393 bool AutoloadHandler::isRunning() {
1394 return m_running;
1397 void AutoloadHandler::removeHandler(CVarRef handler) {
1398 String name = getSignature(handler);
1399 if (name.isNull()) return;
1400 m_handlers.remove(name, true);
1403 void AutoloadHandler::removeAllHandlers() {
1404 m_handlers.reset();
1407 String AutoloadHandler::getSignature(CVarRef handler) {
1408 Variant name;
1409 if (!f_is_callable(handler, false, ref(name))) {
1410 return null_string;
1412 String lName = StringUtil::ToLower(name);
1413 if (handler.isArray()) {
1414 Variant first = handler.getArrayData()->get(int64_t(0));
1415 if (first.isObject()) {
1416 // Add the object address as part of the signature
1417 int64_t data = (int64_t)first.getObjectData();
1418 lName += String((const char *)&data, sizeof(data), CopyString);
1420 } else if (handler.isObject()) {
1421 // "lName" will just be "classname::__invoke",
1422 // add object address to differentiate the signature
1423 int64_t data = (int64_t)handler.getObjectData();
1424 lName += String((const char*)&data, sizeof(data), CopyString);
1426 return lName;
1429 bool function_exists(CStrRef function_name) {
1430 return HPHP::Unit::lookupFunc(function_name.get()) != nullptr;
1433 ///////////////////////////////////////////////////////////////////////////////
1434 // debugger and code coverage instrumentation
1436 inline void throw_exception_unchecked(CObjRef e) {
1437 if (!Eval::Debugger::InterruptException(e)) return;
1438 throw e;
1440 void throw_exception(CObjRef e) {
1441 if (!e.instanceof(SystemLib::s_ExceptionClass)) {
1442 raise_error("Exceptions must be valid objects derived from the "
1443 "Exception base class");
1445 throw_exception_unchecked(e);
1448 ///////////////////////////////////////////////////////////////////////////////