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_options.h"
19 #include <sys/types.h>
26 #include <sys/utsname.h>
29 #include <folly/ScopeGuard.h>
30 #include <folly/String.h>
31 #include <folly/portability/SysResource.h>
32 #include <folly/portability/SysTime.h>
34 #include "hphp/runtime/base/array-init.h"
35 #include "hphp/runtime/base/builtin-functions.h"
36 #include "hphp/runtime/base/ini-setting.h"
37 #include "hphp/runtime/base/memory-manager.h"
38 #include "hphp/runtime/base/php-globals.h"
39 #include "hphp/runtime/base/request-event-handler.h"
40 #include "hphp/runtime/base/request-local.h"
41 #include "hphp/runtime/base/runtime-error.h"
42 #include "hphp/runtime/base/runtime-option.h"
43 #include "hphp/runtime/base/unit-cache.h"
44 #include "hphp/runtime/base/zend-functions.h"
45 #include "hphp/runtime/base/zend-string.h"
46 #include "hphp/runtime/ext/extension.h"
47 #include "hphp/runtime/ext/extension-registry.h"
48 #include "hphp/runtime/ext/std/ext_std_errorfunc.h"
49 #include "hphp/runtime/ext/std/ext_std_function.h"
50 #include "hphp/runtime/ext/std/ext_std_misc.h"
51 #include "hphp/runtime/server/cli-server.h"
52 #include "hphp/runtime/vm/jit/translator-inline.h"
53 #include "hphp/util/process.h"
54 #include "hphp/util/timer.h"
57 ///////////////////////////////////////////////////////////////////////////////
59 const StaticString
s_SLASH_TMP("/tmp");
60 const StaticString
s_ZEND_VERSION("2.4.99");
62 const int64_t k_ASSERT_ACTIVE
= 1;
63 const int64_t k_ASSERT_CALLBACK
= 2;
64 const int64_t k_ASSERT_BAIL
= 3;
65 const int64_t k_ASSERT_WARNING
= 4;
66 const int64_t k_ASSERT_QUIET_EVAL
= 5;
67 const int64_t k_ASSERT_EXCEPTION
= 6;
69 ///////////////////////////////////////////////////////////////////////////////
71 struct OptionData final
: RequestEventHandler
{
72 void requestInit() override
{
77 assertQuietEval
= false;
80 void requestShutdown() override
{
81 assertCallback
.unset();
89 Variant assertCallback
;
92 IMPLEMENT_STATIC_REQUEST_LOCAL(OptionData
, s_option_data
);
94 /////////////////////////////////////////////////////////////////////////////
96 void StandardExtension::requestInitOptions() {
97 IniSetting::Bind(IniSetting::CORE
, IniSetting::PHP_INI_ALL
,
98 "assert.active", "1", &s_option_data
->assertActive
);
99 IniSetting::Bind(IniSetting::CORE
, IniSetting::PHP_INI_ALL
,
100 "assert.exception", "0", &s_option_data
->assertException
);
101 IniSetting::Bind(IniSetting::CORE
, IniSetting::PHP_INI_ALL
,
102 "assert.warning", "1", &s_option_data
->assertWarning
);
103 IniSetting::Bind(IniSetting::CORE
, IniSetting::PHP_INI_ALL
,
104 "assert.bail", "0", &s_option_data
->assertBail
);
107 static Variant
HHVM_FUNCTION(assert_options
,
108 int64_t what
, const Variant
& value
/*=null */) {
109 if (what
== k_ASSERT_ACTIVE
) {
110 int oldValue
= s_option_data
->assertActive
;
111 if (!value
.isNull()) s_option_data
->assertActive
= value
.toInt64();
114 if (what
== k_ASSERT_WARNING
) {
115 int oldValue
= s_option_data
->assertWarning
;
116 if (!value
.isNull()) s_option_data
->assertWarning
= value
.toInt64();
119 if (what
== k_ASSERT_BAIL
) {
120 int oldValue
= s_option_data
->assertBail
;
121 if (!value
.isNull()) s_option_data
->assertBail
= value
.toInt64();
124 if (what
== k_ASSERT_CALLBACK
) {
125 Variant oldValue
= s_option_data
->assertCallback
;
126 if (!value
.isNull()) s_option_data
->assertCallback
= value
;
129 if (what
== k_ASSERT_QUIET_EVAL
) {
130 bool oldValue
= s_option_data
->assertQuietEval
;
131 if (!value
.isNull()) s_option_data
->assertQuietEval
= value
.toBoolean();
132 return Variant(oldValue
);
134 if (what
== k_ASSERT_EXCEPTION
) {
135 int oldValue
= s_option_data
->assertException
;
136 if (!value
.isNull()) s_option_data
->assertException
= value
.toBoolean();
137 return Variant(oldValue
);
139 throw_invalid_argument("assert option %ld is not supported", (long)what
);
143 static Variant
eval_for_assert(ActRec
* const curFP
, const String
& codeStr
) {
144 String prefixedCode
= concat3(
145 curFP
->unit()->isHHFile() ? "<?hh return " : "<?php return ",
150 auto const oldErrorLevel
=
151 s_option_data
->assertQuietEval
? HHVM_FN(error_reporting
)(Variant(0)) : 0;
153 if (s_option_data
->assertQuietEval
) HHVM_FN(error_reporting
)(oldErrorLevel
);
156 auto const unit
= g_context
->compileEvalString(prefixedCode
.get());
157 if (unit
== nullptr) {
158 raise_recoverable_error("Syntax error in assert()");
159 // Failure to compile the eval string doesn't count as an
160 // assertion failure.
161 return Variant(true);
164 if (!(curFP
->func()->attrs() & AttrMayUseVV
)) {
165 throw_not_supported("assert()",
166 "assert called from non-varenv function");
169 if (!curFP
->hasVarEnv()) {
170 curFP
->setVarEnv(VarEnv::createLocal(curFP
));
172 auto varEnv
= curFP
->getVarEnv();
174 ObjectData
* thiz
= nullptr;
175 Class
* cls
= nullptr;
176 Class
* ctx
= curFP
->func()->cls();
178 if (curFP
->hasThis()) {
179 thiz
= curFP
->getThis();
180 cls
= thiz
->getVMClass();
182 cls
= curFP
->getClass();
185 auto const func
= unit
->getMain(ctx
);
186 return Variant::attach(
187 g_context
->invokeFunc(
194 ExecutionContext::InvokePseudoMain
199 static Variant
HHVM_FUNCTION(assert, const Variant
& assertion
,
200 const Variant
& message
/* = null */) {
201 if (!s_option_data
->assertActive
) return true;
205 auto const fp
= cf(&callerOffset
);
207 auto const passed
= [&]() -> bool {
208 if (assertion
.isString()) {
209 if (RuntimeOption::EvalAuthoritativeMode
) {
210 // We could support this with compile-time string literals,
211 // but it's not yet implemented.
212 throw_not_supported("assert()",
213 "assert with strings argument in RepoAuthoritative mode");
215 return eval_for_assert(fp
, assertion
.toString()).toBoolean();
217 return assertion
.toBoolean();
219 if (passed
) return true;
221 if (!s_option_data
->assertCallback
.isNull()) {
222 auto const unit
= fp
->m_func
->unit();
224 PackedArrayInit
ai(3);
225 ai
.append(String(const_cast<StringData
*>(unit
->filepath())));
226 ai
.append(Variant(unit
->getLineNumber(callerOffset
)));
227 ai
.append(assertion
.isString() ? assertion
: empty_string_variant_ref
);
228 HHVM_FN(call_user_func
)(s_option_data
->assertCallback
, ai
.toArray());
230 if (s_option_data
->assertException
) {
231 if (message
.isObject()) {
232 Object exn
= message
.toObject();
233 if (exn
.instanceof(SystemLib::s_AssertionErrorClass
)) {
238 SystemLib::throwExceptionObject(message
.toString());
240 if (s_option_data
->assertWarning
) {
241 String
name(message
.isNull() ? "Assertion" : message
.toString());
242 auto const str
= !assertion
.isString()
244 : concat3(" \"", assertion
.toString(), "\" failed");
245 raise_warning("assert(): %s%s", name
.data(), str
.data());
247 if (s_option_data
->assertBail
) {
248 throw ExitException(1);
254 static int64_t HHVM_FUNCTION(dl
, const String
& /*library*/) {
258 static bool HHVM_FUNCTION(extension_loaded
, const String
& name
) {
259 return ExtensionRegistry::isLoaded(name
);
263 HHVM_FUNCTION(get_loaded_extensions
, bool /*zend_extensions*/ /*=false */) {
264 return ExtensionRegistry::getLoaded();
267 static Variant
HHVM_FUNCTION(get_extension_funcs
, const String
& module_name
) {
268 auto extension
= ExtensionRegistry::get(module_name
);
269 if (!extension
) return Variant(false);
271 auto const& fns
= extension
->getExtensionFunctions();
272 PackedArrayInit
result(fns
.size());
273 for (auto const& fn
: fns
) {
274 result
.append(Variant(fn
));
276 return result
.toVariant();
279 static Variant
HHVM_FUNCTION(get_cfg_var
, const String
& /*option*/) {
283 static String
HHVM_FUNCTION(get_current_user
) {
285 return Process::GetCurrentUser();
287 int pwbuflen
= sysconf(_SC_GETPW_R_SIZE_MAX
);
289 return empty_string();
291 char *pwbuf
= (char*)req::malloc_noptrs(pwbuflen
);
292 SCOPE_EXIT
{ req::free(pwbuf
); };
294 struct passwd
*retpwptr
= nullptr;
295 auto uid
= [] () -> uid_t
{
296 if (auto cred
= get_cli_ucred()) return cred
->uid
;
299 if (getpwuid_r(uid
, &pw
, pwbuf
, pwbuflen
, &retpwptr
) != 0) {
300 return empty_string();
302 String
ret(pw
.pw_name
, CopyString
);
307 static Array
HHVM_FUNCTION(get_defined_constants
, bool categorize
/*=false */) {
308 return lookupDefinedConstants(categorize
);
311 static String
HHVM_FUNCTION(get_include_path
) {
312 return IniSetting::Get("include_path");
315 static void HHVM_FUNCTION(restore_include_path
) {
316 auto path
= ThreadInfo::s_threadInfo
.getNoCheck()->
317 m_reqInjectionData
.getDefaultIncludePath();
318 IniSetting::SetUser("include_path", path
);
321 static String
HHVM_FUNCTION(set_include_path
, const Variant
& new_include_path
) {
322 String s
= f_get_include_path();
323 IniSetting::SetUser("include_path", new_include_path
.toString());
327 static Array
HHVM_FUNCTION(get_included_files
) {
328 PackedArrayInit
pai(g_context
->m_evaledFilesOrder
.size());
329 for (auto& file
: g_context
->m_evaledFilesOrder
) {
330 pai
.append(Variant
{const_cast<StringData
*>(file
)});
332 return pai
.toArray();
335 static Variant
HHVM_FUNCTION(getenv
, const String
& varname
) {
336 String ret
= g_context
->getenv(varname
);
343 static Variant
HHVM_FUNCTION(getlastmod
) {
345 int ret
= ::stat(g_context
->getContainingFileName()->data(), &s
);
346 return ret
== 0 ? s
.st_mtime
: false;
349 static Variant
HHVM_FUNCTION(getmygid
) {
350 if (auto cred
= get_cli_ucred()) return (int64_t)cred
->gid
;
352 int64_t gid
= getgid();
359 static Variant
HHVM_FUNCTION(getmyinode
) {
361 int ret
= ::stat(g_context
->getContainingFileName()->data(), &s
);
362 return ret
== 0 ? s
.st_ino
: false;
365 static Variant
HHVM_FUNCTION(getmypid
) {
366 if (auto cred
= get_cli_ucred()) return cred
->pid
;
368 int64_t pid
= getpid();
375 static Variant
HHVM_FUNCTION(getmyuid
) {
376 if (auto cred
= get_cli_ucred()) return (int64_t)cred
->uid
;
378 int64_t uid
= getuid();
385 ///////////////////////////////////////////////////////////////////////////////
387 #define OPTERRCOLON (1)
389 #define OPTERRARG (3)
391 /* Define structure for one recognized option (both single char and long name).
392 * If short_open is '-' this is the last option. */
396 char* opt_name
{nullptr};
399 static int php_opt_error(const req::vector
<char*>& argv
, int oint
, int optchr
,
400 int err
, int show_err
) {
402 fprintf(stderr
, "Error in argument %d, char %d: ", oint
, optchr
+1);
405 fprintf(stderr
, ": in flags\n");
408 fprintf(stderr
, "option not found %c\n", argv
[oint
][optchr
]);
411 fprintf(stderr
, "no argument for option %c\n", argv
[oint
][optchr
]);
414 fprintf(stderr
, "unknown\n");
421 static int php_getopt(int argc
, req::vector
<char*>& argv
,
422 req::vector
<opt_struct
>& opts
,
423 char **optarg
, int *optind
, int show_err
,
424 int arg_start
, int &optchr
, int &dash
, int &php_optidx
) {
427 if (*optind
>= argc
) {
431 if ((argv
[*optind
][0] != '-')) {
434 if (!argv
[*optind
][1]) {
436 * use to specify stdin. Need to let pgm process this and
443 if ((argv
[*optind
][0] == '-') && (argv
[*optind
][1] == '-')) {
445 int arg_end
= strlen(argv
[*optind
])-1;
447 // '--' indicates end of args if not followed by a known long option name
448 if (argv
[*optind
][2] == '\0') {
455 /* Check for <arg>=<val> */
456 if ((pos
= string_memnstr(&argv
[*optind
][arg_start
], "=", 1,
457 argv
[*optind
]+arg_end
)) != nullptr) {
458 arg_end
= pos
-&argv
[*optind
][arg_start
];
465 if (opts
[php_optidx
].opt_char
== '-') {
467 return(php_opt_error(argv
, *optind
-1, optchr
, OPTERRARG
,
469 } else if (opts
[php_optidx
].opt_name
&&
470 !strncmp(&argv
[*optind
][2], opts
[php_optidx
].opt_name
,
477 arg_start
+= strlen(opts
[php_optidx
].opt_name
);
483 /* Check if the guy tries to do a -: kind of flag */
484 if (argv
[*optind
][optchr
] == ':') {
487 return (php_opt_error(argv
, *optind
-1, optchr
, OPTERRCOLON
,
490 arg_start
= 1 + optchr
;
492 if (php_optidx
< 0) {
495 if (opts
[php_optidx
].opt_char
== '-') {
496 int errind
= *optind
;
499 if (!argv
[*optind
][optchr
+1]) {
506 return(php_opt_error(argv
, errind
, errchr
, OPTERRNF
, show_err
));
507 } else if (argv
[*optind
][optchr
] == opts
[php_optidx
].opt_char
) {
512 if (opts
[php_optidx
].need_param
) {
513 /* Check for cases where the value of the argument
514 is in the form -<arg> <val>, -<arg>=<varl> or -<arg><val> */
516 if (!argv
[*optind
][arg_start
]) {
518 if (*optind
== argc
) {
519 /* Was the value required or is it optional? */
520 if (opts
[php_optidx
].need_param
== 1) {
521 return(php_opt_error(argv
, *optind
-1, optchr
, OPTERRARG
,
524 /* Optional value is not supported with -<arg> <val> style */
525 } else if (opts
[php_optidx
].need_param
== 1) {
526 *optarg
= argv
[(*optind
)++];
528 } else if (argv
[*optind
][arg_start
] == '=') {
530 *optarg
= &argv
[*optind
][arg_start
];
533 *optarg
= &argv
[*optind
][arg_start
];
536 return opts
[php_optidx
].opt_char
;
538 /* multiple options specified as one (exclude long opts) */
539 if (arg_start
>= 2 && !((argv
[*optind
][0] == '-') &&
540 (argv
[*optind
][1] == '-'))) {
541 if (!argv
[*optind
][optchr
+1])
551 return opts
[php_optidx
].opt_char
;
554 return(0); /* never reached */
557 // Free the memory allocated to an longopt array.
558 static void free_longopts(req::vector
<opt_struct
>& longopts
) {
559 for (auto& p
: longopts
) {
560 if (p
.opt_char
== '-') break;
561 req::free(p
.opt_name
);
565 // Convert the typical getopt input characters to the php_getopt struct array
566 static req::vector
<opt_struct
> parse_opts(const char *opts
, int opts_len
) {
568 for (int i
= 0; i
< opts_len
; i
++) {
569 if ((opts
[i
] >= 48 && opts
[i
] <= 57) ||
570 (opts
[i
] >= 65 && opts
[i
] <= 90) ||
571 (opts
[i
] >= 97 && opts
[i
] <= 122)) {
575 req::vector
<opt_struct
> paras(count
);
577 while ((*opts
>= 48 && *opts
<= 57) || /* 0 - 9 */
578 (*opts
>= 65 && *opts
<= 90) || /* A - Z */
579 (*opts
>= 97 && *opts
<= 122)) { /* a - z */
582 p
.need_param
= (*(++opts
) == ':') ? 1 : 0;
583 p
.opt_name
= nullptr;
584 if (p
.need_param
== 1) {
597 static Array
HHVM_FUNCTION(getopt
, const String
& options
,
598 const Variant
& longopts
/*=null */) {
599 auto opt_vec
= parse_opts(options
.data(), options
.size());
601 if (!longopts
.isNull()) {
602 Array arropts
= longopts
.toArray();
604 /* the first vec.size() slots are filled by the one short ops
605 * we now extend our array and jump to the new added structs */
606 auto i
= opt_vec
.size();
607 opt_vec
.resize(opt_vec
.size() + arropts
.size() + 1);
609 for (ArrayIter
iter(arropts
); iter
; ++iter
) {
610 String entry
= iter
.second().toString();
612 auto& opt
= opt_vec
[i
];
614 opt
.opt_name
= req::strdup(entry
.data());
615 auto len
= strlen(opt
.opt_name
);
616 if ((len
> 0) && (opt
.opt_name
[len
- 1] == ':')) {
618 opt
.opt_name
[len
- 1] = '\0';
619 if ((len
> 1) && (opt
.opt_name
[len
- 2] == ':')) {
621 opt
.opt_name
[len
- 2] = '\0';
627 assert(i
== opt_vec
.size() - 1);
629 opt_vec
.resize(opt_vec
.size() + 1);
632 /* php_getopt want to identify the last param */
633 auto& last
= opt_vec
[opt_vec
.size() - 1];
636 last
.opt_name
= nullptr;
638 static const StaticString
s_argv("argv");
639 Array vargv
= php_global(s_argv
).toArray();
640 int argc
= vargv
.size();
641 req::vector
<char*> argv(argc
+ 1);
642 req::vector
<String
> holders
;
644 for (ArrayIter
iter(vargv
); iter
; ++iter
) {
645 String arg
= iter
.second().toString();
646 holders
.push_back(arg
);
647 argv
[index
++] = (char*)arg
.data();
649 argv
[index
] = nullptr;
652 char *php_optarg
= nullptr;
656 free_longopts(opt_vec
);
659 Array ret
= Array::Create();
663 int dash
= 0; /* have already seen the - */
664 char opt
[2] = { '\0' };
668 while ((o
= php_getopt(argc
, argv
, opt_vec
, &php_optarg
, &php_optind
, 0, 1,
669 optchr
, dash
, php_optidx
))
671 /* Skip unknown arguments. */
676 /* Prepare the option character and the argument string. */
678 optname
= opt_vec
[php_optidx
].opt_name
;
687 if (php_optarg
!= nullptr) {
688 /* keep the arg as binary, since the encoding is not known */
689 val
= String(php_optarg
, CopyString
);
694 /* Add this option / argument pair to the result hash. */
695 optname_len
= strlen(optname
);
696 if (!(optname_len
> 1 && optname
[0] == '0') &&
697 is_numeric_string(optname
, optname_len
, nullptr, nullptr, 0) ==
700 int optname_int
= atoi(optname
);
701 if (ret
.exists(optname_int
)) {
702 auto const lval
= ret
.lvalAt(optname_int
).unboxed();
703 if (!isArrayLikeType(lval
.type())) {
704 ret
.set(optname_int
, make_packed_array(Variant::wrap(lval
.tv()), val
));
706 asArrRef(lval
).append(val
);
709 ret
.set(optname_int
, val
);
713 String
key(optname
, strlen(optname
), CopyString
);
714 if (ret
.exists(key
)) {
715 auto const lval
= ret
.lvalAt(key
).unboxed();
716 if (!isArrayLikeType(lval
.type())) {
717 ret
.set(key
, make_packed_array(Variant::wrap(lval
.tv()), val
));
719 asArrRef(lval
).append(val
);
726 php_optarg
= nullptr;
732 ///////////////////////////////////////////////////////////////////////////////
735 s_ru_oublock("ru_oublock"),
736 s_ru_inblock("ru_inblock"),
737 s_ru_msgsnd("ru_msgsnd"),
738 s_ru_msgrcv("ru_msgrcv"),
739 s_ru_maxrss("ru_maxrss"),
740 s_ru_ixrss("ru_ixrss"),
741 s_ru_idrss("ru_idrss"),
742 s_ru_minflt("ru_minflt"),
743 s_ru_majflt("ru_majflt"),
744 s_ru_nsignals("ru_nsignals"),
745 s_ru_nvcsw("ru_nvcsw"),
746 s_ru_nivcsw("ru_nivcsw"),
747 s_ru_nswap("ru_nswap"),
748 s_ru_utime_tv_usec("ru_utime.tv_usec"),
749 s_ru_utime_tv_sec("ru_utime.tv_sec"),
750 s_ru_stime_tv_usec("ru_stime.tv_usec"),
751 s_ru_stime_tv_sec("ru_stime.tv_sec");
753 #define PHP_RUSAGE_PARA(a) s_ ## a, (int64_t)usg.a
754 static Array
HHVM_FUNCTION(getrusage
, int64_t who
/* = 0 */) {
756 memset(&usg
, 0, sizeof(struct rusage
));
760 actual_who
= RUSAGE_CHILDREN
;
764 actual_who
= RUSAGE_THREAD
;
766 throw_not_supported(__func__
, "RUSAGE_THREAD is not defined on this sytem");
770 actual_who
= RUSAGE_SELF
;
774 if (getrusage(actual_who
, &usg
) == -1) {
775 raise_error("getrusage returned %d: %s", errno
,
776 folly::errnoStr(errno
).c_str());
779 return Array(ArrayInit(17, ArrayInit::Mixed
{}).
780 set(PHP_RUSAGE_PARA(ru_oublock
)).
781 set(PHP_RUSAGE_PARA(ru_inblock
)).
782 set(PHP_RUSAGE_PARA(ru_msgsnd
)).
783 set(PHP_RUSAGE_PARA(ru_msgrcv
)).
784 set(PHP_RUSAGE_PARA(ru_maxrss
)).
785 set(PHP_RUSAGE_PARA(ru_ixrss
)).
786 set(PHP_RUSAGE_PARA(ru_idrss
)).
787 set(PHP_RUSAGE_PARA(ru_minflt
)).
788 set(PHP_RUSAGE_PARA(ru_majflt
)).
789 set(PHP_RUSAGE_PARA(ru_nsignals
)).
790 set(PHP_RUSAGE_PARA(ru_nvcsw
)).
791 set(PHP_RUSAGE_PARA(ru_nivcsw
)).
792 set(PHP_RUSAGE_PARA(ru_nswap
)).
793 set(s_ru_utime_tv_usec
, (int64_t)usg
.ru_utime
.tv_usec
).
794 set(s_ru_utime_tv_sec
, (int64_t)usg
.ru_utime
.tv_sec
).
795 set(s_ru_stime_tv_usec
, (int64_t)usg
.ru_stime
.tv_usec
).
796 set(s_ru_stime_tv_sec
, (int64_t)usg
.ru_stime
.tv_sec
).
800 static bool HHVM_FUNCTION(clock_getres
,
801 int64_t clk_id
, VRefParam sec
, VRefParam nsec
) {
802 #if defined(__APPLE__)
803 throw_not_supported(__func__
, "feature not supported on OSX");
806 int ret
= clock_getres(clk_id
, &ts
);
807 sec
.assignIfRef((int64_t)ts
.tv_sec
);
808 nsec
.assignIfRef((int64_t)ts
.tv_nsec
);
813 static bool HHVM_FUNCTION(clock_gettime
,
814 int64_t clk_id
, VRefParam sec
, VRefParam nsec
) {
816 int ret
= gettime(clockid_t(clk_id
), &ts
);
817 sec
.assignIfRef((int64_t)ts
.tv_sec
);
818 nsec
.assignIfRef((int64_t)ts
.tv_nsec
);
822 static int64_t HHVM_FUNCTION(clock_gettime_ns
, int64_t clk_id
) {
823 return gettime_ns(clockid_t(clk_id
));
826 static int64_t HHVM_FUNCTION(cpu_get_count
) {
827 return Process::GetCPUCount();
830 static String
HHVM_FUNCTION(cpu_get_model
) {
831 return Process::GetCPUModel();
834 Variant
HHVM_FUNCTION(ini_get
, const String
& varname
) {
836 bool ret
= IniSetting::Get(varname
, value
);
838 // ret will be false if varname isn't a valid ini setting
843 // Anything other than array for ini_get can be converted to
844 // the expected string result for this function call.
845 // If value is null, an empty string is returned, which is good
847 if (!value
.isArray()) {
848 return value
.toString();
850 // For arrays, this will return an array of values. It will also return
851 // an empty array if the values for a valid collection-like configuration
852 // has not been set. This is similar to returning an empty-string for
853 // standard configuration options.
857 static Array
HHVM_FUNCTION(ini_get_all
,
858 const String
& extension
, bool detailed
) {
859 return IniSetting::GetAll(extension
, detailed
);
862 static void HHVM_FUNCTION(ini_restore
, const String
& varname
) {
863 IniSetting::RestoreUser(varname
);
866 Variant
HHVM_FUNCTION(ini_set
,
867 const String
& varname
, const Variant
& newvalue
) {
868 auto oldvalue
= f_ini_get(varname
);
869 auto ret
= IniSetting::SetUser(varname
, newvalue
);
876 static int64_t HHVM_FUNCTION(memory_get_allocation
) {
877 auto total
= tl_heap
->getStatsCopy().totalAlloc
;
882 static int64_t HHVM_FUNCTION(hphp_memory_get_interval_peak_usage
,
883 bool real_usage
/*=false */) {
884 auto const stats
= tl_heap
->getStatsCopy();
885 int64_t ret
= real_usage
? stats
.peakIntervalUsage
:
886 stats
.peakIntervalCap
;
891 static int64_t HHVM_FUNCTION(memory_get_peak_usage
,
892 bool real_usage
/*=false */) {
893 auto const stats
= tl_heap
->getStatsCopy();
894 int64_t ret
= real_usage
? stats
.peakUsage
: stats
.peakCap
;
899 static int64_t HHVM_FUNCTION(memory_get_usage
, bool real_usage
/*=false */) {
900 auto const stats
= tl_heap
->getStatsCopy();
901 int64_t ret
= real_usage
? stats
.usage() : stats
.capacity();
902 // Since we don't always alloc and dealloc a shared structure from the same
903 // thread it is possible that this can go negative when we are tracking
905 assert((use_jemalloc
&& real_usage
) || ret
>= 0);
906 return std::max
<int64_t>(ret
, 0);
909 static int64_t HHVM_FUNCTION(hphp_memory_heap_usage
) {
910 // This corresponds to PHP memory_get_usage(false), only counting
911 // allocations via MemoryManager.
912 return tl_heap
->getStatsCopy().mmUsage
;
915 static int64_t HHVM_FUNCTION(hphp_memory_heap_capacity
) {
916 // This happens to match HHVM memory_get_usage(false), and
917 // PHP memory_get_usage(true).
918 return tl_heap
->getStatsCopy().capacity();
921 static bool HHVM_FUNCTION(hphp_memory_start_interval
) {
922 return tl_heap
->startStatsInterval();
925 static bool HHVM_FUNCTION(hphp_memory_stop_interval
) {
926 return tl_heap
->stopStatsInterval();
929 const StaticString
s_srv("srv"), s_cli("cli");
931 String
HHVM_FUNCTION(php_sapi_name
) {
932 return RuntimeOption::ServerExecutionMode() && !is_cli_mode()
937 const char* php_get_edition_name(DWORD majVer
, DWORD minVer
);
938 folly::Optional
<String
> php_get_windows_name();
939 String
php_get_windows_cpu();
949 Variant
HHVM_FUNCTION(php_uname
, const String
& mode
/*="" */) {
953 } else if (mode
== s_r
) {
954 DWORD dwVersion
= GetVersion();
955 DWORD dwMajorVersion
= (DWORD
)(LOBYTE(LOWORD(dwVersion
)));
956 DWORD dwMinorVersion
= (DWORD
)(HIBYTE(LOWORD(dwVersion
)));
957 return folly::sformat("{}.{}", dwMajorVersion
, dwMinorVersion
);
958 } else if (mode
== s_n
) {
959 DWORD dwSize
= MAX_COMPUTERNAME_LENGTH
+ 1;
960 char ComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
962 GetComputerName(ComputerName
, &dwSize
);
963 return String(ComputerName
, dwSize
, CopyString
);
964 } else if (mode
== s_v
) {
965 DWORD dwVersion
= GetVersion();
966 auto dwBuild
= (DWORD
)(HIWORD(dwVersion
));
967 auto winVer
= php_get_windows_name();
968 if (!winVer
.hasValue()) {
969 return folly::sformat("build {}", dwBuild
);
971 return folly::sformat("build {} ({})", dwBuild
, winVer
.value());
972 } else if (mode
== s_m
) {
973 return php_get_windows_cpu();
975 auto winVer
= php_get_windows_name();
977 DWORD dwSize
= MAX_COMPUTERNAME_LENGTH
+ 1;
978 char ComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
979 GetComputerName(ComputerName
, &dwSize
);
981 DWORD dwVersion
= GetVersion();
982 DWORD dwMajorVersion
= (DWORD
)(LOBYTE(LOWORD(dwVersion
)));
983 DWORD dwMinorVersion
= (DWORD
)(HIBYTE(LOWORD(dwVersion
)));
984 DWORD dwBuild
= (DWORD
)(HIWORD(dwVersion
));
986 if (dwMajorVersion
== 6 && dwMinorVersion
== 2 && winVer
.hasValue()) {
987 if (strncmp(winVer
.value().c_str(), "Windows 8.1", 11) == 0 ||
988 strncmp(winVer
.value().c_str(), "Windows Server 2012 R2", 22) == 0) {
993 return folly::sformat("Windows NT {} {}.{} build {} ({}) {}",
998 winVer
.hasValue() ? winVer
.value().c_str() : "unknown",
999 php_get_windows_cpu()
1004 if (uname((struct utsname
*)&buf
) == -1) {
1009 return String(buf
.sysname
, CopyString
);
1010 } else if (mode
== s_r
) {
1011 return String(buf
.release
, CopyString
);
1012 } else if (mode
== s_n
) {
1013 return String(buf
.nodename
, CopyString
);
1014 } else if (mode
== s_v
) {
1015 return String(buf
.version
, CopyString
);
1016 } else if (mode
== s_m
) {
1017 return String(buf
.machine
, CopyString
);
1018 } else { /* assume mode == "a" */
1019 char tmp_uname
[512];
1020 snprintf(tmp_uname
, sizeof(tmp_uname
), "%s %s %s %s %s",
1021 buf
.sysname
, buf
.nodename
, buf
.release
, buf
.version
,
1023 return String(tmp_uname
, CopyString
);
1028 static Variant
HHVM_FUNCTION(phpversion
, const String
& extension
/*="" */) {
1031 if (extension
.empty()) {
1032 return get_PHP_VERSION();
1035 if ((ext
= ExtensionRegistry::get(extension
)) != nullptr &&
1036 strcmp(ext
->getVersion(), NO_EXTENSION_VERSION_YET
) != 0) {
1037 return ext
->getVersion();
1043 static bool HHVM_FUNCTION(putenv
, const String
& setting
) {
1044 int pos
= setting
.find('=');
1046 String name
= setting
.substr(0, pos
);
1047 String value
= setting
.substr(pos
+ 1);
1048 g_context
->setenv(name
, value
);
1050 g_context
->unsetenv(setting
);
1055 static void HHVM_FUNCTION(set_time_limit
, int64_t seconds
) {
1056 ThreadInfo
*info
= ThreadInfo::s_threadInfo
.getNoCheck();
1057 RequestInjectionData
&data
= info
->m_reqInjectionData
;
1058 if (RuntimeOption::TimeoutsUseWallTime
) {
1059 data
.setTimeout(seconds
);
1061 data
.setCPUTimeout(seconds
);
1065 String
HHVM_FUNCTION(sys_get_temp_dir
) {
1068 auto len
= GetTempPathA(PATH_MAX
, buf
);
1069 return String(buf
, len
, CopyString
);
1071 char *env
= getenv("TMPDIR");
1072 if (env
&& *env
) return String(env
, CopyString
);
1077 static String
HHVM_FUNCTION(zend_version
) {
1078 return s_ZEND_VERSION
;
1082 ///////////////////////////////////////////////////////////////////////////////
1084 #define sign(n) ((n)<0?-1:((n)>0?1:0))
1086 static char *php_canonicalize_version(const char *version
) {
1087 int len
= strlen(version
);
1088 char *buf
= (char*)req::malloc_noptrs(len
* 2 + 1), *q
, lp
, lq
;
1102 * s/([^\d\.])([^\D\.])/$1.$2/g;
1103 * s/([^\D\.])([^\d\.])/$1.$2/g;
1105 #define isdig(x) (isdigit(x)&&(x)!='.')
1106 #define isndig(x) (!isdigit(x)&&(x)!='.')
1107 #define isspecialver(x) ((x)=='-'||(x)=='_'||(x)=='+')
1110 if (isspecialver(*p
)) {
1114 } else if ((isndig(lp
) && isdig(*p
)) || (isdig(lp
) && isndig(*p
))) {
1119 } else if (!isalnum(*p
)) {
1132 struct special_forms_t
{
1137 static int compare_special_version_forms(const char *form1
, const char *form2
) {
1138 int found1
= -1, found2
= -1;
1139 special_forms_t special_forms
[11] = {
1152 special_forms_t
*pp
;
1154 for (pp
= special_forms
; pp
&& pp
->name
; pp
++) {
1155 if (strncmp(form1
, pp
->name
, strlen(pp
->name
)) == 0) {
1160 for (pp
= special_forms
; pp
&& pp
->name
; pp
++) {
1161 if (strncmp(form2
, pp
->name
, strlen(pp
->name
)) == 0) {
1166 return sign(found1
- found2
);
1169 static int php_version_compare(const char *orig_ver1
, const char *orig_ver2
) {
1172 char *p1
, *p2
, *n1
, *n2
;
1176 if (!*orig_ver1
|| !*orig_ver2
) {
1177 if (!*orig_ver1
&& !*orig_ver2
) {
1180 return *orig_ver1
? 1 : -1;
1183 if (orig_ver1
[0] == '#') {
1184 ver1
= req::strdup(orig_ver1
);
1186 ver1
= php_canonicalize_version(orig_ver1
);
1188 SCOPE_EXIT
{ req::free(ver1
); };
1189 if (orig_ver2
[0] == '#') {
1190 ver2
= req::strdup(orig_ver2
);
1192 ver2
= php_canonicalize_version(orig_ver2
);
1194 SCOPE_EXIT
{ req::free(ver2
); };
1197 while (*p1
&& *p2
&& n1
&& n2
) {
1198 if ((n1
= strchr(p1
, '.')) != nullptr) {
1201 if ((n2
= strchr(p2
, '.')) != nullptr) {
1204 if (isdigit(*p1
) && isdigit(*p2
)) {
1205 /* compare element numerically */
1206 l1
= strtol(p1
, nullptr, 10);
1207 l2
= strtol(p2
, nullptr, 10);
1208 compare
= sign(l1
- l2
);
1209 } else if (!isdigit(*p1
) && !isdigit(*p2
)) {
1210 /* compare element names */
1211 compare
= compare_special_version_forms(p1
, p2
);
1213 /* mix of names and numbers */
1215 compare
= compare_special_version_forms("#N#", p2
);
1217 compare
= compare_special_version_forms(p1
, "#N#");
1223 if (n1
!= nullptr) {
1226 if (n2
!= nullptr) {
1231 if (n1
!= nullptr) {
1235 compare
= php_version_compare(p1
, "#N#");
1237 } else if (n2
!= nullptr) {
1241 compare
= php_version_compare("#N#", p2
);
1248 Variant
HHVM_FUNCTION(version_compare
,
1249 const String
& version1
,
1250 const String
& version2
,
1251 const String
& sop
/*="" */) {
1252 const char *op
= sop
.data();
1253 int op_len
= sop
.size();
1254 int compare
= php_version_compare(version1
.data(), version2
.data());
1258 if (!strncmp(op
, "<", op_len
) || !strncmp(op
, "lt", op_len
)) {
1259 return compare
== -1;
1261 if (!strncmp(op
, "<=", op_len
) || !strncmp(op
, "le", op_len
)) {
1262 return compare
!= 1;
1264 if (!strncmp(op
, ">", op_len
) || !strncmp(op
, "gt", op_len
)) {
1265 return compare
== 1;
1267 if (!strncmp(op
, ">=", op_len
) || !strncmp(op
, "ge", op_len
)) {
1268 return compare
!= -1;
1270 if (!strncmp(op
, "==", op_len
) || !strncmp(op
, "=", op_len
) ||
1271 !strncmp(op
, "eq", op_len
)) {
1272 return compare
== 0;
1274 if (!strncmp(op
, "!=", op_len
) || !strncmp(op
, "<>", op_len
) ||
1275 !strncmp(op
, "ne", op_len
)) {
1276 return compare
!= 0;
1281 ///////////////////////////////////////////////////////////////////////////////
1283 void StandardExtension::initOptions() {
1284 HHVM_FE(assert_options
);
1287 HHVM_FE(extension_loaded
);
1288 HHVM_FE(get_loaded_extensions
);
1289 HHVM_FE(get_extension_funcs
);
1290 HHVM_FE(get_cfg_var
);
1291 HHVM_FE(get_current_user
);
1292 HHVM_FE(get_defined_constants
);
1293 HHVM_FE(get_include_path
);
1294 HHVM_FE(restore_include_path
);
1295 HHVM_FE(set_include_path
);
1296 HHVM_FE(get_included_files
);
1298 HHVM_FE(getlastmod
);
1300 HHVM_FE(getmyinode
);
1305 HHVM_FE(clock_getres
);
1306 HHVM_FE(clock_gettime
);
1307 HHVM_FE(clock_gettime_ns
);
1308 HHVM_FE(cpu_get_count
);
1309 HHVM_FE(cpu_get_model
);
1310 HHVM_FALIAS(ini_alter
, ini_set
);
1312 HHVM_FE(ini_get_all
);
1313 HHVM_FE(ini_restore
);
1315 HHVM_FE(memory_get_peak_usage
);
1316 HHVM_FE(memory_get_usage
);
1317 // HHVM-specific stateless accessors for memory stats
1318 HHVM_FE(hphp_memory_heap_usage
);
1319 HHVM_FE(hphp_memory_heap_capacity
);
1320 // This is HH-specific as well but code depends on the old name.
1321 HHVM_FE(memory_get_allocation
);
1322 HHVM_FE(hphp_memory_get_interval_peak_usage
);
1323 HHVM_FE(hphp_memory_start_interval
);
1324 HHVM_FE(hphp_memory_stop_interval
);
1325 HHVM_FE(php_sapi_name
);
1327 HHVM_FE(phpversion
);
1329 HHVM_FE(set_time_limit
);
1330 HHVM_FE(sys_get_temp_dir
);
1331 HHVM_FE(zend_version
);
1332 HHVM_FE(version_compare
);
1334 HHVM_RC_INT(INFO_GENERAL
, 1 << 0);
1335 HHVM_RC_INT(INFO_CREDITS
, 1 << 0);
1336 HHVM_RC_INT(INFO_CONFIGURATION
, 1 << 0);
1337 HHVM_RC_INT(INFO_MODULES
, 1 << 0);
1338 HHVM_RC_INT(INFO_ENVIRONMENT
, 1 << 0);
1339 HHVM_RC_INT(INFO_VARIABLES
, 1 << 0);
1340 HHVM_RC_INT(INFO_LICENSE
, 1 << 0);
1341 HHVM_RC_INT(INFO_ALL
, 0x7FFFFFFF);
1343 HHVM_RC_INT(ASSERT_ACTIVE
, k_ASSERT_ACTIVE
);
1344 HHVM_RC_INT(ASSERT_CALLBACK
, k_ASSERT_CALLBACK
);
1345 HHVM_RC_INT(ASSERT_BAIL
, k_ASSERT_BAIL
);
1346 HHVM_RC_INT(ASSERT_WARNING
, k_ASSERT_WARNING
);
1347 HHVM_RC_INT(ASSERT_QUIET_EVAL
, k_ASSERT_QUIET_EVAL
);
1348 HHVM_RC_INT(ASSERT_EXCEPTION
, k_ASSERT_EXCEPTION
);
1350 loadSystemlib("std_options");
1353 ///////////////////////////////////////////////////////////////////////////////