Add varray support to the serializer
[hiphop-php.git] / hphp / runtime / ext / std / ext_std_variable.cpp
blob210bae5b56a4abdb4b1645eee656fe218f0b08f0
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
17 #include "hphp/runtime/ext/std/ext_std_variable.h"
19 #include <folly/Likely.h>
21 #include "hphp/util/logger.h"
22 #include "hphp/util/hphp-config.h"
23 #include "hphp/runtime/base/variable-serializer.h"
24 #include "hphp/runtime/base/variable-unserializer.h"
25 #include "hphp/runtime/base/builtin-functions.h"
26 #include "hphp/runtime/base/zend-functions.h"
27 #ifdef ENABLE_EXTENSION_XDEBUG
28 #include "hphp/runtime/ext/xdebug/ext_xdebug.h"
29 #endif
30 #include "hphp/runtime/vm/jit/translator-inline.h"
31 #include "hphp/runtime/server/http-protocol.h"
33 namespace HPHP {
34 ///////////////////////////////////////////////////////////////////////////////
36 const StaticString
37 s_unknown_type("unknown type"),
38 s_boolean("boolean"),
39 s_bool("bool"),
40 s_integer("integer"),
41 s_int("int"),
42 s_float("float"),
43 s_double("double"),
44 s_string("string"),
45 s_object("object"),
46 s_array("array"),
47 s_NULL("NULL"),
48 s_null("null");
50 String HHVM_FUNCTION(gettype, const Variant& v) {
51 if (v.getType() == KindOfResource && v.toCResRef().isInvalid()) {
52 return s_unknown_type;
54 /* Although getDataTypeString also handles the null type, it returns "null"
55 * (lower case). Unfortunately, PHP returns "NULL" (upper case) for
56 * gettype(). So we make an exception here. */
57 if (v.isNull()) {
58 return s_NULL;
60 return getDataTypeString(v.getType());
63 String HHVM_FUNCTION(get_resource_type, const Resource& handle) {
64 return handle->o_getResourceName();
67 bool HHVM_FUNCTION(boolval, const Variant& v) {
68 return v.toBoolean();
71 int64_t HHVM_FUNCTION(intval, const Variant& v, int64_t base /* = 10 */) {
72 return v.toInt64(base);
75 double HHVM_FUNCTION(floatval, const Variant& v) {
76 return v.toDouble();
79 String HHVM_FUNCTION(strval, const Variant& v) {
80 return v.toString();
83 bool HHVM_FUNCTION(settype, VRefParam var, const String& type) {
84 Variant val;
85 if (type == s_boolean) val = var.toBoolean();
86 else if (type == s_bool ) val = var.toBoolean();
87 else if (type == s_integer) val = var.toInt64();
88 else if (type == s_int ) val = var.toInt64();
89 else if (type == s_float ) val = var.toDouble();
90 else if (type == s_double ) val = var.toDouble();
91 else if (type == s_string ) val = var.toString();
92 else if (type == s_array ) val = var.toArray();
93 else if (type == s_object ) val = var.toObject();
94 else if (type == s_null ) val = uninit_null();
95 else return false;
96 var.assignIfRef(val);
97 return true;
100 bool HHVM_FUNCTION(is_null, const Variant& v) {
101 return is_null(v);
104 bool HHVM_FUNCTION(is_bool, const Variant& v) {
105 return is_bool(v);
108 bool HHVM_FUNCTION(is_int, const Variant& v) {
109 return is_int(v);
112 bool HHVM_FUNCTION(is_float, const Variant& v) {
113 return is_double(v);
116 bool HHVM_FUNCTION(is_numeric, const Variant& v) {
117 return v.isNumeric(true);
120 bool HHVM_FUNCTION(is_string, const Variant& v) {
121 return is_string(v);
124 bool HHVM_FUNCTION(is_scalar, const Variant& v) {
125 return v.isScalar();
128 bool HHVM_FUNCTION(is_array, const Variant& v) {
129 return is_array(v);
132 bool HHVM_FUNCTION(HH_is_vec, const Variant& v) {
133 return is_vec(v);
136 bool HHVM_FUNCTION(HH_is_dict, const Variant& v) {
137 return is_dict(v);
140 bool HHVM_FUNCTION(HH_is_keyset, const Variant& v) {
141 return is_keyset(v);
144 bool HHVM_FUNCTION(HH_is_varray, const Variant& val) {
145 return val.isPHPArray() && val.asCArrRef().isVArray();
148 bool HHVM_FUNCTION(is_object, const Variant& v) {
149 return is_object(v);
152 bool HHVM_FUNCTION(is_resource, const Variant& v) {
153 return (v.getType() == KindOfResource && !v.toCResRef().isInvalid());
156 ///////////////////////////////////////////////////////////////////////////////
158 Array HHVM_FUNCTION(HH_object_prop_array, const Object& obj) {
159 return obj.toArray();
162 ///////////////////////////////////////////////////////////////////////////////
163 // input/output
165 Variant HHVM_FUNCTION(print_r, const Variant& expression,
166 bool ret /* = false */) {
167 Variant res;
168 try {
169 VariableSerializer vs(VariableSerializer::Type::PrintR);
170 if (ret) {
171 res = vs.serialize(expression, ret);
172 } else {
173 vs.serialize(expression, ret);
174 res = true;
176 } catch (StringBufferLimitException &e) {
177 raise_notice("print_r() exceeded max bytes limit");
178 res = e.m_result;
180 return res;
183 Variant HHVM_FUNCTION(var_export, const Variant& expression,
184 bool ret /* = false */) {
185 Variant res;
186 try {
187 VariableSerializer vs(VariableSerializer::Type::VarExport);
188 if (ret) {
189 res = vs.serialize(expression, ret);
190 } else {
191 vs.serialize(expression, ret);
192 res = true;
194 } catch (StringBufferLimitException &e) {
195 raise_notice("var_export() exceeded max bytes limit");
197 return res;
200 static ALWAYS_INLINE void do_var_dump(VariableSerializer& vs,
201 const Variant& expression) {
202 // manipulate maxCount to match PHP behavior
203 if (!expression.isObject()) {
204 vs.incMaxCount();
206 vs.serialize(expression, false);
209 void HHVM_FUNCTION(var_dump, const Variant& expression,
210 const Array& _argv /*=null_array */) {
211 #ifdef ENABLE_EXTENSION_XDEBUG
212 if (UNLIKELY(XDEBUG_GLOBAL(OverloadVarDump) &&
213 XDEBUG_GLOBAL(DefaultEnable))) {
214 HHVM_FN(xdebug_var_dump)(expression, _argv);
215 return;
217 #endif
219 VariableSerializer vs(VariableSerializer::Type::VarDump, 0, 2);
220 do_var_dump(vs, expression);
222 auto sz = _argv.size();
223 for (int i = 0; i < sz; i++) {
224 do_var_dump(vs, _argv[i]);
228 void HHVM_FUNCTION(debug_zval_dump, const Variant& variable) {
229 VariableSerializer vs(VariableSerializer::Type::DebugDump);
230 vs.serialize(variable, false);
233 const StaticString
234 s_Null("N;"),
235 s_True("b:1;"),
236 s_False("b:0;"),
237 s_Res("i:0;"),
238 s_EmptyArray("a:0:{}"),
239 s_EmptyVArray("y:0:{}"),
240 s_EmptyVecArray("v:0:{}"),
241 s_EmptyDictArray("D:0:{}"),
242 s_EmptyKeysetArray("k:0:{}");
244 String HHVM_FUNCTION(serialize, const Variant& value) {
245 switch (value.getType()) {
246 case KindOfUninit:
247 case KindOfNull:
248 return s_Null;
249 case KindOfBoolean:
250 return value.getBoolean() ? s_True : s_False;
251 case KindOfInt64: {
252 StringBuffer sb;
253 sb.append("i:");
254 sb.append(value.getInt64());
255 sb.append(';');
256 return sb.detach();
258 case KindOfPersistentString:
259 case KindOfString: {
260 StringData *str = value.getStringData();
261 auto const size = str->size();
262 if (size >= RuntimeOption::MaxSerializedStringSize) {
263 throw Exception("Size of serialized string (%d) exceeds max", size);
265 StringBuffer sb;
266 sb.append("s:");
267 sb.append(size);
268 sb.append(":\"");
269 sb.append(str->data(), size);
270 sb.append("\";");
271 return sb.detach();
273 case KindOfResource:
274 return s_Res;
276 case KindOfPersistentVec:
277 case KindOfVec: {
278 ArrayData* arr = value.getArrayData();
279 assert(arr->isVecArray());
280 if (arr->empty()) return s_EmptyVecArray;
281 break;
284 case KindOfPersistentDict:
285 case KindOfDict: {
286 ArrayData* arr = value.getArrayData();
287 assert(arr->isDict());
288 if (arr->empty()) return s_EmptyDictArray;
289 break;
292 case KindOfPersistentKeyset:
293 case KindOfKeyset: {
294 ArrayData* arr = value.getArrayData();
295 assert(arr->isKeyset());
296 if (arr->empty()) return s_EmptyKeysetArray;
297 break;
300 case KindOfPersistentArray:
301 case KindOfArray: {
302 ArrayData *arr = value.getArrayData();
303 assert(arr->isPHPArray());
304 if (arr->empty()) {
305 if (arr->isVArray()) return s_EmptyVArray;
306 return s_EmptyArray;
308 break;
310 case KindOfDouble:
311 case KindOfObject:
312 break;
314 case KindOfRef:
315 not_reached();
318 VariableSerializer vs(VariableSerializer::Type::Serialize);
319 // Keep the count so recursive calls to serialize() embed references properly.
320 return vs.serialize(value, true, true);
323 Variant HHVM_FUNCTION(unserialize, const String& str,
324 const Array& options /* =[] */) {
325 return unserialize_from_string(str, options);
328 ///////////////////////////////////////////////////////////////////////////////
329 // variable table
331 Array HHVM_FUNCTION(get_defined_vars) {
332 VarEnv* v = g_context->getOrCreateVarEnv();
333 return v ? v->getDefinedVariables() : empty_array();
336 const StaticString
337 s_GLOBALS("GLOBALS"),
338 s_this("this");
340 static const Func* arGetContextFunc(const ActRec* ar) {
341 if (ar == nullptr) {
342 return nullptr;
344 if (ar->m_func->isPseudoMain() || ar->m_func->isBuiltin()) {
345 // Pseudomains inherit the context of their caller
346 auto const context = g_context.getNoCheck();
347 ar = context->getPrevVMState(ar);
348 while (ar != nullptr &&
349 (ar->m_func->isPseudoMain() || ar->m_func->isBuiltin())) {
350 ar = context->getPrevVMState(ar);
352 if (ar == nullptr) {
353 return nullptr;
356 return ar->m_func;
359 static bool modify_extract_name(VarEnv* v,
360 String& name,
361 int64_t extract_type,
362 const String& prefix) {
363 switch (extract_type) {
364 case EXTR_SKIP:
365 if (v->lookup(name.get()) != nullptr) {
366 return false;
368 break;
369 case EXTR_IF_EXISTS:
370 if (v->lookup(name.get()) == nullptr) {
371 return false;
372 } else {
373 goto namechecks;
375 break;
376 case EXTR_PREFIX_SAME:
377 if (v->lookup(name.get()) != nullptr) {
378 name = prefix + "_" + name;
379 } else {
380 goto namechecks;
382 break;
383 case EXTR_PREFIX_ALL:
384 name = prefix + "_" + name;
385 break;
386 case EXTR_PREFIX_INVALID:
387 if (!is_valid_var_name(name.get()->data(), name.size())) {
388 name = prefix + "_" + name;
389 } else {
390 goto namechecks;
392 break;
393 case EXTR_PREFIX_IF_EXISTS:
394 if (v->lookup(name.get()) == nullptr) {
395 return false;
397 name = prefix + "_" + name;
398 break;
399 case EXTR_OVERWRITE:
400 namechecks:
401 if (name == s_GLOBALS) {
402 return false;
404 if (name == s_this) {
405 // Only disallow $this when inside a non-static method, or a static method
406 // that has defined $this (matches Zend)
407 auto const func = arGetContextFunc(GetCallerFrame());
409 if (func && func->isMethod() && v->lookup(s_this.get()) != nullptr) {
410 return false;
413 default:
414 break;
417 // skip invalid variable names, as in PHP
418 return is_valid_var_name(name.get()->data(), name.size());
421 int64_t HHVM_FUNCTION(extract,
422 VRefParam vref_array,
423 int64_t extract_type = EXTR_OVERWRITE,
424 const String& prefix = "") {
425 auto arrByRef = false;
426 auto arr_tv = vref_array.wrapped().asTypedValue();
427 if (arr_tv->m_type == KindOfRef) {
428 arr_tv = arr_tv->m_data.pref->tv();
429 arrByRef = true;
431 if (!isArrayLikeType(arr_tv->m_type)) {
432 raise_warning("extract() expects parameter 1 to be array");
433 return 0;
436 bool reference = extract_type & EXTR_REFS;
437 extract_type &= ~EXTR_REFS;
439 VMRegAnchor _;
440 auto const varEnv = g_context->getOrCreateVarEnv();
441 if (!varEnv) return 0;
443 auto& carr = tvAsCVarRef(arr_tv).asCArrRef();
444 if (UNLIKELY(reference)) {
445 auto extr_refs = [&](Array& arr) {
446 if (arr.size() > 0) {
447 // force arr to escalate (if necessary) by getting an lvalue to the
448 // first element.
449 ArrayData* ad = arr.get();
450 auto const& first_key = ad->getKey(ad->iter_begin());
451 arr.lvalAt(first_key);
453 int count = 0;
454 for (ArrayIter iter(arr); iter; ++iter) {
455 auto name = iter.first().toString();
456 if (!modify_extract_name(varEnv, name, extract_type, prefix)) continue;
457 // The const_cast is safe because we escalated the array. We can't use
458 // arr.lvalAt(name), because arr may have been modified as a side
459 // effect of an earlier iteration.
460 auto const rval = iter.secondRval();
461 g_context->bindVar(name.get(), const_cast<TypedValue*>(rval.tv_ptr()));
462 ++count;
464 return count;
467 if (arrByRef) {
468 return extr_refs(tvAsVariant(vref_array.getRefData()->tv()).asArrRef());
470 Array tmp = carr;
471 return extr_refs(tmp);
474 int count = 0;
475 for (ArrayIter iter(carr); iter; ++iter) {
476 auto name = iter.first().toString();
477 if (!modify_extract_name(varEnv, name, extract_type, prefix)) continue;
478 g_context->setVar(name.get(), iter.secondRval().tv_ptr());
479 ++count;
481 return count;
484 void HHVM_FUNCTION(parse_str,
485 const String& str,
486 VRefParam arr /* = null */) {
487 Array result = Array::Create();
488 HttpProtocol::DecodeParameters(result, str.data(), str.size());
489 if (!arr.isReferenced()) {
490 HHVM_FN(extract)(result);
491 return;
493 arr.assignIfRef(result);
496 /////////////////////////////////////////////////////////////////////////////
498 void StandardExtension::initVariable() {
499 HHVM_RC_INT_SAME(EXTR_IF_EXISTS);
500 HHVM_RC_INT_SAME(EXTR_OVERWRITE);
501 HHVM_RC_INT_SAME(EXTR_PREFIX_ALL);
502 HHVM_RC_INT_SAME(EXTR_PREFIX_IF_EXISTS);
503 HHVM_RC_INT_SAME(EXTR_PREFIX_INVALID);
504 HHVM_RC_INT_SAME(EXTR_PREFIX_SAME);
505 HHVM_RC_INT_SAME(EXTR_REFS);
506 HHVM_RC_INT_SAME(EXTR_SKIP);
508 HHVM_FE(is_null);
509 HHVM_FE(is_bool);
510 HHVM_FE(is_int);
511 HHVM_FALIAS(is_integer, is_int);
512 HHVM_FALIAS(is_long, is_int);
513 HHVM_FE(is_float);
514 HHVM_FALIAS(is_double, is_float);
515 HHVM_FALIAS(is_real, is_float);
516 HHVM_FE(is_numeric);
517 HHVM_FE(is_string);
518 HHVM_FE(is_scalar);
519 HHVM_FE(is_array);
520 HHVM_FALIAS(HH\\is_vec, HH_is_vec);
521 HHVM_FALIAS(HH\\is_dict, HH_is_dict);
522 HHVM_FALIAS(HH\\is_keyset, HH_is_keyset);
523 HHVM_FALIAS(HH\\is_varray, HH_is_varray);
524 HHVM_FALIAS(HH\\is_varray_or_darray, is_array);
525 HHVM_FE(is_object);
526 HHVM_FE(is_resource);
527 HHVM_FE(boolval);
528 HHVM_FE(intval);
529 HHVM_FE(floatval);
530 HHVM_FALIAS(doubleval, floatval);
531 HHVM_FE(strval);
532 HHVM_FE(gettype);
533 HHVM_FE(get_resource_type);
534 HHVM_FE(settype);
535 HHVM_FE(print_r);
536 HHVM_FE(var_export);
537 HHVM_FE(debug_zval_dump);
538 HHVM_FE(var_dump);
539 HHVM_FE(serialize);
540 HHVM_FE(unserialize);
541 HHVM_FE(get_defined_vars);
542 HHVM_FE(extract);
543 HHVM_FE(parse_str);
544 HHVM_FALIAS(HH\\object_prop_array, HH_object_prop_array);
546 loadSystemlib("std_variable");
549 ///////////////////////////////////////////////////////////////////////////////
550 } // namespace HPHP