Skip to content

Commit

Permalink
Add php 8.4 compatibility (#1255)
Browse files Browse the repository at this point in the history
  • Loading branch information
julien-maurel authored Jan 16, 2025
1 parent d04bc88 commit 032adca
Show file tree
Hide file tree
Showing 54 changed files with 743 additions and 149 deletions.
7 changes: 6 additions & 1 deletion .ci/generate_package_lifecycle_test_matrix.sh
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,13 @@ function generateAgentUpgradeRows () {
local testingType=agent-upgrade
local appHostKindShortName=all
local testsGroup=smoke
for phpVersion in 7.4 ; do
for linuxPackageType in rpm ; do
echo "${phpVersion},${linuxPackageType},${testingType},${appHostKindShortName},${testsGroup}"
done
done
for phpVersion in 7.4 "$(latestSupportedPhpVersion)" ; do
for linuxPackageType in deb rpm ; do
for linuxPackageType in deb ; do
echo "${phpVersion},${linuxPackageType},${testingType},${appHostKindShortName},${testsGroup}"
done
done
Expand Down
3 changes: 2 additions & 1 deletion .ci/packer_cache.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ php:8.0-fpm
php:8.1-fpm
php:8.2-fpm
php:8.3-fpm
php:8.4-fpm
ruby:2.7.1-alpine3.12
ubuntu:20.04
"
Expand All @@ -26,7 +27,7 @@ if [ -x "$(command -v docker)" ]; then

# Make sure list of PHP versions supported by the Elastic APM PHP Agent is in sync.
# See the comment in .ci/shared.sh
for version in 7.2 7.3 7.4 8.0 8.1 8.2 8.3
for version in 7.2 7.3 7.4 8.0 8.1 8.2 8.3 8.4
do
PHP_VERSION=${version} make -f .ci/Makefile prepare || true
DOCKERFILE=Dockerfile.alpine PHP_VERSION=${version} make -f .ci/Makefile prepare || true
Expand Down
2 changes: 1 addition & 1 deletion .ci/shared.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ set -e
# *) docker-compose.yml in packaging/test

#
export ELASTIC_APM_PHP_TESTS_SUPPORTED_PHP_VERSIONS=(7.2 7.3 7.4 8.0 8.1 8.2 8.3)
export ELASTIC_APM_PHP_TESTS_SUPPORTED_PHP_VERSIONS=(7.2 7.3 7.4 8.0 8.1 8.2 8.3 8.4)

export ELASTIC_APM_PHP_TESTS_SUPPORTED_LINUX_NATIVE_PACKAGE_TYPES=(apk deb rpm)
export ELASTIC_APM_PHP_TESTS_SUPPORTED_LINUX_PACKAGE_TYPES=("${ELASTIC_APM_PHP_TESTS_SUPPORTED_LINUX_NATIVE_PACKAGE_TYPES[@]}" tar)
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/loop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
- "8.1"
- "8.2"
- "8.3"
- "8.4"
dockerfile:
- "Dockerfile"
- "Dockerfile.alpine"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/phpt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
- "8.1"
- "8.2"
- "8.3"
- "8.4"
steps:
# - uses: actions/checkout@v4
# - name: Fetch and extract latest release of apm-agent-php
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ jobs:
- "8.1"
- "8.2"
- "8.3"
- "8.4"
data: ${{ fromJson(needs.setup-build-matrix.outputs.matrix-combinations).include }}
env:
PHP_VERSION: ${{ matrix.php-version }}
Expand Down Expand Up @@ -90,6 +91,7 @@ jobs:
- "8.1"
- "8.2"
- "8.3"
- "8.4"
dockerfile:
- "Dockerfile"
- "Dockerfile.alpine"
Expand Down
3 changes: 2 additions & 1 deletion agent/native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ include(elastic_conan_debugsymbols)

# Install project dependencies

set(_supported_php_versions 72 73 74 80 81 82 83)
set(_supported_php_versions 72 73 74 80 81 82 83 84)

function(get_php_api_from_release php_version ret_val)
block(SCOPE_FOR VARIABLES)
Expand All @@ -54,6 +54,7 @@ function(get_php_api_from_release php_version ret_val)
set(_php_release_81 20210902)
set(_php_release_82 20220829)
set(_php_release_83 20230831)
set(_php_release_84 20240924)

set(${ret_val} ${_php_release_${php_version}})
return(PROPAGATE ${ret_val})
Expand Down
37 changes: 37 additions & 0 deletions agent/native/building/dependencies/php84/conandata.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: "php-headers-84"
version: "1.0"
php_source_version: 8.4.1

sources:
7.2.34:
linux:
- url: "https://www.php.net/distributions/php-7.2.34.tar.gz"
contentsRoot: "php-7.2.34"
7.3.33:
linux:
- url: "https://www.php.net/distributions/php-7.3.33.tar.gz"
contentsRoot: "php-7.3.33"
7.4.33:
linux:
- url: "https://www.php.net/distributions/php-7.4.33.tar.gz"
contentsRoot: "php-7.4.33"
8.0.28:
linux:
- url: "https://www.php.net/distributions/php-8.0.28.tar.gz"
contentsRoot: "php-8.0.28"
8.1.18:
linux:
- url: "https://www.php.net/distributions/php-8.1.18.tar.gz"
contentsRoot: "php-8.1.18"
8.2.5:
linux:
- url: "https://www.php.net/distributions/php-8.2.5.tar.gz"
contentsRoot: "php-8.2.5"
8.3.2:
linux:
- url: "https://www.php.net/distributions/php-8.3.2.tar.gz"
contentsRoot: "php-8.3.2"
8.4.1:
linux:
- url: "https://www.php.net/distributions/php-8.4.1.tar.gz"
contentsRoot: "php-8.4.1"
56 changes: 56 additions & 0 deletions agent/native/building/dependencies/php84/conanfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import os
import shutil

from conans import tools, ConanFile, AutoToolsBuildEnvironment

class PhpHeadersForPHP81Conan(ConanFile):
description = "PHP headers package required to build Elastic APM agent without additional PHP dependencies"
license = "The PHP License, version 3.01"
homepage = "https://php.net/"
url = "https://php.net/"
author = "[email protected]"

settings = "os", "compiler", "build_type", "arch"
platform = "linux"

def init(self):
self.name = self.conan_data["name"]
self.version = self.conan_data["version"] # version of the package
self.php_version = self.conan_data["php_source_version"] # version of the PHP to build
self.source_temp_dir = "php-src"

def requirements(self):
self.requires("libxml2/2.9.9")
self.requires("sqlite3/3.29.0")

def source(self):
for source in self.conan_data["sources"][self.php_version][self.platform]:

if "contentsRoot" in source:
# small hack - it can't contain custom fields, so we're removing it from source (got an unexpected keyword argument)
contentRoot = source["contentsRoot"]
del source["contentsRoot"]
tools.get(**source)
os.rename(contentRoot, self.source_temp_dir)
else:
self.output.error("Could not find 'contentsRoot' in conandata.yml")
raise Exception("Could not find 'contentsRoot' in conandata.yml")

def build(self):
with tools.chdir(os.path.join(self.source_folder, self.source_temp_dir)):
buildEnv = AutoToolsBuildEnvironment(self)
envVariables = buildEnv.vars
envVariables['ac_cv_php_xml2_config_path'] = os.path.join(self.deps_cpp_info["libxml2"].rootpath, "bin/xml2-config")
envVariables['LIBXML_LIBS'] = os.path.join(self.deps_cpp_info["libxml2"].rootpath, self.deps_cpp_info["libxml2"].libdirs[0])
envVariables['LIBXML_CFLAGS'] = "-I{}".format(os.path.join(self.deps_cpp_info["libxml2"].rootpath, self.deps_cpp_info["libxml2"].includedirs[0]))
envVariables['SQLITE_LIBS'] = os.path.join(self.deps_cpp_info["sqlite3"].rootpath, self.deps_cpp_info["sqlite3"].libdirs[0])
envVariables['SQLITE_CFLAGS'] = "-I{}".format(os.path.join(self.deps_cpp_info["sqlite3"].rootpath, self.deps_cpp_info["sqlite3"].includedirs[0]))
self.run("./buildconf --force")
buildEnv.configure(args=[""], vars=envVariables, build=False, host=False)

def package(self):
source = os.path.join(self.source_folder, self.source_temp_dir)
self.copy("*.h", src=source, dst='include', keep_path=True)

def package_id(self):
del self.info.settings.compiler.version
7 changes: 6 additions & 1 deletion agent/native/ext/AST_debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,16 @@ String zendAstKindToString( zend_ast_kind kind )
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_TYPE_INTERSECTION );
#endif


#if PHP_VERSION_ID >= ELASTIC_APM_BUILD_PHP_VERSION_ID( 8, 3, 0 ) /* if PHP version from 8.3.0 */
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_MODIFIER_LIST );
#endif

