2 +----------------------------------------------------------------------+
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"
30 #include "hphp/runtime/vm/jit/translator-inline.h"
31 #include "hphp/runtime/server/http-protocol.h"
34 ///////////////////////////////////////////////////////////////////////////////
37 s_unknown_type("unknown type"),
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. */
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
) {
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
) {
79 String
HHVM_FUNCTION(strval
, const Variant
& v
) {
83 bool HHVM_FUNCTION(settype
, VRefParam var
, const String
& type
) {
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();
100 bool HHVM_FUNCTION(is_null
, const Variant
& v
) {
104 bool HHVM_FUNCTION(is_bool
, const Variant
& v
) {
108 bool HHVM_FUNCTION(is_int
, const Variant
& v
) {
112 bool HHVM_FUNCTION(is_float
, const Variant
& v
) {
116 bool HHVM_FUNCTION(is_numeric
, const Variant
& v
) {
117 return v
.isNumeric(true);
120 bool HHVM_FUNCTION(is_string
, const Variant
& v
) {
124 bool HHVM_FUNCTION(is_scalar
, const Variant
& v
) {
128 bool HHVM_FUNCTION(is_array
, const Variant
& v
) {
132 bool HHVM_FUNCTION(HH_is_vec
, const Variant
& v
) {
136 bool HHVM_FUNCTION(HH_is_dict
, const Variant
& v
) {
140 bool HHVM_FUNCTION(HH_is_keyset
, const Variant
& 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
) {
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 ///////////////////////////////////////////////////////////////////////////////
165 Variant
HHVM_FUNCTION(print_r
, const Variant
& expression
,
166 bool ret
/* = false */) {
169 VariableSerializer
vs(VariableSerializer::Type::PrintR
);
171 res
= vs
.serialize(expression
, ret
);
173 vs
.serialize(expression
, ret
);
176 } catch (StringBufferLimitException
&e
) {
177 raise_notice("print_r() exceeded max bytes limit");
183 Variant
HHVM_FUNCTION(var_export
, const Variant
& expression
,
184 bool ret
/* = false */) {
187 VariableSerializer
vs(VariableSerializer::Type::VarExport
);
189 res
= vs
.serialize(expression
, ret
);
191 vs
.serialize(expression
, ret
);
194 } catch (StringBufferLimitException
&e
) {
195 raise_notice("var_export() exceeded max bytes limit");
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()) {
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
);
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);
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()) {
250 return value
.getBoolean() ? s_True
: s_False
;
254 sb
.append(value
.getInt64());
258 case KindOfPersistentString
:
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
);
269 sb
.append(str
->data(), size
);
276 case KindOfPersistentVec
:
278 ArrayData
* arr
= value
.getArrayData();
279 assert(arr
->isVecArray());
280 if (arr
->empty()) return s_EmptyVecArray
;
284 case KindOfPersistentDict
:
286 ArrayData
* arr
= value
.getArrayData();
287 assert(arr
->isDict());
288 if (arr
->empty()) return s_EmptyDictArray
;
292 case KindOfPersistentKeyset
:
294 ArrayData
* arr
= value
.getArrayData();
295 assert(arr
->isKeyset());
296 if (arr
->empty()) return s_EmptyKeysetArray
;
300 case KindOfPersistentArray
:
302 ArrayData
*arr
= value
.getArrayData();
303 assert(arr
->isPHPArray());
305 if (arr
->isVArray()) return s_EmptyVArray
;
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 ///////////////////////////////////////////////////////////////////////////////
331 Array
HHVM_FUNCTION(get_defined_vars
) {
332 VarEnv
* v
= g_context
->getOrCreateVarEnv();
333 return v
? v
->getDefinedVariables() : empty_array();
337 s_GLOBALS("GLOBALS"),
340 static const Func
* arGetContextFunc(const ActRec
* ar
) {
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
);
359 static bool modify_extract_name(VarEnv
* v
,
361 int64_t extract_type
,
362 const String
& prefix
) {
363 switch (extract_type
) {
365 if (v
->lookup(name
.get()) != nullptr) {
370 if (v
->lookup(name
.get()) == nullptr) {
376 case EXTR_PREFIX_SAME
:
377 if (v
->lookup(name
.get()) != nullptr) {
378 name
= prefix
+ "_" + name
;
383 case EXTR_PREFIX_ALL
:
384 name
= prefix
+ "_" + name
;
386 case EXTR_PREFIX_INVALID
:
387 if (!is_valid_var_name(name
.get()->data(), name
.size())) {
388 name
= prefix
+ "_" + name
;
393 case EXTR_PREFIX_IF_EXISTS
:
394 if (v
->lookup(name
.get()) == nullptr) {
397 name
= prefix
+ "_" + name
;
401 if (name
== s_GLOBALS
) {
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) {
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();
431 if (!isArrayLikeType(arr_tv
->m_type
)) {
432 raise_warning("extract() expects parameter 1 to be array");
436 bool reference
= extract_type
& EXTR_REFS
;
437 extract_type
&= ~EXTR_REFS
;
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
449 ArrayData
* ad
= arr
.get();
450 auto const& first_key
= ad
->getKey(ad
->iter_begin());
451 arr
.lvalAt(first_key
);
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()));
468 return extr_refs(tvAsVariant(vref_array
.getRefData()->tv()).asArrRef());
471 return extr_refs(tmp
);
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());
484 void HHVM_FUNCTION(parse_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
);
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
);
511 HHVM_FALIAS(is_integer
, is_int
);
512 HHVM_FALIAS(is_long
, is_int
);
514 HHVM_FALIAS(is_double
, is_float
);
515 HHVM_FALIAS(is_real
, is_float
);
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
);
526 HHVM_FE(is_resource
);
530 HHVM_FALIAS(doubleval
, floatval
);
533 HHVM_FE(get_resource_type
);
537 HHVM_FE(debug_zval_dump
);
540 HHVM_FE(unserialize
);
541 HHVM_FE(get_defined_vars
);
544 HHVM_FALIAS(HH
\\object_prop_array
, HH_object_prop_array
);
546 loadSystemlib("std_variable");
549 ///////////////////////////////////////////////////////////////////////////////