Don't return Variant& from Array functions
[hiphop-php.git] / hphp / runtime / ext / std / ext_std_options.cpp
blob2d3d9c5d0d3abe2ea06f825633d14f9e5070b808
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_options.h"
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <pwd.h>
22 #include <algorithm>
23 #include <vector>
25 #ifndef _WIN32
26 #include <sys/utsname.h>
27 #endif
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"
56 namespace HPHP {
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 {
73 assertActive = 1;
74 assertException = 0;
75 assertWarning = 1;
76 assertBail = 0;
77 assertQuietEval = false;
80 void requestShutdown() override {
81 assertCallback.unset();
84 int assertActive;
85 int assertException;
86 int assertWarning;
87 int assertBail;
88 bool assertQuietEval;
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();
112 return oldValue;
114 if (what == k_ASSERT_WARNING) {
115 int oldValue = s_option_data->assertWarning;
116 if (!value.isNull()) s_option_data->assertWarning = value.toInt64();
117 return oldValue;
119 if (what == k_ASSERT_BAIL) {
120 int oldValue = s_option_data->assertBail;
121 if (!value.isNull()) s_option_data->assertBail = value.toInt64();
122 return oldValue;
124 if (what == k_ASSERT_CALLBACK) {
125 Variant oldValue = s_option_data->assertCallback;
126 if (!value.isNull()) s_option_data->assertCallback = value;
127 return oldValue;
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);
140 return false;
143 static Variant eval_for_assert(ActRec* const curFP, const String& codeStr) {
144 String prefixedCode = concat3(
145 curFP->unit()->isHHFile() ? "<?hh return " : "<?php return ",
146 codeStr,
150 auto const oldErrorLevel =
151 s_option_data->assertQuietEval ? HHVM_FN(error_reporting)(Variant(0)) : 0;
152 SCOPE_EXIT {
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();
177 if (ctx) {
178 if (curFP->hasThis()) {
179 thiz = curFP->getThis();
180 cls = thiz->getVMClass();
181 } else {
182 cls = curFP->getClass();
185 auto const func = unit->getMain(ctx);
186 return Variant::attach(
187 g_context->invokeFunc(
188 func,
189 init_null_variant,
190 thiz,
191 cls,
192 varEnv,
193 nullptr,
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;
203 CallerFrame cf;
204 Offset callerOffset;
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();
218 }();
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)) {
234 throw_object(exn);
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()
243 ? " failed"
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);
251 return init_null();
254 static int64_t HHVM_FUNCTION(dl, const String& /*library*/) {
255 return 0;
258 static bool HHVM_FUNCTION(extension_loaded, const String& name) {
259 return ExtensionRegistry::isLoaded(name);
262 static Array
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*/) {
280 return false;
283 static String HHVM_FUNCTION(get_current_user) {
284 #ifdef _MSC_VER
285 return Process::GetCurrentUser();
286 #else
287 int pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
288 if (pwbuflen < 1) {
289 return empty_string();
291 char *pwbuf = (char*)req::malloc_noptrs(pwbuflen);
292 SCOPE_EXIT { req::free(pwbuf); };
293 struct passwd pw;
294 struct passwd *retpwptr = nullptr;
295 auto uid = [] () -> uid_t {
296 if (auto cred = get_cli_ucred()) return cred->uid;
297 return getuid();
298 }();
299 if (getpwuid_r(uid, &pw, pwbuf, pwbuflen, &retpwptr) != 0) {
300 return empty_string();
302 String ret(pw.pw_name, CopyString);
303 return ret;
304 #endif
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());
324 return s;
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);
337 if (!ret.isNull()) {
338 return ret;
340 return false;
343 static Variant HHVM_FUNCTION(getlastmod) {
344 struct stat s;
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();
353 if (gid < 0) {
354 return false;
356 return gid;
359 static Variant HHVM_FUNCTION(getmyinode) {
360 struct stat s;
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();
369 if (pid <= 0) {
370 return false;
372 return pid;
375 static Variant HHVM_FUNCTION(getmyuid) {
376 if (auto cred = get_cli_ucred()) return (int64_t)cred->uid;
378 int64_t uid = getuid();
379 if (uid < 0) {
380 return false;
382 return uid;
385 ///////////////////////////////////////////////////////////////////////////////
387 #define OPTERRCOLON (1)
388 #define OPTERRNF (2)
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. */
393 struct opt_struct {
394 char opt_char{0};
395 int need_param{0};
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) {
401 if (show_err) {
402 fprintf(stderr, "Error in argument %d, char %d: ", oint, optchr+1);
403 switch (err) {
404 case OPTERRCOLON:
405 fprintf(stderr, ": in flags\n");
406 break;
407 case OPTERRNF:
408 fprintf(stderr, "option not found %c\n", argv[oint][optchr]);
409 break;
410 case OPTERRARG:
411 fprintf(stderr, "no argument for option %c\n", argv[oint][optchr]);
412 break;
413 default:
414 fprintf(stderr, "unknown\n");
415 break;
418 return('?');
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) {
425 php_optidx = -1;
427 if (*optind >= argc) {
428 return(EOF);
430 if (!dash) {
431 if ((argv[*optind][0] != '-')) {
432 return(EOF);
433 } else {
434 if (!argv[*optind][1]) {
436 * use to specify stdin. Need to let pgm process this and
437 * the following args
439 return(EOF);
443 if ((argv[*optind][0] == '-') && (argv[*optind][1] == '-')) {
444 const char *pos;
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') {
449 (*optind)++;
450 return(EOF);
453 arg_start = 2;
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];
459 arg_start++;
463 while (1) {
464 php_optidx++;
465 if (opts[php_optidx].opt_char == '-') {
466 (*optind)++;
467 return(php_opt_error(argv, *optind-1, optchr, OPTERRARG,
468 show_err));
469 } else if (opts[php_optidx].opt_name &&
470 !strncmp(&argv[*optind][2], opts[php_optidx].opt_name,
471 arg_end)) {
472 break;
475 optchr = 0;
476 dash = 0;
477 arg_start += strlen(opts[php_optidx].opt_name);
478 } else {
479 if (!dash) {
480 dash = 1;
481 optchr = 1;
483 /* Check if the guy tries to do a -: kind of flag */
484 if (argv[*optind][optchr] == ':') {
485 dash = 0;
486 (*optind)++;
487 return (php_opt_error(argv, *optind-1, optchr, OPTERRCOLON,
488 show_err));
490 arg_start = 1 + optchr;
492 if (php_optidx < 0) {
493 while (1) {
494 php_optidx++;
495 if (opts[php_optidx].opt_char == '-') {
496 int errind = *optind;
497 int errchr = optchr;
499 if (!argv[*optind][optchr+1]) {
500 dash = 0;
501 (*optind)++;
502 } else {
503 optchr++;
504 arg_start++;
506 return(php_opt_error(argv, errind, errchr, OPTERRNF, show_err));
507 } else if (argv[*optind][optchr] == opts[php_optidx].opt_char) {
508 break;
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> */
515 dash = 0;
516 if (!argv[*optind][arg_start]) {
517 (*optind)++;
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,
522 show_err));
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] == '=') {
529 arg_start++;
530 *optarg = &argv[*optind][arg_start];
531 (*optind)++;
532 } else {
533 *optarg = &argv[*optind][arg_start];
534 (*optind)++;
536 return opts[php_optidx].opt_char;
537 } else {
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])
543 dash = 0;
544 (*optind)++;
545 } else {
546 optchr++;
548 } else {
549 (*optind)++;
551 return opts[php_optidx].opt_char;
553 assert(false);
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) {
567 int count = 0;
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)) {
572 count++;
575 req::vector<opt_struct> paras(count);
576 int i = 0;
577 while ((*opts >= 48 && *opts <= 57) || /* 0 - 9 */
578 (*opts >= 65 && *opts <= 90) || /* A - Z */
579 (*opts >= 97 && *opts <= 122)) { /* a - z */
580 auto& p = paras[i];
581 p.opt_char = *opts;
582 p.need_param = (*(++opts) == ':') ? 1 : 0;
583 p.opt_name = nullptr;
584 if (p.need_param == 1) {
585 opts++;
586 if (*opts == ':') {
587 p.need_param++;
588 opts++;
591 ++i;
593 assert(i == count);
594 return paras;
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];
613 opt.need_param = 0;
614 opt.opt_name = req::strdup(entry.data());
615 auto len = strlen(opt.opt_name);
616 if ((len > 0) && (opt.opt_name[len - 1] == ':')) {
617 opt.need_param++;
618 opt.opt_name[len - 1] = '\0';
619 if ((len > 1) && (opt.opt_name[len - 2] == ':')) {
620 opt.need_param++;
621 opt.opt_name[len - 2] = '\0';
624 opt.opt_char = 0;
625 ++i;
627 assert(i == opt_vec.size() - 1);
628 } else {
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];
634 last.opt_char = '-';
635 last.need_param = 0;
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;
643 int index = 0;
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;
651 int o;
652 char *php_optarg = nullptr;
653 int php_optind = 1;
655 SCOPE_EXIT {
656 free_longopts(opt_vec);
659 Array ret = Array::Create();
661 Variant val;
662 int optchr = 0;
663 int dash = 0; /* have already seen the - */
664 char opt[2] = { '\0' };
665 char *optname;
666 int optname_len = 0;
667 int php_optidx;
668 while ((o = php_getopt(argc, argv, opt_vec, &php_optarg, &php_optind, 0, 1,
669 optchr, dash, php_optidx))
670 != -1) {
671 /* Skip unknown arguments. */
672 if (o == '?') {
673 continue;
676 /* Prepare the option character and the argument string. */
677 if (o == 0) {
678 optname = opt_vec[php_optidx].opt_name;
679 } else {
680 if (o == 1) {
681 o = '-';
683 opt[0] = o;
684 optname = opt;
687 if (php_optarg != nullptr) {
688 /* keep the arg as binary, since the encoding is not known */
689 val = String(php_optarg, CopyString);
690 } else {
691 val = false;
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) ==
698 KindOfInt64) {
699 /* numeric string */
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));
705 } else {
706 asArrRef(lval).append(val);
708 } else {
709 ret.set(optname_int, val);
711 } else {
712 /* other strings */
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));
718 } else {
719 asArrRef(lval).append(val);
721 } else {
722 ret.set(key, val);
726 php_optarg = nullptr;
729 return ret;
732 ///////////////////////////////////////////////////////////////////////////////
734 const StaticString
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 */) {
755 struct rusage usg;
756 memset(&usg, 0, sizeof(struct rusage));
757 int actual_who;
758 switch (who) {
759 case 1:
760 actual_who = RUSAGE_CHILDREN;
761 break;
762 case 2:
763 #ifdef RUSAGE_THREAD
764 actual_who = RUSAGE_THREAD;
765 #else
766 throw_not_supported(__func__, "RUSAGE_THREAD is not defined on this sytem");
767 #endif
768 break;
769 default:
770 actual_who = RUSAGE_SELF;
771 break;
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).
797 toArray());
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");
804 #else
805 struct timespec ts;
806 int ret = clock_getres(clk_id, &ts);
807 sec.assignIfRef((int64_t)ts.tv_sec);
808 nsec.assignIfRef((int64_t)ts.tv_nsec);
809 return ret == 0;
810 #endif
813 static bool HHVM_FUNCTION(clock_gettime,
814 int64_t clk_id, VRefParam sec, VRefParam nsec) {
815 struct timespec ts;
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);
819 return ret == 0;
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) {
835 Variant value;
836 bool ret = IniSetting::Get(varname, value);
838 // ret will be false if varname isn't a valid ini setting
839 if (!ret) {
840 return false;
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
846 // and expected.
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.
854 return value;
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);
870 if (!ret) {
871 return false;
873 return oldvalue;
876 static int64_t HHVM_FUNCTION(memory_get_allocation) {
877 auto total = tl_heap->getStatsCopy().totalAlloc;
878 assert(total >= 0);
879 return total;
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;
887 assert(ret >= 0);
888 return ret;
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;
895 assert(ret >= 0);
896 return ret;
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
904 // jemalloc stats.
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()
933 ? s_srv : s_cli;
936 #ifdef _WIN32
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();
940 #endif
942 const StaticString
943 s_s("s"),
944 s_r("r"),
945 s_n("n"),
946 s_v("v"),
947 s_m("m");
949 Variant HHVM_FUNCTION(php_uname, const String& mode /*="" */) {
950 #ifdef _WIN32
951 if (mode == s_s) {
952 return s_Windows_NT;
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();
974 } else {
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) {
989 dwMinorVersion = 3;
993 return folly::sformat("Windows NT {} {}.{} build {} ({}) {}",
994 ComputerName,
995 dwMajorVersion,
996 dwMinorVersion,
997 dwBuild,
998 winVer.hasValue() ? winVer.value().c_str() : "unknown",
999 php_get_windows_cpu()
1002 #else
1003 struct utsname buf;
1004 if (uname((struct utsname *)&buf) == -1) {
1005 return init_null();
1008 if (mode == s_s) {
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,
1022 buf.machine);
1023 return String(tmp_uname, CopyString);
1025 #endif
1028 static Variant HHVM_FUNCTION(phpversion, const String& extension /*="" */) {
1029 Extension *ext;
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();
1040 return false;
1043 static bool HHVM_FUNCTION(putenv, const String& setting) {
1044 int pos = setting.find('=');
1045 if (pos >= 0) {
1046 String name = setting.substr(0, pos);
1047 String value = setting.substr(pos + 1);
1048 g_context->setenv(name, value);
1049 } else {
1050 g_context->unsetenv(setting);
1052 return true;
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);
1060 } else {
1061 data.setCPUTimeout(seconds);
1065 String HHVM_FUNCTION(sys_get_temp_dir) {
1066 #ifdef WIN32
1067 char buf[PATH_MAX];
1068 auto len = GetTempPathA(PATH_MAX, buf);
1069 return String(buf, len, CopyString);
1070 #else
1071 char *env = getenv("TMPDIR");
1072 if (env && *env) return String(env, CopyString);
1073 return s_SLASH_TMP;
1074 #endif
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;
1089 const char *p;
1091 if (len == 0) {
1092 *buf = '\0';
1093 return buf;
1096 p = version;
1097 q = buf;
1098 *q++ = lp = *p++;
1099 lq = '\0';
1100 while (*p) {
1101 /* s/[-_+]/./g;
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)=='+')
1109 lq = *(q - 1);
1110 if (isspecialver(*p)) {
1111 if (lq != '.') {
1112 lq = *q++ = '.';
1114 } else if ((isndig(lp) && isdig(*p)) || (isdig(lp) && isndig(*p))) {
1115 if (lq != '.') {
1116 *q++ = '.';
1118 lq = *q++ = *p;
1119 } else if (!isalnum(*p)) {
1120 if (lq != '.') {
1121 lq = *q++ = '.';
1123 } else {
1124 lq = *q++ = *p;
1126 lp = *p++;
1128 *q++ = '\0';
1129 return buf;
1132 struct special_forms_t {
1133 const char *name;
1134 int order;
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] = {
1140 {"dev", 0},
1141 {"alpha", 1},
1142 {"a", 1},
1143 {"beta", 2},
1144 {"b", 2},
1145 {"RC", 3},
1146 {"rc", 3},
1147 {"#", 4},
1148 {"pl", 5},
1149 {"p", 5},
1150 {nullptr, 0},
1152 special_forms_t *pp;
1154 for (pp = special_forms; pp && pp->name; pp++) {
1155 if (strncmp(form1, pp->name, strlen(pp->name)) == 0) {
1156 found1 = pp->order;
1157 break;
1160 for (pp = special_forms; pp && pp->name; pp++) {
1161 if (strncmp(form2, pp->name, strlen(pp->name)) == 0) {
1162 found2 = pp->order;
1163 break;
1166 return sign(found1 - found2);
1169 static int php_version_compare(const char *orig_ver1, const char *orig_ver2) {
1170 char *ver1;
1171 char *ver2;
1172 char *p1, *p2, *n1, *n2;
1173 long l1, l2;
1174 int compare = 0;
1176 if (!*orig_ver1 || !*orig_ver2) {
1177 if (!*orig_ver1 && !*orig_ver2) {
1178 return 0;
1179 } else {
1180 return *orig_ver1 ? 1 : -1;
1183 if (orig_ver1[0] == '#') {
1184 ver1 = req::strdup(orig_ver1);
1185 } else {
1186 ver1 = php_canonicalize_version(orig_ver1);
1188 SCOPE_EXIT { req::free(ver1); };
1189 if (orig_ver2[0] == '#') {
1190 ver2 = req::strdup(orig_ver2);
1191 } else {
1192 ver2 = php_canonicalize_version(orig_ver2);
1194 SCOPE_EXIT { req::free(ver2); };
1195 p1 = n1 = ver1;
1196 p2 = n2 = ver2;
1197 while (*p1 && *p2 && n1 && n2) {
1198 if ((n1 = strchr(p1, '.')) != nullptr) {
1199 *n1 = '\0';
1201 if ((n2 = strchr(p2, '.')) != nullptr) {
1202 *n2 = '\0';
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);
1212 } else {
1213 /* mix of names and numbers */
1214 if (isdigit(*p1)) {
1215 compare = compare_special_version_forms("#N#", p2);
1216 } else {
1217 compare = compare_special_version_forms(p1, "#N#");
1220 if (compare != 0) {
1221 break;
1223 if (n1 != nullptr) {
1224 p1 = n1 + 1;
1226 if (n2 != nullptr) {
1227 p2 = n2 + 1;
1230 if (compare == 0) {
1231 if (n1 != nullptr) {
1232 if (isdigit(*p1)) {
1233 compare = 1;
1234 } else {
1235 compare = php_version_compare(p1, "#N#");
1237 } else if (n2 != nullptr) {
1238 if (isdigit(*p2)) {
1239 compare = -1;
1240 } else {
1241 compare = php_version_compare("#N#", p2);
1245 return compare;
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());
1255 if (sop.empty()) {
1256 return compare;
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;
1278 return init_null();
1281 ///////////////////////////////////////////////////////////////////////////////
1283 void StandardExtension::initOptions() {
1284 HHVM_FE(assert_options);
1285 HHVM_FE(assert);
1286 HHVM_FE(dl);
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);
1297 HHVM_FE(getenv);
1298 HHVM_FE(getlastmod);
1299 HHVM_FE(getmygid);
1300 HHVM_FE(getmyinode);
1301 HHVM_FE(getmypid);
1302 HHVM_FE(getmyuid);
1303 HHVM_FE(getopt);
1304 HHVM_FE(getrusage);
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);
1311 HHVM_FE(ini_get);
1312 HHVM_FE(ini_get_all);
1313 HHVM_FE(ini_restore);
1314 HHVM_FE(ini_set);
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);
1326 HHVM_FE(php_uname);
1327 HHVM_FE(phpversion);
1328 HHVM_FE(putenv);
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 ///////////////////////////////////////////////////////////////////////////////