#if PHP_VERSION_ID >= ELASTIC_APM_BUILD_PHP_VERSION_ID( 8, 4, 0 ) /* if PHP version from 8.4.0 */
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PARENT_PROPERTY_HOOK_CALL );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PROPERTY_HOOK );
ELASTIC_APM_GEN_ENUM_TO_STRING_SWITCH_CASE( ZEND_AST_PROPERTY_HOOK_SHORT_BODY );
#endif

default:
return NULL;
}
Expand Down
42 changes: 34 additions & 8 deletions agent/native/ext/AST_instrumentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,25 +181,47 @@ bool isZendAstListKind( zend_ast_kind kind )
}

/**
* zend_ast_create and zend_ast_create_ex allowed up to 4 child* parameters for version before PHP v8
* and the limit was increased to 5 in PHP v8
* Max number of children for AST nodes is
* 4 for PHP before 8.0
* 5 for PHP from 8.0 but before 8.4
* 6 for PHP from 8.4
*
* @see ZEND_AST_SPEC_CALL_EX
*
* When adding support for a new PHP version:
* - Make sure g_astNodeMaxChildCount is correct
* - If g_astNodeMaxChildCount changed then update createAstEx()
* - Make sure zendAstKindToString() in AST_debug.cpp includes all the enum cases from enum _zend_ast_kind in <php-src>/Zend/zend_ast.h
* - Increment minor part of PHP version in static_assert below
*/
static size_t g_maxCreateAstChildCount =
static_assert(
PHP_VERSION_ID < ELASTIC_APM_BUILD_PHP_VERSION_ID( 8, 5, 0 ),
"Make sure g_astNodeMaxChildCount is correct. See max number of children in enum _zend_ast_kind in <php-src>/Zend/zend_ast.h"
);
static constexpr size_t g_astNodeMaxChildCount =
#if PHP_VERSION_ID < ELASTIC_APM_BUILD_PHP_VERSION_ID( 8, 0, 0 )
4 // PHP before 8.0
#elif PHP_VERSION_ID < ELASTIC_APM_BUILD_PHP_VERSION_ID( 8, 4, 0 )
5 // PHP from 8.0 but before 8.4
#else
6 // PHP from 8.4
#endif
;
static constexpr size_t g_zendKindWithLargestChildNodesCount =
#if PHP_VERSION_ID < ELASTIC_APM_BUILD_PHP_VERSION_ID( 8, 0, 0 )
4
ZEND_AST_FOR // PHP before 8.0
#else
5
ZEND_AST_PARAM // PHP from 8.0
#endif
;
static_assert(g_zendKindWithLargestChildNodesCount == (g_astNodeMaxChildCount << ZEND_AST_NUM_CHILDREN_SHIFT));

