prod/native/libphpbridge/code/OpCache.cpp (70 lines of code) (raw):

/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "AutoZval.h" #include "PhpBridge.h" #include "LoggerInterface.h" #include "main/SAPI.h" #include "Zend/zend_ini.h" #include "Zend/zend_globals_macros.h" #include "ext/opcache/ZendAccelerator.h" namespace elasticapm::php { bool PhpBridge::isOpcacheEnabled() const { using namespace std::string_view_literals; return getPhpSapiName() == "cli"sv ? INI_BOOL("opcache.enable_cli") : INI_BOOL("opcache.enable"); } bool PhpBridge::detectOpcachePreload() const { if (!isOpcacheEnabled()) { return false; } const char *preloadValue = INI_STR("opcache.preload"); if (!preloadValue || strlen(preloadValue) == 0) { return false; } return sapi_module.activate == nullptr && sapi_module.deactivate == nullptr && sapi_module.register_server_variables == nullptr && sapi_module.getenv == nullptr; // EG(error_reporting) == 0 is null only in request init scope } bool PhpBridge::isScriptRestricedByOpcacheAPI() const { if (!isOpcacheEnabled()) { return false; } char *restrict_api = INI_STR("opcache.restrict_api"); if (!restrict_api || strlen(restrict_api) == 0) { return false; } size_t len = strlen(restrict_api); if (!SG(request_info).path_translated || strlen(SG(request_info).path_translated) < len || memcmp(SG(request_info).path_translated, restrict_api, len) != 0) { ELOGF_WARNING(log_, REQUEST, "Script '%s' is restricted by \"opcache.restrict_api\" configuration directive. Can't perform any opcache API calls.", SG(request_info).path_translated); return true; } return false; } bool PhpBridge::detectOpcacheRestartPending() const { if (!isOpcacheEnabled()) { return false; } if (EG(function_table) && !zend_hash_str_find_ptr(EG(function_table), ZEND_STRL("opcache_get_status"))) { return false; } AutoZval rv; rv.setNull(); AutoZval parameters{false}; int originalErrorReportingState = EG(error_reporting); // suppress error/warning reporing EG(error_reporting) = 0; bool result = callMethod(NULL, "opcache_get_status", parameters.get(), 1, rv.get()); EG(error_reporting) = originalErrorReportingState; if (!result) { ELOGF_ERROR(log_, REQUEST, "opcache_get_status failure"); return false; } if (Z_TYPE(*rv) != IS_ARRAY) { ELOGF_DEBUG(log_, REQUEST, "opcache_get_status failed, rvtype: %d", Z_TYPE(*rv)); return false; } zval *restartPending = zend_hash_str_find(Z_ARRVAL(*rv), ZEND_STRL("restart_pending")); if (restartPending && Z_TYPE_P(restartPending) == IS_TRUE) { return true; } else if (!restartPending || Z_TYPE_P(restartPending) != IS_FALSE) { ELOGF_DEBUG(log_, REQUEST, "opcache_get_status returned unexpected data ptr: %p t:%d", restartPending, restartPending ? Z_TYPE_P(restartPending) : -1); } return false; } }