zend_ast* createAstEx( zend_ast_kind kind, zend_ast_attr attr, ZendAstPtrArrayView children )
{
char txtOutStreamBuf[ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE];
TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf );

ELASTIC_APM_ASSERT_LE_UINT64( children.count, g_maxCreateAstChildCount );
ELASTIC_APM_ASSERT_LE_UINT64( children.count, g_astNodeMaxChildCount );
ELASTIC_APM_ASSERT( ! isZendAstListKind( kind ), "kind: %s", streamZendAstKind( kind, &txtOutStream ) );

switch( children.count )
Expand All @@ -218,6 +240,10 @@ zend_ast* createAstEx( zend_ast_kind kind, zend_ast_attr attr, ZendAstPtrArrayVi
case 5:
return zend_ast_create_ex( kind, attr, children.values[ 0 ], children.values[ 1 ], children.values[ 2 ], children.values[ 3 ], children.values[ 4 ] );
#endif
#if PHP_VERSION_ID >= ELASTIC_APM_BUILD_PHP_VERSION_ID( 8, 4, 0 )
case 6:
return zend_ast_create_ex( kind, attr, children.values[ 0 ], children.values[ 1 ], children.values[ 2 ], children.values[ 3 ], children.values[ 4 ], children.values[ 5 ] );
#endif
default: // silence compiler warning
return nullptr;
}
Expand All @@ -227,9 +253,9 @@ ResultCode createAstExCheckChildrenCount( zend_ast_kind kind, zend_ast_attr attr
{
ResultCode resultCode;

if ( children.count > g_maxCreateAstChildCount )
if ( children.count > g_astNodeMaxChildCount )
{
ELASTIC_APM_LOG_ERROR( "Number of children is larger than max; children.count: %u, g_maxCreateAstChildCount: %u", (unsigned)children.count, (unsigned)g_maxCreateAstChildCount );
ELASTIC_APM_LOG_ERROR( "Number of children is larger than max; children.count: %u, g_astNodeMaxChildCount: %u", (unsigned)children.count, (unsigned)g_astNodeMaxChildCount );
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE_EX( resultFailure );
}

Expand Down
2 changes: 1 addition & 1 deletion agent/native/ext/tests_util/tests_util.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

declare(strict_types=1);

error_reporting(E_ALL | E_STRICT);
error_reporting(E_ALL);

function elasticApmOnAssertFailure(string $condDesc, string $expr, $actual, $expected)
{
Expand Down
5 changes: 3 additions & 2 deletions agent/native/loader/code/phpdetection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ bool isThreadSafe() {

std::tuple<std::string_view, int, bool> getZendModuleApiVersion(std::string_view zendVersion) {
using namespace std::string_view_literals;
constexpr size_t knownVersionsCount = 16;
constexpr size_t knownVersionsCount = 17;

constexpr std::array<std::tuple<std::string_view, int, bool>, knownVersionsCount> knownPhpVersions {{
{"4.4"sv, 20240924, true}, // PHP 8.4
{"4.3"sv, 20230831, true}, // PHP 8.3
{"4.2"sv, 20220829, true}, // PHP 8.2
{"4.1"sv, 20210902, true}, // PHP 8.1
Expand Down Expand Up @@ -91,4 +92,4 @@ std::tuple<std::string_view, int, bool> getZendModuleApiVersion(std::string_view



}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public function setOpt(int $option, $value): bool

public function asInt(): int
{
/** @phpstan-ignore-next-line */
return is_resource($this->curlHandle) ? intval($this->curlHandle) : spl_object_id($this->curlHandle);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,7 @@ private function discoverStartTime(float $requestInitStartTime): float
return $requestInitStartTime;
}

/** @phpstan-ignore-next-line */
$serverRequestTimeInSeconds = floatval($serverRequestTimeAsString);
$serverRequestTimeInMicroseconds = $serverRequestTimeInSeconds * TimeUtil::NUMBER_OF_MICROSECONDS_IN_SECOND;
if ($requestInitStartTime < $serverRequestTimeInMicroseconds) {
Expand Down
1 change: 1 addition & 0 deletions agent/php/ElasticApm/Impl/Config/IniRawSnapshotSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ private static function iniValueToString($iniValue): string
return $iniValue ? 'true' : 'false';
}

/** @phpstan-ignore-next-line */
return strval($iniValue);
}
}
13 changes: 8 additions & 5 deletions agent/php/ElasticApm/Impl/InferredSpansManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,14 @@ private function onNewCurrentTransactionHasBegun(Transaction $transaction): void
($assertProxy = Assert::ifEnabled())
&& $assertProxy->that($this->onCurrentSpanChangedCallback === null)
&& $assertProxy->withContext('$this->onCurrentSpanChangedCallback === null', ['this' => $this]);
$this->currentTransaction->onCurrentSpanChanged->add(
$this->onCurrentSpanChangedCallback = function (?Span $span): void {
$this->onCurrentSpanChanged($span);
}
);

if ($this->currentTransaction !== null) {
$this->currentTransaction->onCurrentSpanChanged->add(
$this->onCurrentSpanChangedCallback = function (?Span $span): void {
$this->onCurrentSpanChanged($span);
}
);
}

$this->builder = new InferredSpansBuilder($this->tracer);
$this->state = self::STATE_RUNNING;
Expand Down
1 change: 1 addition & 0 deletions agent/php/ElasticApm/Impl/Log/LoggableToJsonEncodable.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ public static function convert($value, int $depth)
return self::convertObject($value, $depth);
}

/** @phpstan-ignore-next-line */
return [LogConsts::TYPE_KEY => DbgUtil::getType($value), LogConsts::VALUE_AS_STRING_KEY => strval($value)];
}

Expand Down
19 changes: 10 additions & 9 deletions agent/php/ElasticApm/Impl/Util/ArrayUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,18 @@ final class ArrayUtil
use StaticClassTrait;

/**
* @param string $key
* @param array<mixed> $array
* @param mixed $valueDst
* @template TKey of array-key
* @template TValue
*
* @return bool
* @param TKey $key
* @param array<TKey, TValue> $array
* @param TValue &$valueDst
*
* @template T
* @phpstan-param T[] $array
* @phpstan-param T $valueDst
* @param-out TValue $valueDst
*
* @return bool
*/
public static function getValueIfKeyExists(string $key, array $array, &$valueDst): bool
public static function getValueIfKeyExists($key, array $array, /* out */ &$valueDst): bool
{
if (!array_key_exists($key, $array)) {
return false;
Expand All @@ -54,7 +55,7 @@ public static function getValueIfKeyExists(string $key, array $array, &$valueDst
}

/**
* @template TKey of string|int
* @template TKey of array-key
* @template TValue
*
* @param TKey $key
Expand Down
Loading

0 comments on commit 032adca

Please sign in to comment.