From 2b5791c9c427fd36b69af169f48b7d714c0bad86 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 30 Jun 2024 11:58:47 +0900 Subject: [PATCH 01/40] Add rayaop extension and configuration setup The update includes the addition of rayaop extension to the PHP code base. This is made possible through the inclusion of new files namely: php-executable.m4, pecl.m4, config.w32, and CMakeLists.txt. Config.m4 has been modified to initialize and configure the added rayaop extension. An option has been provided to enable or disable rayaop during PHP configuration, adding flexibility to the build process. --- CMakeLists.txt | 50 +++++ autoconf/pecl.m4 | 415 +++++++++++++++++++++++++++++++++++++ autoconf/php-executable.m4 | 6 + config.m4 | 39 +++- config.w32 | 10 + 5 files changed, 516 insertions(+), 4 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 autoconf/pecl.m4 create mode 100644 autoconf/php-executable.m4 create mode 100644 config.w32 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d9bfd1d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,50 @@ +# 必要なCMakeのバージョンを指定 +# @link https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html +cmake_minimum_required(VERSION 3.8) + +# プロジェクトの名前と使用する言語を指定 +# @link https://cmake.org/cmake/help/latest/command/project.html +project(rayaop C) + +# コンパイル時に定義するシンボルを指定 +# @link https://cmake.org/cmake/help/latest/command/add_compile_definitions.html +add_compile_definitions(HAVE_RAYAOP) + +# ソースファイルのリストを指定 +# @link https://cmake.org/cmake/help/latest/command/set.html +set(SOURCE_FILES php_rayaop rayaop.c) + +# `php-config` コマンドを使ってPHPのインクルードディレクトリを取得 +# @link https://cmake.org/cmake/help/latest/command/execute_process.html +execute_process ( + COMMAND php-config --include-dir + OUTPUT_VARIABLE PHP_SOURCE +) + +# 取得したディレクトリの末尾にある改行を削除 +# @link https://cmake.org/cmake/help/latest/command/string.html +string(REGEX REPLACE "\n$" "" PHP_SOURCE "${PHP_SOURCE}") + +# 使用するソースディレクトリをメッセージとして表示 +# @link https://cmake.org/cmake/help/latest/command/message.html +message("Using source directory: ${PHP_SOURCE}") + +# インクルードディレクトリを追加 +# @link https://cmake.org/cmake/help/latest/command/include_directories.html +include_directories(${PHP_SOURCE}) +include_directories(${PHP_SOURCE}/main) +include_directories(${PHP_SOURCE}/Zend) +include_directories(${PHP_SOURCE}/TSRM) +include_directories(${PROJECT_SOURCE_DIR}) + +# カスタムターゲット `configure` を追加 +# `phpize` と `./configure` を実行し、ソースファイルに依存させる +# @link https://cmake.org/cmake/help/latest/command/add_custom_target.html +add_custom_target(configure + COMMAND phpize && ./configure + DEPENDS ${SOURCE_FILES} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + +# ソースファイルからライブラリを作成(ただし、ALLビルドからは除外) +# @link https://cmake.org/cmake/help/latest/command/add_library.html +add_library(___ EXCLUDE_FROM_ALL ${SOURCE_FILES}) diff --git a/autoconf/pecl.m4 b/autoconf/pecl.m4 new file mode 100644 index 0000000..ffa45ac --- /dev/null +++ b/autoconf/pecl.m4 @@ -0,0 +1,415 @@ + +yes() { + true +} +no() { + false +} +dnl +dnl PECL_INIT(name) +dnl +dnl Start configuring the PECL extension. +dnl +AC_DEFUN([PECL_INIT], [dnl + m4_define([PECL_NAME],[$1])dnl +])dnl +dnl +dnl +dnl PECL_VAR(name) +dnl +AC_DEFUN([PECL_VAR], [dnl +AS_TR_CPP([PHP_]PECL_NAME[_$1])dnl +])dnl +dnl +dnl PECL_CACHE_VAR(name) +dnl +AC_DEFUN([PECL_CACHE_VAR], [dnl +AS_TR_SH([PECL_cv_$1])dnl +])dnl +dnl +dnl PECL_SAVE_VAR(name) +dnl +AC_DEFUN([PECL_SAVE_VAR], [dnl +AS_TR_SH([PECL_sv_$1])dnl +])dnl +dnl +dnl PECL_DEFINE(what, to[, desc]) +dnl +AC_DEFUN([PECL_DEFINE], [dnl + AC_DEFINE(PECL_VAR([$1]), ifelse([$2],,1,[$2]), ifelse([$3],,[ ],[$3])) +])dnl +dnl +dnl PECL_DEFINE_UQ(what, to[, desc]) +dnl +AC_DEFUN([PECL_DEFINE_UQ], [dnl + AC_DEFINE_UNQUOTED(PECL_VAR([$1]), [$2], ifelse([$3],,[ ],[$3])) +])dnl +dnl +dnl PECL_DEFINE_SH(what, to[, desc]) +dnl +AC_DEFUN([PECL_DEFINE_SH], [dnl + PECL_VAR([$1])=$2 + PECL_DEFINE_UQ([$1], [$2], [$3]) +]) +dnl +dnl PECL_DEFINE_FN(fn) +dnl +AC_DEFUN([PECL_DEFINE_FN], [ + AC_DEFINE(AS_TR_CPP([HAVE_$1]), [1], [ ]) +]) +dnl +dnl PECL_SAVE_ENV(var, ns) +dnl +AC_DEFUN([PECL_SAVE_ENV], [ + PECL_SAVE_VAR([$2_$1])=[$]$1 +]) +dnl +dnl PECL_RESTORE_ENV(var, ns) +dnl +AC_DEFUN([PECL_RESTORE_ENV], [ + $1=$PECL_SAVE_VAR([$2_$1]) +]) +dnl +dnl PECL_EVAL_LIBLINE(libline) +dnl +AC_DEFUN([PECL_EVAL_LIBLINE], [ + PECL_SAVE_ENV(ext_shared, pecl) + ext_shared=no + PHP_EVAL_LIBLINE([$1], _pecl_eval_libline_dummy) + PECL_RESTORE_ENV(ext_shared, pecl) +]) +dnl +dnl PECL_PROG_EGREP +dnl +dnl Checks for an egrep. Defines $EGREP. +dnl +AC_DEFUN([PECL_PROG_EGREP], [ + ifdef([AC_PROG_EGREP], [ + AC_PROG_EGREP + ], [ + AC_CHECK_PROG(EGREP, egrep, egrep) + ]) +]) +dnl +dnl PECL_PROG_AWK +dnl +dnl Checks for an awk. Defines $AWK. +dnl +AC_DEFUN([PECL_PROG_AWK], [ + ifdef([AC_PROG_AWK], [ + AC_PROG_AWK + ], [ + AC_CHECK_PROG(AWK, awk, awk) + ]) +]) +dnl +dnl PECL_PROG_SED +dnl +dnl Checks for the sed program. Defines $SED. +dnl +AC_DEFUN([PECL_PROG_SED], [ + ifdef([AC_PROG_SED], [ + AC_PROG_SED + ], [ + ifdef([LT_AC_PROG_SED], [ + LT_AC_PROG_SED + ], [ + AC_CHECK_PROG(SED, sed, sed) + ]) + ]) +]) +dnl +dnl PECL_PROG_PKGCONFIG +dnl +dnl Checks for pkg-config program and defines $PKG_CONFIG (to false if not found). +dnl +AC_DEFUN([PECL_PROG_PKGCONFIG], [ + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG([PKG_CONFIG], [pkg-config], [false]) + fi +]) +dnl +dnl PECL_HAVE_PHP_EXT(name[, code-if-yes[, code-if-not]]) +dnl +dnl Check whether ext/$name is enabled in $PHP_EXECUTABLE (PECL build) +dnl or if $PHP_ is defined to anything else than "no" (in-tree build). +dnl Defines shell var PECL_VAR(HAVE_EXT_) to true or false. +dnl +AC_DEFUN([PECL_HAVE_PHP_EXT], [ + AC_REQUIRE([PECL_PROG_EGREP])dnl + AC_CACHE_CHECK([whether ext/$1 is enabled], PECL_CACHE_VAR([HAVE_EXT_$1]), [ + PECL_CACHE_VAR([HAVE_EXT_$1])=no + if test -x "$PHP_EXECUTABLE"; then + if $PHP_EXECUTABLE -m | $EGREP -q ^$1\$; then + PECL_CACHE_VAR([HAVE_EXT_$1])=yes + fi + elif test -n "$AS_TR_CPP([PHP_$1])" && test "$AS_TR_CPP([PHP_$1])" != "no"; then + PECL_CACHE_VAR([HAVE_EXT_$1])=yes + fi + ]) + if $PECL_CACHE_VAR([HAVE_EXT_$1]); then + PECL_VAR([HAVE_EXT_$1])=true + PECL_DEFINE([HAVE_EXT_$1]) + $2 + else + PECL_VAR([HAVE_EXT_$1])=false + $3 + fi +]) +dnl +dnl PECL_HAVE_PHP_EXT_HEADER(ext[, header]) +dnl +dnl Check where to find a header for ext and add the found dir to $INCLUDES. +dnl If header is not specified php_.h is assumed. +dnl Defines shell var PHP__EXT__INCDIR to the found dir. +dnl Defines PHP__HAVE_
to the found path. +dnl +AC_DEFUN([PECL_HAVE_PHP_EXT_HEADER], [dnl + AC_REQUIRE([PECL_PROG_SED])dnl + m4_define([EXT_HEADER], ifelse([$2],,php_$1.h,[$2]))dnl + AC_CACHE_CHECK([for EXT_HEADER of ext/$1], PECL_CACHE_VAR([EXT_$1]_INCDIR), [ + for i in $(printf "%s" "$INCLUDES" | $SED -e's/-I//g') $abs_srcdir ../$1; do + if test -d $i; then + for j in $i/EXT_HEADER $i/ext/$1/EXT_HEADER; do + if test -f $j; then + PECL_CACHE_VAR([EXT_$1]_INCDIR)=$(dirname "$j") + break + fi + done + fi + done + ]) + PECL_VAR([EXT_$1]_INCDIR)=$PECL_CACHE_VAR([EXT_$1]_INCDIR) + PHP_ADD_INCLUDE([$PECL_VAR([EXT_$1]_INCDIR)]) + PECL_DEFINE_UQ([HAVE_]EXT_HEADER, "$PECL_VAR([EXT_$1]_INCDIR)/EXT_HEADER") +]) +dnl +dnl PECL_HAVE_CONST(header, const[, type=int[, code-if-yes[, code-if-mno]]]) +dnl +AC_DEFUN([PECL_HAVE_CONST], [dnl + AC_REQUIRE([PECL_PROG_EGREP])dnl + AC_CACHE_CHECK([for $2 in $1], PECL_CACHE_VAR([HAVE_$1_$2]), [ + AC_TRY_COMPILE([ + #include "$1" + ], [ + ]ifelse([$3],,int,[$3])[ _c = $2; + (void) _c; + ], [ + PECL_CACHE_VAR([HAVE_$1_$2])=yes + ], [ + PECL_CACHE_VAR([HAVE_$1_$2])=no + ]) + ]) + if $PECL_CACHE_VAR([HAVE_$1_$2]); then + PECL_DEFINE([HAVE_$2]) + $4 + else + ifelse([$5],,:,[$5]) + fi +]) +dnl +dnl _PECL_TR_VERSION(version) +dnl +AC_DEFUN([_PECL_TR_VERSION], [dnl +AC_REQUIRE([PECL_PROG_AWK])dnl +$(printf "%s" $1 | $AWK -F "[.]" '{print $[]1*1000000 + $[]2*10000 + $[]3*100 + $[]4}') +]) +dnl +dnl PECL_CHECKED_VERSION(name) +dnl +dnl Shell var name of an already checked version. +dnl +AC_DEFUN([PECL_CHECKED_VERSION], [PECL_VAR([$1][_VERSION])]) +dnl +dnl PECL_HAVE_VERSION(name, min-version[, code-if-yes[, code-if-not]]) +dnl +dnl Perform a min-version check while in an PECL_CHECK_* block. +dnl Expands AC_MSG_ERROR when code-if-not is empty and the version check fails. +dnl +AC_DEFUN([PECL_HAVE_VERSION], [ + aversion=_PECL_TR_VERSION([$PECL_CHECKED_VERSION([$1])]) + mversion=_PECL_TR_VERSION([$2]) + AC_MSG_CHECKING([whether $1 version $PECL_CHECKED_VERSION([$1]) >= $2]) + if test -z "$aversion" || test "$aversion" -lt "$mversion"; then + ifelse($4,,AC_MSG_ERROR([no]), [ + AC_MSG_RESULT([no]) + $4 + ]) + else + AC_MSG_RESULT([ok]) + $3 + fi +]) +dnl +dnl PECL_CHECK_CUSTOM(name, path, header, lib, version) +dnl +AC_DEFUN([PECL_CHECK_CUSTOM], [ + PECL_SAVE_ENV([CPPFLAGS], [$1]) + PECL_SAVE_ENV([LDFLAGS], [$1]) + PECL_SAVE_ENV([LIBS], [$1]) + + AC_MSG_CHECKING([for $1]) + AC_CACHE_VAL(PECL_CACHE_VAR([$1_prefix]), [ + for path in $2 /usr/local /usr /opt; do + if test "$path" = "" || test "$path" = "yes" || test "$path" = "no"; then + continue + elif test -f "$path/include/$3"; then + PECL_CACHE_VAR([$1_prefix])="$path" + break + fi + done + ]) + if test -n "$PECL_CACHE_VAR([$1_prefix])"; then + CPPFLAGS="-I$PECL_CACHE_VAR([$1_prefix])/include" + LDFLAGS="-L$PECL_CACHE_VAR([$1_prefix])/$PHP_LIBDIR" + LIBS="-l$4" + PECL_EVAL_LIBLINE([$LDFLAGS $LIBS]) + + AC_CACHE_VAL(PECL_CACHE_VAR([$1_version]), [ + pushd $PECL_CACHE_VAR([$1_prefix]) >/dev/null + PECL_CACHE_VAR([$1_version])=$5 + popd >/dev/null + ]) + PECL_CHECKED_VERSION([$1])=$PECL_CACHE_VAR([$1_version]) + + if test -n "$PECL_CHECKED_VERSION([$1])"; then + PECL_VAR([HAVE_$1])=true + PECL_DEFINE([HAVE_$1]) + PECL_DEFINE_UQ($1[_VERSION], "$PECL_CHECKED_VERSION([$1])") + else + PECL_VAR([HAVE_$1])=false + fi + else + PECL_VAR([HAVE_$1])=false + fi + AC_MSG_RESULT([${PECL_CHECKED_VERSION([$1]):-no}]) +]) +dnl +dnl PECL_CHECK_CONFIG(name, prog-config, version-flag, cppflags-flag, ldflags-flag, libs-flag) +dnl +AC_DEFUN([PECL_CHECK_CONFIG], [ + PECL_SAVE_ENV([CPPFLAGS], [$1]) + PECL_SAVE_ENV([LDFLAGS], [$1]) + PECL_SAVE_ENV([LIBS], [$1]) + + + AC_MSG_CHECKING([for $1]) + ifelse($2, [$PKG_CONFIG $1], [ + AC_CACHE_VAL(PECL_CACHE_VAR([$1_exists]), [ + if $($2 --exists); then + PECL_CACHE_VAR([$1_exists])=yes + else + PECL_CACHE_VAR([$1_exists])=no + fi + ]) + if $PECL_CACHE_VAR([$1_exists]); then + ]) + AC_CACHE_VAL(PECL_CACHE_VAR([$1_version]), [ + PECL_CACHE_VAR([$1_version])=$($2 $3) + ]) + PECL_CHECKED_VERSION([$1])=$PECL_CACHE_VAR([$1_version]) + AC_CACHE_VAL(PECL_CACHE_VAR([$1_cppflags]), [ + PECL_CACHE_VAR([$1_cppflags])=$($2 $4) + ]) + CPPFLAGS=$PECL_CACHE_VAR([$1_cppflags]) + AC_CACHE_VAL(PECL_CACHE_VAR([$1_ldflags]), [ + PECL_CACHE_VAR([$1_ldflags])=$($2 $5) + ]) + LDFLAGS=$PECL_CACHE_VAR([$1_ldflags]) + AC_CACHE_VAL(PECL_CACHE_VAR([$1_libs]), [ + PECL_CACHE_VAR([$1_libs])=$($2 $6) + ]) + LIBS=$PECL_CACHE_VAR([$1_libs]) + PECL_EVAL_LIBLINE([$LDFLAGS $LIBS]) + ifelse($2, [$PKG_CONFIG $1], [ + fi + ]) + + if test -n "$PECL_CHECKED_VERSION([$1])"; then + PECL_VAR([HAVE_$1])=true + PECL_DEFINE([HAVE_$1]) + PECL_DEFINE_UQ([$1_VERSION], "$PECL_CHECKED_VERSION([$1])") + else + PECL_VAR([HAVE_$1])=false + fi + + AC_MSG_RESULT([${PECL_CHECKED_VERSION([$1]):-no}]) +]) +dnl +dnl PECL_CHECK_PKGCONFIG(pkg[, additional-pkg-config-path]) +dnl +AC_DEFUN([PECL_CHECK_PKGCONFIG], [dnl + AC_REQUIRE([PECL_PROG_PKGCONFIG])dnl + ifelse($2,,, [ + PECL_SAVE_VAR(pkgconfig_path)="$PKG_CONFIG_PATH" + if test -d "$2"; then + export PKG_CONFIG_PATH="$2/lib/pkgconfig:$PKG_CONFIG_PATH" + fi + ]) + PECL_CHECK_CONFIG([$1], [$PKG_CONFIG $1], [--modversion], [--cflags-only-I], [--libs-only-L], [--libs-only-l]) + ifelse($2,,, [ + PKG_CONFIG_PATH="$PECL_SAVE_VAR(pkgconfig_path)" + ]) +]) +dnl +dnl PECL_CHECK_DONE(name, success[, incline, libline]) +dnl +AC_DEFUN([PECL_CHECK_DONE], [ + if $2; then + incline=$CPPFLAGS + libline="$LDFLAGS $LIBS" + PECL_DEFINE([HAVE_$1]) + else + incline=$3 + libline=$4 + fi + + PECL_RESTORE_ENV([CPPFLAGS], [$1]) + PECL_RESTORE_ENV([LDFLAGS], [$1]) + PECL_RESTORE_ENV([LIBS], [$1]) + + PHP_EVAL_INCLINE([$incline]) + PHP_EVAL_LIBLINE([$libline], AS_TR_CPP(PECL_NAME[_SHARED_LIBADD])) +]) + +dnl +dnl PECL_CHECK_CA([additional-ca-paths,[ additional-ca-bundles]]) +dnl +AC_DEFUN([PECL_CHECK_CA], [ + AC_CACHE_CHECK([for default CA path], PECL_CACHE_VAR([CAPATH]), [ + PECL_VAR([CAPATH])= + for ca_path in $1 \ + /etc/ssl/certs \ + /System/Library/OpenSSL + do + # check if it's actually a hashed directory + if test -d "$ca_path" && ls "$ca_path"/@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@@<:@0-9a-f@:>@.0 >/dev/null 2>&1; then + PECL_CACHE_VAR([CAPATH])=$ca_path + break + fi + done + ]) + if test -n "$PECL_CACHE_VAR([CAPATH])"; then + PECL_DEFINE_SH([CAPATH], "$PECL_CACHE_VAR([CAPATH])") + fi + + AC_CACHE_CHECK([for default CA info], PECL_CACHE_VAR([CAINFO]), [ + for ca_info in $2 \ + /etc/ssl/{cert,ca-bundle}.pem \ + /{etc,usr/share}/ssl/certs/ca-{bundle,ceritifcates}.crt \ + /etc/{pki/ca-trust,ca-certificates}/extracted/pem/tls-ca-bundle.pem \ + /etc/pki/tls/certs/ca-bundle{,.trust}.crt \ + /usr/local/etc/{,open}ssl/cert.pem \ + /usr/local/share/certs/ca-root-nss.crt \ + /{usr,usr/local,opt}/local/share/curl/curl-ca-bundle.crt + do + if test -f "$ca_info"; then + PECL_CACHE_VAR([CAINFO])=$ca_info + break + fi + done + ]) + if test -n "$PECL_CACHE_VAR([CAINFO])"; then + PECL_DEFINE_SH([CAINFO], "$PECL_CACHE_VAR([CAINFO])") + fi +]) diff --git a/autoconf/php-executable.m4 b/autoconf/php-executable.m4 new file mode 100644 index 0000000..29a3e1d --- /dev/null +++ b/autoconf/php-executable.m4 @@ -0,0 +1,6 @@ +dnl +dnl Generate run-php bash script +dnl +AC_CONFIG_COMMANDS_POST([ + ln -s "$PHP_EXECUTABLE" build/php +]) diff --git a/config.m4 b/config.m4 index f6e73f3..68c4867 100644 --- a/config.m4 +++ b/config.m4 @@ -1,6 +1,37 @@ -# 'rayaop'サポートを有効にするかどうかの設定を追加 -PHP_ARG_ENABLE(rayaop, whether to enable rayaop support, -[ --enable-rayaop Enable rayaop support]) +dnl $Id$ +dnl config.m4 for extension rayaop -PHP_NEW_EXTENSION(rayaop, src/rayaop.c, $ext_shared) +dnl PECLの設定マクロをインクルード +dnl @link https://github.com/php/pecl-tools/blob/master/autoconf/pecl.m4 +sinclude(./autoconf/pecl.m4) +dnl PHP実行ファイルの検出マクロをインクルード +dnl @link https://github.com/php/pecl-tools/blob/master/autoconf/php-executable.m4 +sinclude(./autoconf/php-executable.m4) + +dnl PECL拡張の初期化 +dnl @link https://github.com/php/pecl-tools/blob/master/pecl.m4#L229 +PECL_INIT([rayaop]) + +dnl 拡張機能を有効にするかどうかの設定オプションを追加 +dnl @link https://www.gnu.org/software/autoconf/manual/autoconf-2.68/html_node/External-Shell-Variables.html +PHP_ARG_ENABLE(rayaop, whether to enable rayaop, [ --enable-rayaop Enable rayaop]) + +dnl 拡張機能が有効な場合の処理 +if test "$PHP_RAYAOP" != "no"; then + dnl 拡張機能が有効かどうかを定義 + dnl @link https://www.gnu.org/software/autoconf/manual/autoconf-2.68/html_node/Defining-Variables.html + AC_DEFINE(HAVE_RAYAOP, 1, [whether rayaop is enabled]) + + dnl PHPの新しい拡張機能を追加 + dnl @link https://www.phpinternalsbook.com/build_system/build_system.html + PHP_NEW_EXTENSION(rayaop, rayaop.c, $ext_shared) + + dnl Makefileフラグメントを追加 + dnl @link https://www.phpinternalsbook.com/build_system/build_system.html#php-add-makefile-fragment + PHP_ADD_MAKEFILE_FRAGMENT + + dnl ヘッダーファイルのインストール指示を追加 + dnl @link https://www.phpinternalsbook.com/build_system/build_system.html#php-install-headers + PHP_INSTALL_HEADERS([ext/rayaop], [php_rayaop.h]) +fi diff --git a/config.w32 b/config.w32 new file mode 100644 index 0000000..584e91d --- /dev/null +++ b/config.w32 @@ -0,0 +1,10 @@ +// $Id$ +// vim:ft=javascript + +ARG_ENABLE("rayaop", "enable rayaop", "no"); + +if (PHP_RAYAOP != "no") { + EXTENSION("rayaop", "rayaop.c"); + AC_DEFINE('HAVE_RAYAOP', 1 , 'whether rayaop is enabled'); + PHP_INSTALL_HEADERS("ext/rayaop/", "php_rayaop.h"); +} From 94dd5188e2a7948c4bb733b0965983511500b911 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 30 Jun 2024 11:59:26 +0900 Subject: [PATCH 02/40] Replace and rename php_rayaop.h files The old version of the php_rayaop.h file has been replaced with a new one. Simultaneously, the source file rayaop.c has been renamed. The new header file comes with additional definitions and the declaration of new function rayaop_nop. Commit includes added license and copyright information. --- include/php_rayaop.h | 37 ----------------------------- php_rayaop.h | 51 ++++++++++++++++++++++++++++++++++++++++ src/rayaop.c => rayaop.c | 0 3 files changed, 51 insertions(+), 37 deletions(-) delete mode 100644 include/php_rayaop.h create mode 100644 php_rayaop.h rename src/rayaop.c => rayaop.c (100%) diff --git a/include/php_rayaop.h b/include/php_rayaop.h deleted file mode 100644 index b6d7a34..0000000 --- a/include/php_rayaop.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef PHP_RAYAOP_H -#define PHP_RAYAOP_H - -extern zend_module_entry rayaop_module_entry; // 拡張機能のエントリポイント -#define phpext_rayaop_ptr &rayaop_module_entry // 拡張機能のポインタ - -#define PHP_RAYAOP_VERSION "1.0.0" // 拡張機能のバージョン - -#ifdef ZTS -#include "TSRM.h" // スレッドセーフリソース管理ヘッダー -#endif - -/** - * 拡張機能のグローバル変数の宣言 - * @link https://www.php.net/manual/en/internals2.variables.globals.php - */ -ZEND_BEGIN_MODULE_GLOBALS(rayaop) - // 拡張機能のグローバル変数をここに追加 -ZEND_END_MODULE_GLOBALS(rayaop) - -/** - * グローバル変数アクセス用のマクロ - * @link https://www.php.net/manual/en/internals2.variables.globals.php - */ -#define RAYAOP_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(rayaop, v) - -/** - * スレッドセーフリソース管理のキャッシュの外部宣言 - * @link https://www.php.net/manual/en/internals2.variables.globals.php - */ -#if defined(ZTS) && defined(COMPILE_DL_RAYAOP) -ZEND_TSRMLS_CACHE_EXTERN() -#endif - -PHP_FUNCTION(method_intercept); // method_intercept関数の宣言 - -#endif /* PHP_RAYAOP_H */ diff --git a/php_rayaop.h b/php_rayaop.h new file mode 100644 index 0000000..72d0386 --- /dev/null +++ b/php_rayaop.h @@ -0,0 +1,51 @@ +/* + +----------------------------------------------------------------------+ + | RayAop PHP extension | + +----------------------------------------------------------------------+ + | Copyright (c) 2018 NAME | + +----------------------------------------------------------------------+ + | Permission is hereby granted, free of charge, to any person | + | obtaining a copy of this software and associated documentation files | + | (the "Software"), to deal in the Software without restriction, | + | including without limitation the rights to use, copy, modify, merge, | + | publish, distribute, sublicense, and/or sell copies of the Software, | + | and to permit persons to whom the Software is furnished to do so, | + | subject to the following conditions: | + | | + | The above copyright notice and this permission notice shall be | + | included in all copies or substantial portions of the Software. | + | | + | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | + | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | + | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | + | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | + | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | + | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | + | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | + | SOFTWARE. | + +----------------------------------------------------------------------+ + | Author: NAME | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_RAYAOP_H +#define PHP_RAYAOP_H 1 + +#define PHP_RAYAOP_VERSION "0.0.1-dev" +#define PHP_RAYAOP_EXTNAME "rayaop" + +#ifdef PHP_WIN32 +# define PHP_RAYAOP_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_RAYAOP_API __attribute__ ((visibility("default"))) +#else +# define PHP_RAYAOP_API +#endif + +/* Declare all functions and classes of the extension */ +static PHP_FUNCTION(rayaop_nop); + +extern zend_module_entry rayaop_module_entry; + +#endif + diff --git a/src/rayaop.c b/rayaop.c similarity index 100% rename from src/rayaop.c rename to rayaop.c From a43b209b5db121e5fa74130d5ad77800a1ebbc05 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 30 Jun 2024 12:23:20 +0900 Subject: [PATCH 03/40] Add clean-tests and mrproper to Makefile The Makefile.frag file has been updated to include a 'clean-tests' command to clean up test related files and a 'mrproper' command for a deep clean of the build environment. This helps to keep the workspace free of unnecessary files that may hinder the building and testing process. --- Makefile.frag | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Makefile.frag diff --git a/Makefile.frag b/Makefile.frag new file mode 100644 index 0000000..43f3386 --- /dev/null +++ b/Makefile.frag @@ -0,0 +1,16 @@ +clean-tests: + rm -f tests/*.diff tests/*.exp tests/*.log tests/*.out tests/*.php tests/*.sh + +mrproper: clean clean-tests + rm -rf autom4te.cache build modules vendor + rm -f acinclude.m4 aclocal.m4 config.guess config.h config.h.in config.log config.nice config.status config.sub \ + configure configure.ac install-sh libtool ltmain.sh Makefile Makefile.fragments Makefile.global \ + Makefile.objects missing mkinstalldirs run-tests.php *~ + +info: $(all_targets) + "$(PHP_EXECUTABLE)" -d "extension=$(phplibdir)/$(PHP_PECL_EXTENSION).so" --re "$(PHP_PECL_EXTENSION)" + +package.xml: php_$(PHP_PECL_EXTENSION).h + $(PHP_EXECUTABLE) build-packagexml.php + +.PHONY: all clean install distclean test prof-gen prof-clean prof-use clean-tests mrproper info \ No newline at end of file From cd48704502b5dd1e21f6a16a0424e7147529932f Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 30 Jun 2024 12:27:01 +0900 Subject: [PATCH 04/40] Add 'clean' and 'make' run configurations New run configurations named 'clean' and 'make' were added to the ProjectRunConfigurationManager. These set up the way the CLionExternalRunConfiguration works with specific parameters for program execution. They are saved under '.idea/runConfigurations'. --- .idea/runConfigurations/clean.xml | 7 +++++++ .idea/runConfigurations/make.xml | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 .idea/runConfigurations/clean.xml create mode 100644 .idea/runConfigurations/make.xml diff --git a/.idea/runConfigurations/clean.xml b/.idea/runConfigurations/clean.xml new file mode 100644 index 0000000..c0f66af --- /dev/null +++ b/.idea/runConfigurations/clean.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/make.xml b/.idea/runConfigurations/make.xml new file mode 100644 index 0000000..865f5a9 --- /dev/null +++ b/.idea/runConfigurations/make.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file From 6f924790b86897e828689192459e4d1d254a7ac8 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 30 Jun 2024 12:31:55 +0900 Subject: [PATCH 05/40] Update PHP version in GitHub workflow The build.yml file for the GitHub workflow has been updated. The PHP version 8.0 has been removed from the testing matrix, leaving only versions 8.1, 8.2, and 8.3 to test. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba47e82..cb0ddcb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: - php-version: ['8.0', '8.1', '8.2', '8.3'] + php-version: ['8.1', '8.2', '8.3'] steps: - name: Checkout code From a83bc59756f61f8ea66eea1d8da9eb69f394f501 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 30 Jun 2024 12:49:04 +0900 Subject: [PATCH 06/40] Uncomment code to release class and method names The previous version had certain lines of code commented out. These lines were responsible for releasing the names of classes and methods in intercept info. In this version, these lines are uncommented to ensure proper memory management. --- rayaop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rayaop.c b/rayaop.c index 8375faa..58da4ef 100644 --- a/rayaop.c +++ b/rayaop.c @@ -182,8 +182,8 @@ static int efree_intercept_info(zval *zv) if (info) { RAYAOP_DEBUG_PRINT("Freeing intercept info for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name)); - // zend_string_release(info->class_name); // クラス名を解放 *SHOULD_BE_REMOVED* - // zend_string_release(info->method_name); // メソッド名を解放 *SHOULD_BE_REMOVED* + zend_string_release(info->class_name); // クラス名を解放 + zend_string_release(info->method_name); // メソッド名を解放 RAYAOP_DEBUG_PRINT("class_name and method_name released"); From e373bd38a2f31983cb49c5110387f6dc76f24923 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 30 Jun 2024 17:59:15 +0900 Subject: [PATCH 07/40] Adjust return value cleanup and hash table initialization This commit modifies the cleanup of the 'retval' variable so it's performed regardless of the conditional check outcome. Moreover, a hash table initialization line has been adjusted to utilize the 'efree_intercept_info' function instead of 'NULL'. These changes ensure more reliable resource management within the application. --- rayaop.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rayaop.c b/rayaop.c index 58da4ef..8144724 100644 --- a/rayaop.c +++ b/rayaop.c @@ -103,10 +103,10 @@ static void rayaop_zend_execute_ex(zend_execute_data *execute_data) if (!Z_ISUNDEF(retval)) { ZVAL_COPY(execute_data->return_value, &retval); // 戻り値を設定 } - zval_ptr_dtor(&retval); // 戻り値のデストラクタを呼ぶ } else { php_error_docref(NULL, E_WARNING, "Interception failed for %s::%s", ZSTR_VAL(class_name), ZSTR_VAL(method_name)); } + zval_ptr_dtor(&retval); // 成功時と失敗時の両方で retval を解放 zval_ptr_dtor(&func_name); // メソッド名のデストラクタを呼ぶ zval_ptr_dtor(¶ms[1]); // メソッド名のデストラクタを呼ぶ @@ -193,7 +193,6 @@ static int efree_intercept_info(zval *zv) efree(info); // インターセプト情報構造体を解放 RAYAOP_DEBUG_PRINT("Memory freed for intercept info"); } - return ZEND_HASH_APPLY_REMOVE; // ハッシュテーブルからエントリを削除 } /** @@ -208,7 +207,7 @@ PHP_MINIT_FUNCTION(rayaop) zend_execute_ex = rayaop_zend_execute_ex; // カスタムzend_execute_ex関数を設定 intercept_ht = pemalloc(sizeof(HashTable), 1); // ハッシュテーブルを確保 - zend_hash_init(intercept_ht, 8, NULL, NULL, 1); // ハッシュテーブルを初期化 + zend_hash_init(intercept_ht, 8, NULL, efree_intercept_info, 1); // ハッシュテーブルを初期化 RAYAOP_DEBUG_PRINT("RayAOP extension initialized"); return SUCCESS; // 初期化成功 From b8f2d47abcd8082e0786c81a5ec9328058bbd703 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 30 Jun 2024 18:13:07 +0900 Subject: [PATCH 08/40] Uncomment debug definition in rayaop.c The RAYAOP_DEBUG definition previously commented out in the rayaop.c file has been uncommented. This change should enhance the debugging process for developers. --- rayaop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rayaop.c b/rayaop.c index 8144724..0a7050c 100644 --- a/rayaop.c +++ b/rayaop.c @@ -13,7 +13,7 @@ #include "TSRM.h" // スレッドセーフリソース管理ヘッダー #endif -//#define RAYAOP_DEBUG // デバッグ用の定義 +//#define RAYAOP_DEBUG /** * インターセプト情報を保持する構造体 From e324283aae29c417f7363490e1d336f4f24977bc Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 30 Jun 2024 18:23:28 +0900 Subject: [PATCH 09/40] Update rayaop.c with enhanced debugging and error handling The code for rayaop.c has been updated to provide more comprehensive debugging messages and robust error handling. The changes include additional feedback when intercepting and registering intercept information as well as more nuanced memory handling. This amelioration will help in diagnosing potential issues within the module. --- rayaop.c | 126 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 22 deletions(-) diff --git a/rayaop.c b/rayaop.c index 0a7050c..79f7fe9 100644 --- a/rayaop.c +++ b/rayaop.c @@ -13,7 +13,8 @@ #include "TSRM.h" // スレッドセーフリソース管理ヘッダー #endif -//#define RAYAOP_DEBUG +// デバッグ出力を有効にする場合はこのマクロをアンコメントしてください +// #define RAYAOP_DEBUG /** * インターセプト情報を保持する構造体 @@ -24,9 +25,9 @@ typedef struct _intercept_info { zend_string *class_name; // インターセプト対象のクラス名 zend_string *method_name; // インターセプト対象のメソッド名 zval handler; // インターセプトハンドラー - struct _intercept_info *next; // 次のインターセプト情報へのポインタ } intercept_info; +// グローバル変数の宣言 static HashTable *intercept_ht = NULL; // インターセプト情報を格納するハッシュテーブル static void (*original_zend_execute_ex)(zend_execute_data *execute_data); // 元のzend_execute_ex関数へのポインタ @@ -36,22 +37,22 @@ static THREAD_LOCAL zend_bool is_intercepting = 0; // スレッドローカル static zend_bool is_intercepting = 0; // インターセプト中かどうかのフラグ #endif +// デバッグ出力用マクロ #ifdef RAYAOP_DEBUG -#define RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) // デバッグ用出力 +#define RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) #else #define RAYAOP_DEBUG_PRINT(fmt, ...) #endif -static zend_class_entry *zend_ce_ray_aop_interceptedinterface; // インターセプトされたインターフェースのクラスエントリ +// インターセプトされたインターフェースのクラスエントリ +static zend_class_entry *zend_ce_ray_aop_interceptedinterface; -/** - * カスタム zend_execute_ex 関数 - * @link http://php.adamharvey.name/manual/ja/migration55.internals.php - */ +// カスタム zend_execute_ex 関数 static void rayaop_zend_execute_ex(zend_execute_data *execute_data) { - RAYAOP_DEBUG_PRINT("rayaop_zend_execute_ex called"); // デバッグ出力 + RAYAOP_DEBUG_PRINT("rayaop_zend_execute_ex called"); + // 既にインターセプト中の場合は元の関数を呼び出す if (is_intercepting) { RAYAOP_DEBUG_PRINT("Already intercepting, calling original zend_execute_ex"); original_zend_execute_ex(execute_data); // 元のzend_execute_ex関数を呼び出す @@ -60,6 +61,7 @@ static void rayaop_zend_execute_ex(zend_execute_data *execute_data) zend_function *current_function = execute_data->func; // 現在の関数情報を取得 + // クラスメソッドの場合のみ処理を行う if (current_function->common.scope && current_function->common.function_name) { zend_string *class_name = current_function->common.scope->name; // クラス名を取得 zend_string *method_name = current_function->common.function_name; // メソッド名を取得 @@ -77,8 +79,11 @@ static void rayaop_zend_execute_ex(zend_execute_data *execute_data) if (Z_TYPE(info->handler) == IS_OBJECT) { zval retval, params[3]; // 戻り値とパラメータ用のzvalを宣言 + // オブジェクトがNULLの場合は元の関数を実行 if (execute_data->This.value.obj == NULL) { - original_zend_execute_ex(execute_data); // オブジェクトがNULLの場合は元の関数を実行 + RAYAOP_DEBUG_PRINT("Object is NULL, calling original function"); + original_zend_execute_ex(execute_data); + efree(key); // キーを解放 return; } @@ -106,25 +111,27 @@ static void rayaop_zend_execute_ex(zend_execute_data *execute_data) } else { php_error_docref(NULL, E_WARNING, "Interception failed for %s::%s", ZSTR_VAL(class_name), ZSTR_VAL(method_name)); } - zval_ptr_dtor(&retval); // 成功時と失敗時の両方で retval を解放 + zval_ptr_dtor(&retval); // 戻り値を解放 zval_ptr_dtor(&func_name); // メソッド名のデストラクタを呼ぶ zval_ptr_dtor(¶ms[1]); // メソッド名のデストラクタを呼ぶ zval_ptr_dtor(¶ms[2]); // 引数配列のデストラクタを呼ぶ is_intercepting = 0; // インターセプト中フラグを解除 + efree(key); // キーを解放 return; } } else { RAYAOP_DEBUG_PRINT("No intercept info found for key: %s", key); } - efree(key); // keyの解放はすべてのデバッグ出力の後 + efree(key); // キーを解放 } original_zend_execute_ex(execute_data); // 元のzend_execute_ex関数を呼び出す } +// method_intercept 関数の引数情報 ZEND_BEGIN_ARG_INFO_EX(arginfo_method_intercept, 0, 0, 3) ZEND_ARG_TYPE_INFO(0, class_name, IS_STRING, 0) // クラス名の引数情報 ZEND_ARG_TYPE_INFO(0, method_name, IS_STRING, 0) // メソッド名の引数情報 @@ -149,7 +156,7 @@ PHP_FUNCTION(method_intercept) Z_PARAM_OBJECT(intercepted) // インターセプトハンドラーのパラメータを解析 ZEND_PARSE_PARAMETERS_END(); - intercept_info *new_info = ecalloc(1, sizeof(intercept_info)); // 新しいインターセプト情報を確保 + intercept_info *new_info = emalloc(sizeof(intercept_info)); if (!new_info) { php_error_docref(NULL, E_ERROR, "Memory allocation failed"); // メモリ確保に失敗した場合のエラー RETURN_FALSE; @@ -164,11 +171,20 @@ PHP_FUNCTION(method_intercept) char *key = NULL; // ハッシュキー用の文字列ポインタ size_t key_len = 0; // ハッシュキーの長さ key_len = spprintf(&key, 0, "%s::%s", class_name, method_name); // ハッシュキーを生成 - RAYAOP_DEBUG_PRINT("Registered intercept info for key: %s", key); - - zend_hash_str_update_ptr(intercept_ht, key, key_len, new_info); // ハッシュテーブルに新しいインターセプト情報を登録 - efree(key); // ハッシュキーを解放 + RAYAOP_DEBUG_PRINT("Generated key: %s", key); + + if (zend_hash_str_update_ptr(intercept_ht, key, key_len, new_info) == NULL) { + php_error_docref(NULL, E_ERROR, "Failed to update hash table"); // ハッシュテーブルの更新に失敗した場合のエラー + zend_string_release(new_info->class_name); // クラス名を解放 + zend_string_release(new_info->method_name); // メソッド名を解放 + zval_ptr_dtor(&new_info->handler); // ハンドラーを解放 + efree(new_info); // インターセプト情報を解放 + efree(key); // キーを解放 + RETURN_FALSE; + } + efree(key); // キーを解放 + RAYAOP_DEBUG_PRINT("Successfully registered intercept info"); RETURN_TRUE; } @@ -184,7 +200,6 @@ static int efree_intercept_info(zval *zv) zend_string_release(info->class_name); // クラス名を解放 zend_string_release(info->method_name); // メソッド名を解放 - RAYAOP_DEBUG_PRINT("class_name and method_name released"); zval_ptr_dtor(&info->handler); // ハンドラーを解放 @@ -207,7 +222,11 @@ PHP_MINIT_FUNCTION(rayaop) zend_execute_ex = rayaop_zend_execute_ex; // カスタムzend_execute_ex関数を設定 intercept_ht = pemalloc(sizeof(HashTable), 1); // ハッシュテーブルを確保 - zend_hash_init(intercept_ht, 8, NULL, efree_intercept_info, 1); // ハッシュテーブルを初期化 + if (!intercept_ht) { + php_error_docref(NULL, E_ERROR, "Failed to allocate memory for intercept hash table"); + return FAILURE; + } + zend_hash_init(intercept_ht, 8, NULL, (dtor_func_t)efree_intercept_info, 1); // ハッシュテーブルを初期化 RAYAOP_DEBUG_PRINT("RayAOP extension initialized"); return SUCCESS; // 初期化成功 @@ -245,12 +264,14 @@ PHP_MINFO_FUNCTION(rayaop) php_info_print_table_end(); // 情報テーブルの終了 } +// 拡張機能が提供する関数の定義 // https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html static const zend_function_entry rayaop_functions[] = { PHP_FE(method_intercept, arginfo_method_intercept) // method_intercept関数の登録 - PHP_FE_END + PHP_FE_END // 関数エントリの終了 }; +// 拡張機能のモジュールエントリ // @link https://www.phpinternalsbook.com/php7/extensions_design/extension_infos.html zend_module_entry rayaop_module_entry = { STANDARD_MODULE_HEADER, @@ -258,13 +279,74 @@ zend_module_entry rayaop_module_entry = { rayaop_functions, // 拡張機能が提供する関数 PHP_MINIT(rayaop), // 拡張機能の初期化関数 PHP_MSHUTDOWN(rayaop), // 拡張機能のシャットダウン関数 - NULL, /* Request init */ - NULL, /* Request shutdown */ + NULL, // リクエスト開始時の関数(未使用) + NULL, // リクエスト終了時の関数(未使用) PHP_MINFO(rayaop), // 拡張機能の情報表示関数 PHP_RAYAOP_VERSION, // 拡張機能のバージョン STANDARD_MODULE_PROPERTIES }; +// 動的にロード可能なモジュールとしてコンパイルする場合の処理 #ifdef COMPILE_DL_RAYAOP ZEND_GET_MODULE(rayaop) #endif + +// 以下は、必要に応じて追加の関数や定義を記述できます + +// 例: デバッグ用のヘルパー関数 +#ifdef RAYAOP_DEBUG +static void rayaop_debug_print_zval(zval *value) +{ + switch (Z_TYPE_P(value)) { + case IS_NULL: + RAYAOP_DEBUG_PRINT("(null)"); + break; + case IS_TRUE: + RAYAOP_DEBUG_PRINT("(bool) true"); + break; + case IS_FALSE: + RAYAOP_DEBUG_PRINT("(bool) false"); + break; + case IS_LONG: + RAYAOP_DEBUG_PRINT("(int) %ld", Z_LVAL_P(value)); + break; + case IS_DOUBLE: + RAYAOP_DEBUG_PRINT("(float) %f", Z_DVAL_P(value)); + break; + case IS_STRING: + RAYAOP_DEBUG_PRINT("(string) %s", Z_STRVAL_P(value)); + break; + case IS_ARRAY: + RAYAOP_DEBUG_PRINT("(array)"); + break; + case IS_OBJECT: + RAYAOP_DEBUG_PRINT("(object)"); + break; + default: + RAYAOP_DEBUG_PRINT("(unknown type)"); + break; + } +} +#endif + +// 例: インターセプト情報をダンプする関数 +#ifdef RAYAOP_DEBUG +static void rayaop_dump_intercept_info(void) +{ + RAYAOP_DEBUG_PRINT("Dumping intercept information:"); + if (intercept_ht) { + zend_string *key; + intercept_info *info; + ZEND_HASH_FOREACH_STR_KEY_PTR(intercept_ht, key, info) { + if (key && info) { + RAYAOP_DEBUG_PRINT("Key: %s", ZSTR_VAL(key)); + RAYAOP_DEBUG_PRINT(" Class: %s", ZSTR_VAL(info->class_name)); + RAYAOP_DEBUG_PRINT(" Method: %s", ZSTR_VAL(info->method_name)); + RAYAOP_DEBUG_PRINT(" Handler type: %d", Z_TYPE(info->handler)); + } + } ZEND_HASH_FOREACH_END(); + } else { + RAYAOP_DEBUG_PRINT("Intercept hash table is not initialized"); + } +} +#endif From 23ed5ce923b117ec8b716493d3b9b4f8bf0c83b5 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 30 Jun 2024 18:31:51 +0900 Subject: [PATCH 10/40] Add 'cmake-build-debug' to .gitignore In this commit, 'cmake-build-debug' has been added to the .gitignore file to prevent it from getting tracked by git. This is usually a directory generated as a result of using CLion or similar IDE, and its content should not be part of the repository. --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 96cf1bc..c465303 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,5 @@ # Ignore directories and its content /Makefile.fragments -/Makefile.objects \ No newline at end of file +/Makefile.objects +/cmake-build-debug \ No newline at end of file From f72bd3a6bb67b5c3c81c7f985e8497537dda5743 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 30 Jun 2024 19:21:13 +0900 Subject: [PATCH 11/40] Add test.php with TestClass and Intercepted classes The new file, test.php, includes two classes: TestClass and Intercepted. The TestClass is meant to test method handling functionalities. The Intercepted class implements the MethodInterceptorInterface from the Ray\Aop package, and handles the interception and call of methods. --- test.php | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test.php diff --git a/test.php b/test.php new file mode 100644 index 0000000..671b5f4 --- /dev/null +++ b/test.php @@ -0,0 +1,33 @@ +testMethod("test"); From 747999fcfc1b2d7dc4e5eac8da0d5758dd57e1be Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Mon, 1 Jul 2024 06:27:35 +0900 Subject: [PATCH 12/40] Add script to generate package.xml for pecl extensions The new build-packagexml.php file generates a package.xml file for PECL extensions, both new and existing ones. This is done by reading extension metadata and packing information from source code and Git. The script then generates the XML file following the PEAR package.xml 2.0 schema. --- build-packagexml.php | 452 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 452 insertions(+) create mode 100644 build-packagexml.php diff --git a/build-packagexml.php b/build-packagexml.php new file mode 100644 index 0000000..a83d467 --- /dev/null +++ b/build-packagexml.php @@ -0,0 +1,452 @@ + + * @license PHP License + * @license MIT + */ + +declare (strict_types = 1); + +set_exception_handler(function (Throwable $exception): void { + $msg = ($exception instanceof RuntimeException ? "" : "Unexpected error: ") . $exception->getMessage(); + + fwrite(STDERR, "\033[0;31m" /* red */ . $msg . "\033[0m" . "\n"); + exit(1); +}); + +if (!extension_loaded('dom') || !extension_loaded('simplexml') || !extension_loaded('spl')) { + throw new RuntimeException("Following extensions are required: DOM, SimpleXML and SPL"); +} + +// 1. Load package.xml and create release +$package = (function (): PackageXMLElement { + return file_exists('package.xml') + ? simplexml_load_file('package.xml', PackageXMLElement::class) + : PackageXMLElement::create(); +})(); + +$release = new Release(); + +// 2. Process php_{extname}.h +(function () use ($package, $release): void { + $extname = (string)$package->name ?: getenv('PHP_PECL_EXTENSION') ?: null; + + if ($extname !== null) { + $filename = "php_{$extname}.h"; + + if (!file_exists($filename)) { + throw new RuntimeException("{$filename} not found"); + } + } else { + $filename = glob('php_*.h')[0] ?? null; + + if ($filename === null) { + throw new RuntimeException("Couldn't find the main header file (php_*.h)"); + } + + $extname = preg_replace('/^php_(.+)\.h$/', '$1', $filename); + } + + if ((string)$package->name === '') { + $package->name = $extname; + } + + + $macroPrefix = strtoupper(pathinfo($filename, PATHINFO_FILENAME)); + $contents = file_get_contents($filename); + + preg_match_all("/^[ \t]*#define\s+{$macroPrefix}_(?\w+)[ \t]+\"(?.+)\"/m", $contents, $matches, + PREG_PATTERN_ORDER); + $macros = array_combine($matches['key'], $matches['value']); + + // Package name + if (isset($macros['EXTNAME'])) { + if ((string)$package->name !== '' && (string)$package->name !== $macros['EXTNAME']) { + throw new RuntimeException("Package name '{$package->name}' (package.xml) doesn't match " + . "{$macroPrefix}_EXTNAME '{$macros['EXTNAME']}' ($filename)"); + } + } + + // Release version + if (!isset($macros['VERSION'])) { + throw new RuntimeException("$filename does not contain {$macroPrefix}_VERSION macro"); + } + $release->version = $macros['VERSION']; + + if (strpos($release->version, 'dev') !== false) { + throw new RuntimeException("Development versions shouldn't be released ({$macros['VERSION']}). " + . "Please change {$macroPrefix}_VERSION in $filename."); + } + if ($release->existsIn($package->changelog)) { + throw new RuntimeException("Version {$macros['VERSION']} already released. " + . "Please change {$macroPrefix}_VERSION in $filename."); + } +})(); + +// 3. Copy info from package to release +(function () use ($package, $release): void { + $release->license = (string)$package->license; + $release->licenseUri = (string)$package->license['uri']; + + if (strpos($release->version, 'alpha') !== false) { + $release->stability = 'alpha'; + } else if (strpos($release->version, 'beta') !== false || strpos($release->version, 'RC') !== false) { + $release->stability = 'beta'; + } else if (substr($release->version, 0, 1) === '0') { + $release->stability = ((string)$package->stability->release) ?: 'alpha'; + } else { + $release->stability = 'stable'; + } + + if ($release->isMajor((string)$package->version->release)) { + $release->apiVersion = $release->version; + $release->apiStability = $release->stability; + } else { + $release->apiVersion = (string)$package->version->api; + $release->apiStability = (string)$package->stability->api; + } +})(); + +// 4. Get release notes from stdin +$release->notes = (function (string $name, string $version): string { + if (function_exists('posix_isatty') && posix_isatty(STDIN)) { + fwrite(STDOUT, "Enter the release notes for $name $version (end with Ctrl-D):\n"); + } + + $notes = ''; + + do { + $notes .= fread(STDIN, 1024); + } while (!feof(STDIN)); + + return trim($notes); +})((string)$package->name, $release->version); + +// 5. Add release to package +if (!isset($package->changelog)) { + $package->addChild('changelog'); +} +$release->update($package->changelog->prependChild('release')); +$release->update($package); + +// 6. Removes files that no longer exist from package.xml +(function () use ($package): void { + $fileElements = $package->xpath('p:contents//p:file'); + + foreach ($fileElements as $element) { + $path = join("/", array_map('strval', $element->xpath('ancestor-or-self::*[@name!="/"]/@name'))); + + if (!file_exists($path)) { + unset($element[0]); + } + }; +})(); + +// 7. Add new files to package.xml +(function () use ($package): void { + $ext = ['c', 'h', 'phpt']; + + $dir = new RecursiveDirectoryIterator('.', FilesystemIterator::SKIP_DOTS | FilesystemIterator::CURRENT_AS_PATHNAME); + $itDir = new RecursiveIteratorIterator($dir); + $itReg = new RegexIterator($itDir, '~^./(.+\.(?:' . join('|', $ext) . '))$~', RegexIterator::GET_MATCH); + $files = iterable_column($itReg, 1); + + $newFiles = new CallbackFilterIterator($files, function ($path) use ($package): bool { + $file = basename($path); + $dirs = dirname($path) !== '.' ? explode('/', dirname($path)) : []; + array_unshift($dirs, '/'); + + $xpath = 'p:dir[@name="' . join('"]/p:dir[@name="', $dirs) . '"]/p:file[@name="' . $file . '"]'; + return count($package->contents->xpath($xpath)) === 0; + }); + + if (is_dir('.git')) { + $newFiles = gitignore_filter($newFiles); + } + + foreach ($newFiles as $file) { + $dirs = dirname($file) !== '.' ? explode('/', dirname($file)) : []; + + $dirElement = array_reduce($dirs, function (SimpleXMLElement $parent, string $dir): SimpleXMLElement { + $cur = $parent->xpath('p:dir[@name="' . $dir . '"]')[0] ?? null; + + if ($cur === null) { + $cur = $parent->addChild('dir'); + $cur['name'] = $dir; + } + + return $cur; + }, $package->contents->dir[0]); + + $fileElement = $dirElement->addChild('file'); + $fileElement['name'] = basename($file); + $fileElement['role'] = pathinfo($file, PATHINFO_EXTENSION) === 'phpt' ? 'test' : 'src'; + } +})(); + +// 8. Remove empty dirs from package.xml +(function () use ($package): void { + $emptyDirs = $package->xpath('p:contents//p:dir[not(descendant::*[local-name()="file"])]'); + + foreach (array_reverse($emptyDirs) as $element) { // reverse so children are deleted first + unset($element[0]); + } +})(); + +// 9. Sort files +(function () use ($package): void { + $dirElements = $package->xpath('p:contents//p:dir'); + + $sorter = function(SimpleXMLElement $first, SimpleXMLElement $second): int { + return + (($first->getName() !== 'file') <=> ($second->getName() !== 'file')) ?: + strcasecmp((string)$first['name'], (string)$second['name']); + }; + + foreach ($dirElements as $element) { + $element->sort($sorter); + } +})(); + +// 10. Save package.xml +$package->asXML('package.xml'); + +// :) friendly message +echo "\033[0;32m", /* green */ "Updated package.xml for {$package->name} {$release->version}", "\033[0;0m", "\n"; + +// ================= classes and functions ==================== + +class Release +{ + public $version; + public $apiVersion; + public $stability; + public $apiStability; + + public $license; + public $licenseUri; + public $notes; + + public function isMajor(string $oldVersion): bool + { + if ($oldVersion === '') { + return true; + } + + [$major, $minor] = explode('.', $this->version); + [$oldMajor, $oldMinor] = explode('.', $oldVersion); + + return ($major !== $oldMajor) || ($major === '0' && $minor !== $oldMinor); + } + + public function existsIn(SimpleXMLElement $changelog): bool + { + if (!isset($changelog->release)) { + return false; + } + + foreach ($changelog->release as $release) { + if ((string)($release->version->release) === $this->version) { + return true; + } + } + + return false; + } + + public function update(SimpleXMLElement $element) + { + $noTime = isset($element->date) && !isset($element->time); + + $element->date = date('Y-m-d'); + if (!$noTime) { + $element->time = date('H:i:s'); + } + + $element->version->release = $this->version; + $element->version->api = $this->apiVersion; + $element->stability->release = $this->stability; + $element->stability->api = $this->apiStability; + + $element->license = $this->license; + $element->license['uri'] = $this->licenseUri; + + $indent = str_repeat(' ', count($element->xpath('ancestor::*')) + 1); + $element->notes = "\n" . $this->notes . "\n$indent"; + } +}; + +class PackageXMLElement extends SimpleXMLElement +{ + private const BLANK_PACKAGE = << + + + pecl.php.net + + + + + + + yes + + + + + + + + + + + MIT License + + + + + + + + + + + + + + + + + + 7.0.0 + + + 1.4.3 + + + + + + + +XML; + + public static function create(): self + { + return new static(self::BLANK_PACKAGE); + } + + public function prependChild($name, $value = '') + { + $dom = dom_import_simplexml($this); + + $new = $dom->insertBefore( + $dom->ownerDocument->createElement($name, $value), + $dom->firstChild + ); + + return simplexml_import_dom($new, __CLASS__); + } + + public function sort(callable $sorter): void + { + if ($this->count() === 0) { + return; + } + + $children = iterator_to_array($this->children(), false); + usort($children, $sorter); + + $dom = dom_import_simplexml($this); + + while ($dom->hasChildNodes()) { + $dom->removeChild($dom->firstChild); + } + + foreach ($children as $child) { + $dom->appendChild(dom_import_simplexml($child)); + } + } + + public function asXML($filename = null) + { + $dom = new DOMDocument("1.0"); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + $dom->loadXML(parent::asXML()); + + $xml = $dom->saveXML(); + + // Go from 2 space indentation to 1. + $xml = preg_replace('~^([ ]+)\1<(?!/notes>)~m', '\1<', $xml); + + if ($filename === null) { + return $xml; + } + + if (file_exists($filename)) { + rename($filename, $filename . '~'); + } + file_put_contents($filename, $xml); + } + + public function xpath($xpath) + { + $this->registerXPathNamespace('p', 'http://pear.php.net/dtd/package-2.0'); + + return parent::xpath($xpath); + } +} + +function iterable_column(iterable $iterable, $column): Generator +{ + foreach ($iterable as $key => $item) { + yield $key => $item[$column]; + } +} + +function gitignore_filter(iterable $files): Generator +{ + $spec = [ + ["pipe", "r"], + ["pipe", "w"], + ["pipe", "w"], + ]; + + $process = proc_open('git check-ignore --non-matching --verbose --stdin', $spec, $pipes); + stream_set_timeout($pipes[1], 1); + stream_set_blocking($pipes[2], false); + + foreach ($files as $file) { + fwrite($pipes[0], $file . "\n"); + $line = fgets($pipes[1], 1024); + + if ($line === false || substr($line, 0, 2) === '::') { + yield $file; + } + } + + $err = fread($pipes[2], 10000); + + fclose($pipes[0]); + fclose($pipes[1]); + fclose($pipes[2]); + + $ret = proc_close($process); + + if ($ret !== 0 && $err !== '') { + fwrite(STDERR, "\033[0;31m" /* red */ . $err . "\033[0m" /* no color */ . "\n"); + throw new RuntimeException("Checking .gitignore failed"); + } +} From fda5c27305b5a255834798fc846ce09c4729ac16 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Mon, 1 Jul 2024 06:28:25 +0900 Subject: [PATCH 13/40] Update link formatting and variable definitions The commit modifies the format of documentation links from the '@link' annotation style to simple 'link:' style. It also adjusts variable definitions in the PHP source code, specifically `size_t key_len` and `efree_intercept_info`, to increase code readability and efficiency. --- CMakeLists.txt | 20 ++++++++++---------- config.m4 | 16 ++++++++-------- rayaop.c | 23 +++++++++++------------ 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d9bfd1d..d84f55c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,36 +1,36 @@ # 必要なCMakeのバージョンを指定 -# @link https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html +# link https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html cmake_minimum_required(VERSION 3.8) # プロジェクトの名前と使用する言語を指定 -# @link https://cmake.org/cmake/help/latest/command/project.html +# link https://cmake.org/cmake/help/latest/command/project.html project(rayaop C) # コンパイル時に定義するシンボルを指定 -# @link https://cmake.org/cmake/help/latest/command/add_compile_definitions.html +# link https://cmake.org/cmake/help/latest/command/add_compile_definitions.html add_compile_definitions(HAVE_RAYAOP) # ソースファイルのリストを指定 -# @link https://cmake.org/cmake/help/latest/command/set.html +# link https://cmake.org/cmake/help/latest/command/set.html set(SOURCE_FILES php_rayaop rayaop.c) # `php-config` コマンドを使ってPHPのインクルードディレクトリを取得 -# @link https://cmake.org/cmake/help/latest/command/execute_process.html +# link https://cmake.org/cmake/help/latest/command/execute_process.html execute_process ( COMMAND php-config --include-dir OUTPUT_VARIABLE PHP_SOURCE ) # 取得したディレクトリの末尾にある改行を削除 -# @link https://cmake.org/cmake/help/latest/command/string.html +# link https://cmake.org/cmake/help/latest/command/string.html string(REGEX REPLACE "\n$" "" PHP_SOURCE "${PHP_SOURCE}") # 使用するソースディレクトリをメッセージとして表示 -# @link https://cmake.org/cmake/help/latest/command/message.html +# link https://cmake.org/cmake/help/latest/command/message.html message("Using source directory: ${PHP_SOURCE}") # インクルードディレクトリを追加 -# @link https://cmake.org/cmake/help/latest/command/include_directories.html +# link https://cmake.org/cmake/help/latest/command/include_directories.html include_directories(${PHP_SOURCE}) include_directories(${PHP_SOURCE}/main) include_directories(${PHP_SOURCE}/Zend) @@ -39,12 +39,12 @@ include_directories(${PROJECT_SOURCE_DIR}) # カスタムターゲット `configure` を追加 # `phpize` と `./configure` を実行し、ソースファイルに依存させる -# @link https://cmake.org/cmake/help/latest/command/add_custom_target.html +# link https://cmake.org/cmake/help/latest/command/add_custom_target.html add_custom_target(configure COMMAND phpize && ./configure DEPENDS ${SOURCE_FILES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) # ソースファイルからライブラリを作成(ただし、ALLビルドからは除外) -# @link https://cmake.org/cmake/help/latest/command/add_library.html +# link https://cmake.org/cmake/help/latest/command/add_library.html add_library(___ EXCLUDE_FROM_ALL ${SOURCE_FILES}) diff --git a/config.m4 b/config.m4 index 68c4867..9a27757 100644 --- a/config.m4 +++ b/config.m4 @@ -2,36 +2,36 @@ dnl $Id$ dnl config.m4 for extension rayaop dnl PECLの設定マクロをインクルード -dnl @link https://github.com/php/pecl-tools/blob/master/autoconf/pecl.m4 +dnl link https://github.com/php/pecl-tools/blob/master/autoconf/pecl.m4 sinclude(./autoconf/pecl.m4) dnl PHP実行ファイルの検出マクロをインクルード -dnl @link https://github.com/php/pecl-tools/blob/master/autoconf/php-executable.m4 +dnl link https://github.com/php/pecl-tools/blob/master/autoconf/php-executable.m4 sinclude(./autoconf/php-executable.m4) dnl PECL拡張の初期化 -dnl @link https://github.com/php/pecl-tools/blob/master/pecl.m4#L229 +dnl link https://github.com/php/pecl-tools/blob/master/pecl.m4#L229 PECL_INIT([rayaop]) dnl 拡張機能を有効にするかどうかの設定オプションを追加 -dnl @link https://www.gnu.org/software/autoconf/manual/autoconf-2.68/html_node/External-Shell-Variables.html +dnl link https://www.gnu.org/software/autoconf/manual/autoconf-2.68/html_node/External-Shell-Variables.html PHP_ARG_ENABLE(rayaop, whether to enable rayaop, [ --enable-rayaop Enable rayaop]) dnl 拡張機能が有効な場合の処理 if test "$PHP_RAYAOP" != "no"; then dnl 拡張機能が有効かどうかを定義 - dnl @link https://www.gnu.org/software/autoconf/manual/autoconf-2.68/html_node/Defining-Variables.html + dnl link https://www.gnu.org/software/autoconf/manual/autoconf-2.68/html_node/Defining-Variables.html AC_DEFINE(HAVE_RAYAOP, 1, [whether rayaop is enabled]) dnl PHPの新しい拡張機能を追加 - dnl @link https://www.phpinternalsbook.com/build_system/build_system.html + dnl link https://www.phpinternalsbook.com/build_system/build_system.html PHP_NEW_EXTENSION(rayaop, rayaop.c, $ext_shared) dnl Makefileフラグメントを追加 - dnl @link https://www.phpinternalsbook.com/build_system/build_system.html#php-add-makefile-fragment + dnl link https://www.phpinternalsbook.com/build_system/build_system.html#php-add-makefile-fragment PHP_ADD_MAKEFILE_FRAGMENT dnl ヘッダーファイルのインストール指示を追加 - dnl @link https://www.phpinternalsbook.com/build_system/build_system.html#php-install-headers + dnl link https://www.phpinternalsbook.com/build_system/build_system.html#php-install-headers PHP_INSTALL_HEADERS([ext/rayaop], [php_rayaop.h]) fi diff --git a/rayaop.c b/rayaop.c index 79f7fe9..2ff66c2 100644 --- a/rayaop.c +++ b/rayaop.c @@ -18,8 +18,8 @@ /** * インターセプト情報を保持する構造体 - * @link https://www.phpinternalsbook.com/php5/classes_objects/internal_structures_and_implementation.html - * @link http://php.adamharvey.name/manual/ja/internals2.variables.tables.php + * link: http//://www.phpinternalsbook.com/php5/classes_objects/internal_structures_and_implementation.html + * link: http://php.adamharvey.name/manual/ja/internals2.variables.tables.php */ typedef struct _intercept_info { zend_string *class_name; // インターセプト対象のクラス名 @@ -67,7 +67,7 @@ static void rayaop_zend_execute_ex(zend_execute_data *execute_data) zend_string *method_name = current_function->common.function_name; // メソッド名を取得 char *key = NULL; // ハッシュキー用の文字列ポインタ - size_t key_len = 0; // ハッシュキーの長さ + size_t key_len; // ハッシュキーの長さ key_len = spprintf(&key, 0, "%s::%s", ZSTR_VAL(class_name), ZSTR_VAL(method_name)); // ハッシュキーを生成 RAYAOP_DEBUG_PRINT("Generated key: %s", key); @@ -140,7 +140,7 @@ ZEND_END_ARG_INFO() /** * インターセプトメソッドを登録する関数 - * @link https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html + * link: https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html */ PHP_FUNCTION(method_intercept) { @@ -169,8 +169,7 @@ PHP_FUNCTION(method_intercept) RAYAOP_DEBUG_PRINT("Initialized intercept_info for %s::%s", class_name, method_name); char *key = NULL; // ハッシュキー用の文字列ポインタ - size_t key_len = 0; // ハッシュキーの長さ - key_len = spprintf(&key, 0, "%s::%s", class_name, method_name); // ハッシュキーを生成 + size_t key_len = spprintf(&key, 0, "%s::%s", class_name, method_name); // ハッシュキーを生成 RAYAOP_DEBUG_PRINT("Generated key: %s", key); if (zend_hash_str_update_ptr(intercept_ht, key, key_len, new_info) == NULL) { @@ -190,9 +189,9 @@ PHP_FUNCTION(method_intercept) /** * インターセプト情報を解放する関数 - * @link https://www.phpinternalsbook.com/php7/internal_types/strings/zend_strings.html + * link: https://www.phpinternalsbook.com/php7/internal_types/strings/zend_strings.html */ -static int efree_intercept_info(zval *zv) +static void efree_intercept_info(zval *zv) { intercept_info *info = Z_PTR_P(zv); // インターセプト情報を取得 if (info) { @@ -212,7 +211,7 @@ static int efree_intercept_info(zval *zv) /** * 拡張機能の初期化関数 - * @link https://www.phpinternalsbook.com/php7/extensions_design/hooks.html + * link: https://www.phpinternalsbook.com/php7/extensions_design/hooks.html */ PHP_MINIT_FUNCTION(rayaop) { @@ -234,7 +233,7 @@ PHP_MINIT_FUNCTION(rayaop) /** * 拡張機能のシャットダウン関数 - * @link https://www.phpinternalsbook.com/php7/extensions_design/hooks.html + * link: https://www.phpinternalsbook.com/php7/extensions_design/hooks.html */ PHP_MSHUTDOWN_FUNCTION(rayaop) { @@ -254,7 +253,7 @@ PHP_MSHUTDOWN_FUNCTION(rayaop) /** * 拡張機能の情報表示関数 - * @link https://www.phpinternalsbook.com/php7/extensions_design/extension_infos.html + * link: https://www.phpinternalsbook.com/php7/extensions_design/extension_infos.html */ PHP_MINFO_FUNCTION(rayaop) { @@ -272,7 +271,7 @@ static const zend_function_entry rayaop_functions[] = { }; // 拡張機能のモジュールエントリ -// @link https://www.phpinternalsbook.com/php7/extensions_design/extension_infos.html +// link https://www.phpinternalsbook.com/php7/extensions_design/extension_infos.html zend_module_entry rayaop_module_entry = { STANDARD_MODULE_HEADER, "rayaop", // 拡張機能の名前 From d919ea5c6c317e1c742e7fe38a0c5be2884c542c Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Mon, 1 Jul 2024 07:29:14 +0900 Subject: [PATCH 14/40] Update source files and set CMP0115 policy in CMakeLists.txt The source files set in the CMakeLists.txt have been updated, where the source file list was move from line 10 to 45 and the file `php_rayaop.h` was added to it. Additionally, the CMake policy CMP0115 has been set to NEW, enabling add_executable() or add_library() to run even if source files are not found, generating an error if the build does not locate the file. --- CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d84f55c..05b0e53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,9 +10,6 @@ project(rayaop C) # link https://cmake.org/cmake/help/latest/command/add_compile_definitions.html add_compile_definitions(HAVE_RAYAOP) -# ソースファイルのリストを指定 -# link https://cmake.org/cmake/help/latest/command/set.html -set(SOURCE_FILES php_rayaop rayaop.c) # `php-config` コマンドを使ってPHPのインクルードディレクトリを取得 # link https://cmake.org/cmake/help/latest/command/execute_process.html @@ -45,6 +42,16 @@ add_custom_target(configure DEPENDS ${SOURCE_FILES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) +# ソースファイルのリストを指定 +# link https://cmake.org/cmake/help/latest/command/set.html +set(SOURCE_FILES php_rayaop.h rayaop.c) + # ソースファイルからライブラリを作成(ただし、ALLビルドからは除外) # link https://cmake.org/cmake/help/latest/command/add_library.html add_library(___ EXCLUDE_FROM_ALL ${SOURCE_FILES}) + +# CMakeの CMP0115 ポリシーの振る舞いを設定。新しいポリシーでは、ソースファイルが存在しない場合でも +# add_executable() または add_library() を実行しますが、生成されたビルドがそのファイルを見つけられない場合にはエラーを生成します。 +# link https://cmake.org/cmake/help/latest/policy/CMP0115.html +cmake_policy(SET CMP0115 NEW) + From 935de3aab069cf5349f2d840148419489740bc0c Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 2 Jul 2024 01:47:07 +0900 Subject: [PATCH 15/40] Update memory management for intercept hash table The persistent allocation flag in pemalloc and pefree functions for the intercept hash table is changed from 1 to 0. This reduces the use of persistent memory allocation, which can retain allocated memory across requests. Commented out the zend_hash_destroy function call to prevent unexpected destruction of the hash table. --- rayaop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rayaop.c b/rayaop.c index 2ff66c2..bf78e42 100644 --- a/rayaop.c +++ b/rayaop.c @@ -220,12 +220,12 @@ PHP_MINIT_FUNCTION(rayaop) original_zend_execute_ex = zend_execute_ex; // 元のzend_execute_ex関数を保存 zend_execute_ex = rayaop_zend_execute_ex; // カスタムzend_execute_ex関数を設定 - intercept_ht = pemalloc(sizeof(HashTable), 1); // ハッシュテーブルを確保 + intercept_ht = pemalloc(sizeof(HashTable), 0); // ハッシュテーブルを確保 if (!intercept_ht) { php_error_docref(NULL, E_ERROR, "Failed to allocate memory for intercept hash table"); return FAILURE; } - zend_hash_init(intercept_ht, 8, NULL, (dtor_func_t)efree_intercept_info, 1); // ハッシュテーブルを初期化 + zend_hash_init(intercept_ht, 8, NULL, (dtor_func_t)efree_intercept_info, 0); // ハッシュテーブルを初期化 RAYAOP_DEBUG_PRINT("RayAOP extension initialized"); return SUCCESS; // 初期化成功 @@ -242,8 +242,8 @@ PHP_MSHUTDOWN_FUNCTION(rayaop) zend_execute_ex = original_zend_execute_ex; // 元のzend_execute_ex関数を復元 if (intercept_ht) { - zend_hash_destroy(intercept_ht); // ハッシュテーブルを破棄 - pefree(intercept_ht, 1); // ハッシュテーブルのメモリを解放 + // zend_hash_destroy(intercept_ht); // ハッシュテーブルを破棄 + pefree(intercept_ht, 0); // ハッシュテーブルのメモリを解放 intercept_ht = NULL; // ハッシュテーブルポインタをNULLに設定 } From 4cc134eca9e368d61b9502c4510f918d957890d9 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 2 Jul 2024 01:49:16 +0900 Subject: [PATCH 16/40] Change script name in run function The name of the script that is executed in the run function of build.sh has been updated. The old script, rayaop.php, has been replaced by smoke.php. --- build.sh | 2 +- rayaop.php => smoke.php | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename rayaop.php => smoke.php (100%) diff --git a/build.sh b/build.sh index de64399..4117f68 100755 --- a/build.sh +++ b/build.sh @@ -24,7 +24,7 @@ install() { run() { echo "Run..." - php -dextension=modules/rayaop.so -ddisplay_errors=1 rayaop.php + php -dextension=modules/rayaop.so -ddisplay_errors=1 smoke.php } case $1 in diff --git a/rayaop.php b/smoke.php similarity index 100% rename from rayaop.php rename to smoke.php From 4e935caec59345d941e21b07fc00a50f6348810a Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 2 Jul 2024 01:53:08 +0900 Subject: [PATCH 17/40] Update PHP command in GitHub workflow The PHP command in .github/workflows/build.yml has been updated. The 'rayaop.php' script has been replaced with 'smoke.php'. Additionally, a space character was removed between the '-d' flag and 'extension' in the PHP command to correct syntax. --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb0ddcb..4e68e9b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,5 +37,5 @@ jobs: - name: Run demo run: | - php -d extension=./modules/rayaop.so -i | grep rayaop - timeout 60s php -d extension=./modules/rayaop.so -d memory_limit=128M -d report_memleaks=1 -d zend.assertions=1 -d assert.exception=1 rayaop.php 2> php_stderr.log || true + php -dextension=./modules/rayaop.so -i | grep rayaop + timeout 60s php -dextension=./modules/rayaop.so -d memory_limit=128M -d report_memleaks=1 -d zend.assertions=1 -d assert.exception=1 smoke.php From 4cc434aa937e5d3e12ec5252930538730c4cc4ac Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Tue, 2 Jul 2024 01:56:54 +0900 Subject: [PATCH 18/40] fixup! Update PHP command in GitHub workflow --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4e68e9b..acfe8c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,4 +38,4 @@ jobs: - name: Run demo run: | php -dextension=./modules/rayaop.so -i | grep rayaop - timeout 60s php -dextension=./modules/rayaop.so -d memory_limit=128M -d report_memleaks=1 -d zend.assertions=1 -d assert.exception=1 smoke.php + timeout 60s php -dextension=./modules/rayaop.so -dmemory_limit=128M -dreport_memleaks=1 -dzend.assertions=1 -dassert.exception=1 smoke.php From 29a4a16c68d6e6812a0216ad3cd926b93f31d54a Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Fri, 5 Jul 2024 04:21:05 +0900 Subject: [PATCH 19/40] Refactor hash table allocation and shutdown functions in rayaop.c Replaced manual hash table allocation with ALLOC_HASHTABLE for simplification and efficiency. The shutdown functions were also modified to provide more descriptive debugging messages and to handle the restoration of original Zend functions and the memory release in a more correct way. Also, the request shutdown function now uses PHP_MSHUTDOWN function. --- rayaop.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/rayaop.c b/rayaop.c index bf78e42..5e70e35 100644 --- a/rayaop.c +++ b/rayaop.c @@ -220,11 +220,7 @@ PHP_MINIT_FUNCTION(rayaop) original_zend_execute_ex = zend_execute_ex; // 元のzend_execute_ex関数を保存 zend_execute_ex = rayaop_zend_execute_ex; // カスタムzend_execute_ex関数を設定 - intercept_ht = pemalloc(sizeof(HashTable), 0); // ハッシュテーブルを確保 - if (!intercept_ht) { - php_error_docref(NULL, E_ERROR, "Failed to allocate memory for intercept hash table"); - return FAILURE; - } + ALLOC_HASHTABLE(intercept_ht); // ハッシュテーブルを確保 zend_hash_init(intercept_ht, 8, NULL, (dtor_func_t)efree_intercept_info, 0); // ハッシュテーブルを初期化 RAYAOP_DEBUG_PRINT("RayAOP extension initialized"); @@ -237,17 +233,29 @@ PHP_MINIT_FUNCTION(rayaop) */ PHP_MSHUTDOWN_FUNCTION(rayaop) { - RAYAOP_DEBUG_PRINT("PHP_MSHUTDOWN_FUNCTION called"); + RAYAOP_DEBUG_PRINT("RayAOP PHP_MSHUTDOWN_FUNCTION called"); zend_execute_ex = original_zend_execute_ex; // 元のzend_execute_ex関数を復元 + original_zend_execute_ex = NULL; + + RAYAOP_DEBUG_PRINT("RayAOP PHP_MSHUTDOWN_FUNCTION shut down"); + return SUCCESS; // シャットダウン成功 +} +/** + * 拡張機能のリクエストシャットダウン関数 + * link: https://www.phpinternalsbook.com/php7/extensions_design/hooks.html + */ +PHP_RSHUTDOWN_FUNCTION(rayaop) +{ + RAYAOP_DEBUG_PRINT("RayAOP PHP_RSHUTDOWN_FUNCTION called"); if (intercept_ht) { - // zend_hash_destroy(intercept_ht); // ハッシュテーブルを破棄 - pefree(intercept_ht, 0); // ハッシュテーブルのメモリを解放 + zend_hash_destroy(intercept_ht); // ハッシュテーブルを破棄 + FREE_HASHTABLE(intercept_ht); // ハッシュテーブルのメモリを解放 intercept_ht = NULL; // ハッシュテーブルポインタをNULLに設定 } - RAYAOP_DEBUG_PRINT("RayAOP extension shut down"); + RAYAOP_DEBUG_PRINT("RayAOP PHP_RSHUTDOWN_FUNCTION shut down"); return SUCCESS; // シャットダウン成功 } @@ -279,7 +287,7 @@ zend_module_entry rayaop_module_entry = { PHP_MINIT(rayaop), // 拡張機能の初期化関数 PHP_MSHUTDOWN(rayaop), // 拡張機能のシャットダウン関数 NULL, // リクエスト開始時の関数(未使用) - NULL, // リクエスト終了時の関数(未使用) + PHP_MSHUTDOWN(rayaop), // リクエスト終了時の関数(未使用) PHP_MINFO(rayaop), // 拡張機能の情報表示関数 PHP_RAYAOP_VERSION, // 拡張機能のバージョン STANDARD_MODULE_PROPERTIES From 959503fad4c97083346250f8a319268917732af3 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Fri, 5 Jul 2024 04:23:01 +0900 Subject: [PATCH 20/40] Enable RAYAOP_DEBUG for debugging The code has been updated to enable RAYAOP_DEBUG. This will allow debugging output when required, which will assist in identifying and resolving potential issues more efficiently. --- rayaop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rayaop.c b/rayaop.c index 5e70e35..1ac5eae 100644 --- a/rayaop.c +++ b/rayaop.c @@ -14,7 +14,7 @@ #endif // デバッグ出力を有効にする場合はこのマクロをアンコメントしてください -// #define RAYAOP_DEBUG +#define RAYAOP_DEBUG /** * インターセプト情報を保持する構造体 From 62b0e71514982af55d93a6abfe297d4db7364c2b Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 09:05:33 +0900 Subject: [PATCH 21/40] Update build workflow with memory check and test enhancements The build workflow has been updated to include Valgrind for memory checking. It now also continues after test failures, which allows for more comprehensive error reporting. In addition, logs are now uploaded when a test fails, providing more diagnostic information for debugging. --- .github/workflows/build.yml | 72 ++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index acfe8c2..c3103eb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: jobs: - build: + build-and-test: runs-on: ubuntu-latest strategy: @@ -23,8 +23,10 @@ jobs: php-version: ${{ matrix.php-version }} extensions: mbstring, intl - - name: Install build tools - run: sudo apt-get update && sudo apt-get install -y autoconf automake libtool bison re2c + - name: Install build tools and Valgrind + run: | + sudo apt-get update + sudo apt-get install -y autoconf automake libtool bison re2c valgrind - name: Prepare build run: | @@ -33,9 +35,69 @@ jobs: make - name: Run tests + id: run_tests run: make test + continue-on-error: true - - name: Run demo + - name: Run demo with debug logging + id: run_demo run: | php -dextension=./modules/rayaop.so -i | grep rayaop - timeout 60s php -dextension=./modules/rayaop.so -dmemory_limit=128M -dreport_memleaks=1 -dzend.assertions=1 -dassert.exception=1 smoke.php + timeout 60s php -n -dextension=./modules/rayaop.so -dmemory_limit=128M -dreport_memleaks=1 -dzend.assertions=1 -dassert.exception=1 smoke.php + continue-on-error: true + + - name: Run Valgrind memory check + if: steps.run_tests.outcome == 'failure' || steps.run_demo.outcome == 'failure' + run: | + cat << EOF > valgrind.supp + { + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:php_module_startup + ... + } + EOF + valgrind --suppressions=valgrind.supp --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt php -n -dextension=./modules/rayaop.so smoke.php + + - name: Check Valgrind results + if: steps.run_tests.outcome == 'failure' || steps.run_demo.outcome == 'failure' + run: | + if [ -f valgrind-out.txt ]; then + echo "Valgrind log found:" + cat valgrind-out.txt + if ! grep -q "ERROR SUMMARY: 0 errors from 0 contexts" valgrind-out.txt; then + echo "Valgrind found errors" + exit 1 + fi + else + echo "Valgrind log not found. This is unexpected." + exit 1 + fi + + - name: Upload Valgrind log file + if: (steps.run_tests.outcome == 'failure' || steps.run_demo.outcome == 'failure') && always() + uses: actions/upload-artifact@v2 + with: + name: valgrind-log + path: valgrind-out.txt + if-no-files-found: warn + + - name: Upload test logs + if: failure() + uses: actions/upload-artifact@v2 + with: + name: test-logs + path: | + tests/*.log + tests/*.sh + if-no-files-found: warn + + - name: Final status check + if: always() + run: | + if [ "${{ steps.run_tests.outcome }}" == "failure" ] || [ "${{ steps.run_demo.outcome }}" == "failure" ]; then + echo "Tests or demo run failed. Please check the logs for more information." + exit 1 + fi From e0135b63365feeb0918426fc3a5c22742233571d Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 09:05:58 +0900 Subject: [PATCH 22/40] Update PROGRAM_PARAMS in make.xml The filename 'rayaop.php' in the PROGRAM_PARAMS section of make.xml file has been updated to 'smoke.php'. This change ensures that the correct script is being referenced when the make configuration is executed. --- .idea/runConfigurations/make.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.idea/runConfigurations/make.xml b/.idea/runConfigurations/make.xml index 865f5a9..a7a7de3 100644 --- a/.idea/runConfigurations/make.xml +++ b/.idea/runConfigurations/make.xml @@ -1,5 +1,5 @@ - + From e2fbca1c549838d8ab47db122e1d0ced89745118 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 09:25:29 +0900 Subject: [PATCH 23/40] Refactor rayaop_zend_execute_ex for better maintainability The function rayaop_zend_execute_ex has been refactored into smaller, readable helper functions which improves code maintainability and readability. This includes error handling, intercept conditional checks, intercept key generation, intercept info retrieval, and actual execution of intercept, thus adhering more closely to the Single Responsibility Principle. --- rayaop.c | 164 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 93 insertions(+), 71 deletions(-) diff --git a/rayaop.c b/rayaop.c index 1ac5eae..807305d 100644 --- a/rayaop.c +++ b/rayaop.c @@ -48,87 +48,109 @@ static zend_bool is_intercepting = 0; // インターセプト中かどうか static zend_class_entry *zend_ce_ray_aop_interceptedinterface; // カスタム zend_execute_ex 関数 +#define RAYAOP_ERROR_MEMORY_ALLOCATION 1 +#define RAYAOP_ERROR_HASH_UPDATE 2 + +static void rayaop_handle_error(int error_code, const char *message) { + switch (error_code) { + case RAYAOP_ERROR_MEMORY_ALLOCATION: + php_error_docref(NULL, E_ERROR, "Memory allocation failed: %s", message); + break; + case RAYAOP_ERROR_HASH_UPDATE: + php_error_docref(NULL, E_ERROR, "Failed to update hash table: %s", message); + break; + default: + php_error_docref(NULL, E_ERROR, "Unknown error: %s", message); + } +} + +static bool rayaop_should_intercept(zend_execute_data *execute_data) { + return execute_data->func->common.scope && execute_data->func->common.function_name && !is_intercepting; +} + +static char* rayaop_generate_intercept_key(zend_string *class_name, zend_string *method_name, size_t *key_len) { + char *key = NULL; + *key_len = spprintf(&key, 0, "%s::%s", ZSTR_VAL(class_name), ZSTR_VAL(method_name)); + RAYAOP_DEBUG_PRINT("Generated key: %s", key); + return key; +} + +static intercept_info* rayaop_find_intercept_info(const char *key, size_t key_len) { + return zend_hash_str_find_ptr(intercept_ht, key, key_len); +} + +static void rayaop_execute_intercept(zend_execute_data *execute_data, intercept_info *info) { + if (Z_TYPE(info->handler) != IS_OBJECT) { + return; + } + + if (execute_data->This.value.obj == NULL) { + RAYAOP_DEBUG_PRINT("Object is NULL, calling original function"); + original_zend_execute_ex(execute_data); + return; + } + + zval retval, params[3]; + ZVAL_OBJ(¶ms[0], execute_data->This.value.obj); + ZVAL_STR(¶ms[1], info->method_name); + + array_init(¶ms[2]); + uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data); + zval *args = ZEND_CALL_ARG(execute_data, 1); + for (uint32_t i = 0; i < arg_count; i++) { + zval *arg = &args[i]; + Z_TRY_ADDREF_P(arg); + add_next_index_zval(¶ms[2], arg); + } + + is_intercepting = 1; + zval func_name; + ZVAL_STRING(&func_name, "intercept"); + + ZVAL_UNDEF(&retval); + if (call_user_function(NULL, &info->handler, &func_name, &retval, 3, params) == SUCCESS) { + if (!Z_ISUNDEF(retval)) { + ZVAL_COPY(execute_data->return_value, &retval); + } + } else { + php_error_docref(NULL, E_WARNING, "Interception failed for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name)); + } + + zval_ptr_dtor(&retval); + zval_ptr_dtor(&func_name); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + + is_intercepting = 0; +} + static void rayaop_zend_execute_ex(zend_execute_data *execute_data) { RAYAOP_DEBUG_PRINT("rayaop_zend_execute_ex called"); - // 既にインターセプト中の場合は元の関数を呼び出す - if (is_intercepting) { - RAYAOP_DEBUG_PRINT("Already intercepting, calling original zend_execute_ex"); - original_zend_execute_ex(execute_data); // 元のzend_execute_ex関数を呼び出す + if (!rayaop_should_intercept(execute_data)) { + original_zend_execute_ex(execute_data); return; } - zend_function *current_function = execute_data->func; // 現在の関数情報を取得 - - // クラスメソッドの場合のみ処理を行う - if (current_function->common.scope && current_function->common.function_name) { - zend_string *class_name = current_function->common.scope->name; // クラス名を取得 - zend_string *method_name = current_function->common.function_name; // メソッド名を取得 - - char *key = NULL; // ハッシュキー用の文字列ポインタ - size_t key_len; // ハッシュキーの長さ - key_len = spprintf(&key, 0, "%s::%s", ZSTR_VAL(class_name), ZSTR_VAL(method_name)); // ハッシュキーを生成 - RAYAOP_DEBUG_PRINT("Generated key: %s", key); - - intercept_info *info = zend_hash_str_find_ptr(intercept_ht, key, key_len); // ハッシュテーブルからインターセプト情報を取得 - - if (info) { - RAYAOP_DEBUG_PRINT("Found intercept info for key: %s", key); - - if (Z_TYPE(info->handler) == IS_OBJECT) { - zval retval, params[3]; // 戻り値とパラメータ用のzvalを宣言 - - // オブジェクトがNULLの場合は元の関数を実行 - if (execute_data->This.value.obj == NULL) { - RAYAOP_DEBUG_PRINT("Object is NULL, calling original function"); - original_zend_execute_ex(execute_data); - efree(key); // キーを解放 - return; - } - - ZVAL_OBJ(¶ms[0], execute_data->This.value.obj); // オブジェクトを設定 - ZVAL_STR(¶ms[1], method_name); // メソッド名を設定 - - array_init(¶ms[2]); // 引数の配列を初期化 - uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data); // 引数の数を取得 - zval *args = ZEND_CALL_ARG(execute_data, 1); // 引数を取得 - for (uint32_t i = 0; i < arg_count; i++) { - zval *arg = &args[i]; // 各引数を取得 - Z_TRY_ADDREF_P(arg); // 引数の参照カウントを増やす - add_next_index_zval(¶ms[2], arg); // 配列に引数を追加 - } - - is_intercepting = 1; // インターセプト中フラグを設定 - zval func_name; - ZVAL_STRING(&func_name, "intercept"); // インターセプトハンドラーのメソッド名を設定 - - ZVAL_UNDEF(&retval); - if (call_user_function(NULL, &info->handler, &func_name, &retval, 3, params) == SUCCESS) { - if (!Z_ISUNDEF(retval)) { - ZVAL_COPY(execute_data->return_value, &retval); // 戻り値を設定 - } - } else { - php_error_docref(NULL, E_WARNING, "Interception failed for %s::%s", ZSTR_VAL(class_name), ZSTR_VAL(method_name)); - } - zval_ptr_dtor(&retval); // 戻り値を解放 - - zval_ptr_dtor(&func_name); // メソッド名のデストラクタを呼ぶ - zval_ptr_dtor(¶ms[1]); // メソッド名のデストラクタを呼ぶ - zval_ptr_dtor(¶ms[2]); // 引数配列のデストラクタを呼ぶ - - is_intercepting = 0; // インターセプト中フラグを解除 - efree(key); // キーを解放 - return; - } - } else { - RAYAOP_DEBUG_PRINT("No intercept info found for key: %s", key); - } + zend_function *current_function = execute_data->func; + zend_string *class_name = current_function->common.scope->name; + zend_string *method_name = current_function->common.function_name; - efree(key); // キーを解放 + size_t key_len; + char *key = rayaop_generate_intercept_key(class_name, method_name, &key_len); + + intercept_info *info = rayaop_find_intercept_info(key, key_len); + + if (info) { + RAYAOP_DEBUG_PRINT("Found intercept info for key: %s", key); + rayaop_execute_intercept(execute_data, info); + } else { + RAYAOP_DEBUG_PRINT("No intercept info found for key: %s", key); + original_zend_execute_ex(execute_data); } - original_zend_execute_ex(execute_data); // 元のzend_execute_ex関数を呼び出す + efree(key); } // method_intercept 関数の引数情報 From ac470768892e58a3068c743733a85c5b03963763 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 09:40:59 +0900 Subject: [PATCH 24/40] Update RayAop PHP extension header The header file for the RayAop PHP extension has been significantly updated. This includes the removal of the extension's copyright and licensing information, and instead incorporating additional configuration and function declarations. Modifications also included version upgrade for RayAop extension and added more functionalities related to interception. --- php_rayaop.h | 118 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 39 deletions(-) diff --git a/php_rayaop.h b/php_rayaop.h index 72d0386..c9aef16 100644 --- a/php_rayaop.h +++ b/php_rayaop.h @@ -1,51 +1,91 @@ -/* - +----------------------------------------------------------------------+ - | RayAop PHP extension | - +----------------------------------------------------------------------+ - | Copyright (c) 2018 NAME | - +----------------------------------------------------------------------+ - | Permission is hereby granted, free of charge, to any person | - | obtaining a copy of this software and associated documentation files | - | (the "Software"), to deal in the Software without restriction, | - | including without limitation the rights to use, copy, modify, merge, | - | publish, distribute, sublicense, and/or sell copies of the Software, | - | and to permit persons to whom the Software is furnished to do so, | - | subject to the following conditions: | - | | - | The above copyright notice and this permission notice shall be | - | included in all copies or substantial portions of the Software. | - | | - | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | - | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | - | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | - | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | - | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | - | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | - | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | - | SOFTWARE. | - +----------------------------------------------------------------------+ - | Author: NAME | - +----------------------------------------------------------------------+ -*/ - #ifndef PHP_RAYAOP_H -#define PHP_RAYAOP_H 1 +#define PHP_RAYAOP_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "zend_exceptions.h" +#include "zend_interfaces.h" + +#ifdef ZTS +#include "TSRM.h" +#endif -#define PHP_RAYAOP_VERSION "0.0.1-dev" -#define PHP_RAYAOP_EXTNAME "rayaop" +#define PHP_RAYAOP_VERSION "1.0.0" +#define RAYAOP_NS "Ray\\Aop\\" + +extern zend_module_entry rayaop_module_entry; +#define phpext_rayaop_ptr &rayaop_module_entry #ifdef PHP_WIN32 -# define PHP_RAYAOP_API __declspec(dllexport) +#define PHP_RAYAOP_API __declspec(dllexport) #elif defined(__GNUC__) && __GNUC__ >= 4 -# define PHP_RAYAOP_API __attribute__ ((visibility("default"))) +#define PHP_RAYAOP_API __attribute__ ((visibility("default"))) #else -# define PHP_RAYAOP_API +#define PHP_RAYAOP_API #endif -/* Declare all functions and classes of the extension */ -static PHP_FUNCTION(rayaop_nop); +#ifdef ZTS +#include "TSRM.h" +#endif -extern zend_module_entry rayaop_module_entry; +// デバッグ出力用マクロ +#ifdef RAYAOP_DEBUG +#define RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) +#else +#define RAYAOP_DEBUG_PRINT(fmt, ...) +#endif + +// エラーコード +#define RAYAOP_ERROR_MEMORY_ALLOCATION 1 +#define RAYAOP_ERROR_HASH_UPDATE 2 + +/** + * インターセプト情報を保持する構造体 + * link: http//://www.phpinternalsbook.com/php5/classes_objects/internal_structures_and_implementation.html + * link: http://php.adamharvey.name/manual/ja/internals2.variables.tables.php + */ +typedef struct _intercept_info { + zend_string *class_name; // インターセプト対象のクラス名 + zend_string *method_name; // インターセプト対象のメソッド名 + zval handler; // インターセプトハンドラー +} intercept_info; + +// 関数宣言 +PHP_MINIT_FUNCTION(rayaop); +PHP_MSHUTDOWN_FUNCTION(rayaop); +PHP_RINIT_FUNCTION(rayaop); +PHP_RSHUTDOWN_FUNCTION(rayaop); +PHP_MINFO_FUNCTION(rayaop); + +PHP_FUNCTION(method_intercept); +// ユーティリティ関数の宣言 +void rayaop_handle_error(int error_code, const char *message); +bool rayaop_should_intercept(zend_execute_data *execute_data); +char* rayaop_generate_intercept_key(zend_string *class_name, zend_string *method_name, size_t *key_len); +intercept_info* rayaop_find_intercept_info(const char *key, size_t key_len); +void rayaop_execute_intercept(zend_execute_data *execute_data, intercept_info *info); +void rayaop_free_intercept_info(zval *zv); + +#ifdef RAYAOP_DEBUG +void rayaop_debug_print_zval(zval *value); +void rayaop_dump_intercept_info(void); +#endif + +ZEND_BEGIN_MODULE_GLOBALS(rayaop) + HashTable *intercept_ht; + zend_bool is_intercepting; +ZEND_END_MODULE_GLOBALS(rayaop) + +#ifdef ZTS +#define RAYAOP_G(v) TSRMG(rayaop_globals_id, zend_rayaop_globals *, v) +#else +#define RAYAOP_G(v) (rayaop_globals.v) #endif +#endif /* PHP_RAYAOP_H */ \ No newline at end of file From ca13b0d8b81b772270cb2c93a2a32d909ecc3a08 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 09:41:14 +0900 Subject: [PATCH 25/40] Refactor rayaop.c for better management of intercept info This update significantly refactors the rayaop.c file. The code for registering, handling, and freeing intercept information has been improved. The implementation of global variables has also been optimized specifically for thread-safe builds. This update aims to enhance maintainability and efficiency of the code. --- rayaop.c | 260 ++++++++++++++++++++++++++----------------------------- 1 file changed, 122 insertions(+), 138 deletions(-) diff --git a/rayaop.c b/rayaop.c index 807305d..a0a95a1 100644 --- a/rayaop.c +++ b/rayaop.c @@ -2,40 +2,22 @@ #include "config.h" #endif -#include "php.h" // PHPのメインヘッダー -#include "php_ini.h" // PHP設定関連のヘッダー -#include "ext/standard/info.h" // 標準の情報関数用ヘッダー -#include "zend_exceptions.h" // Zend例外用ヘッダー -#include "zend_interfaces.h" // Zendインターフェース用ヘッダー -#include "php_rayaop.h" // 拡張機能のヘッダー +#include "php_rayaop.h" -#ifdef ZTS -#include "TSRM.h" // スレッドセーフリソース管理ヘッダー -#endif +// モジュールグローバル変数の宣言 +ZEND_DECLARE_MODULE_GLOBALS(rayaop) -// デバッグ出力を有効にする場合はこのマクロをアンコメントしてください -#define RAYAOP_DEBUG +// 静的変数の宣言 +static void (*original_zend_execute_ex)(zend_execute_data *execute_data); -/** - * インターセプト情報を保持する構造体 - * link: http//://www.phpinternalsbook.com/php5/classes_objects/internal_structures_and_implementation.html - * link: http://php.adamharvey.name/manual/ja/internals2.variables.tables.php - */ -typedef struct _intercept_info { - zend_string *class_name; // インターセプト対象のクラス名 - zend_string *method_name; // インターセプト対象のメソッド名 - zval handler; // インターセプトハンドラー -} intercept_info; +// グローバル初期化関数 +static void php_rayaop_init_globals(zend_rayaop_globals *rayaop_globals) +{ + rayaop_globals->intercept_ht = NULL; + rayaop_globals->is_intercepting = 0; +} -// グローバル変数の宣言 -static HashTable *intercept_ht = NULL; // インターセプト情報を格納するハッシュテーブル -static void (*original_zend_execute_ex)(zend_execute_data *execute_data); // 元のzend_execute_ex関数へのポインタ - -#ifdef ZTS -static THREAD_LOCAL zend_bool is_intercepting = 0; // スレッドローカル変数(スレッドセーフビルド時用) -#else -static zend_bool is_intercepting = 0; // インターセプト中かどうかのフラグ -#endif +static void (*original_zend_execute_ex)(zend_execute_data *execute_data); // デバッグ出力用マクロ #ifdef RAYAOP_DEBUG @@ -44,14 +26,42 @@ static zend_bool is_intercepting = 0; // インターセプト中かどうか #define RAYAOP_DEBUG_PRINT(fmt, ...) #endif -// インターセプトされたインターフェースのクラスエントリ -static zend_class_entry *zend_ce_ray_aop_interceptedinterface; +// method_intercept 関数の引数情報 +ZEND_BEGIN_ARG_INFO_EX(arginfo_method_intercept, 0, 0, 3) + ZEND_ARG_TYPE_INFO(0, class_name, IS_STRING, 0) // クラス名の引数情報 + ZEND_ARG_TYPE_INFO(0, method_name, IS_STRING, 0) // メソッド名の引数情報 + ZEND_ARG_OBJ_INFO(0, interceptor, Ray\\Aop\\MethodInterceptorInterface, 0) // インターセプトハンドラーの引数情報 +ZEND_END_ARG_INFO() -// カスタム zend_execute_ex 関数 -#define RAYAOP_ERROR_MEMORY_ALLOCATION 1 -#define RAYAOP_ERROR_HASH_UPDATE 2 +// 拡張機能が提供する関数の定義 +// https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html +static const zend_function_entry rayaop_functions[] = { + PHP_FE(method_intercept, arginfo_method_intercept) // method_intercept関数の登録 + PHP_FE_END // 関数エントリの終了 +}; -static void rayaop_handle_error(int error_code, const char *message) { +// 拡張機能のモジュールエントリ +// link https://www.phpinternalsbook.com/php7/extensions_design/extension_infos.html +zend_module_entry rayaop_module_entry = { + STANDARD_MODULE_HEADER, + "rayaop", // 拡張機能の名前 + rayaop_functions, // 拡張機能が提供する関数 + PHP_MINIT(rayaop), // 拡張機能の初期化関数 + PHP_MSHUTDOWN(rayaop), // 拡張機能のシャットダウン関数 + PHP_RINIT(rayaop), // リクエスト開始時の関数 + PHP_RSHUTDOWN(rayaop), // リクエスト終了時の関数 + PHP_MINFO(rayaop), // 拡張機能の情報表示関数 + PHP_RAYAOP_VERSION, // 拡張機能のバージョン + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_RAYAOP +ZEND_GET_MODULE(rayaop) +#endif + +// ユーティリティ関数の実装 + +void rayaop_handle_error(int error_code, const char *message) { switch (error_code) { case RAYAOP_ERROR_MEMORY_ALLOCATION: php_error_docref(NULL, E_ERROR, "Memory allocation failed: %s", message); @@ -64,22 +74,24 @@ static void rayaop_handle_error(int error_code, const char *message) { } } -static bool rayaop_should_intercept(zend_execute_data *execute_data) { - return execute_data->func->common.scope && execute_data->func->common.function_name && !is_intercepting; +bool rayaop_should_intercept(zend_execute_data *execute_data) { + return execute_data->func->common.scope && + execute_data->func->common.function_name && + !RAYAOP_G(is_intercepting); } -static char* rayaop_generate_intercept_key(zend_string *class_name, zend_string *method_name, size_t *key_len) { +char* rayaop_generate_intercept_key(zend_string *class_name, zend_string *method_name, size_t *key_len) { char *key = NULL; *key_len = spprintf(&key, 0, "%s::%s", ZSTR_VAL(class_name), ZSTR_VAL(method_name)); RAYAOP_DEBUG_PRINT("Generated key: %s", key); return key; } -static intercept_info* rayaop_find_intercept_info(const char *key, size_t key_len) { - return zend_hash_str_find_ptr(intercept_ht, key, key_len); +intercept_info* rayaop_find_intercept_info(const char *key, size_t key_len) { + return zend_hash_str_find_ptr(RAYAOP_G(intercept_ht), key, key_len); } -static void rayaop_execute_intercept(zend_execute_data *execute_data, intercept_info *info) { +void rayaop_execute_intercept(zend_execute_data *execute_data, intercept_info *info) { if (Z_TYPE(info->handler) != IS_OBJECT) { return; } @@ -103,7 +115,7 @@ static void rayaop_execute_intercept(zend_execute_data *execute_data, intercept_ add_next_index_zval(¶ms[2], arg); } - is_intercepting = 1; + RAYAOP_G(is_intercepting) = 1; zval func_name; ZVAL_STRING(&func_name, "intercept"); @@ -121,11 +133,26 @@ static void rayaop_execute_intercept(zend_execute_data *execute_data, intercept_ zval_ptr_dtor(¶ms[1]); zval_ptr_dtor(¶ms[2]); - is_intercepting = 0; + RAYAOP_G(is_intercepting) = 0; } -static void rayaop_zend_execute_ex(zend_execute_data *execute_data) -{ +/** + * インターセプト情報を解放する関数 + * link: https://www.phpinternalsbook.com/php7/internal_types/strings/zend_strings.html + */ +void rayaop_free_intercept_info(zval *zv) { + intercept_info *info = Z_PTR_P(zv); + if (info) { + RAYAOP_DEBUG_PRINT("Freeing intercept info for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name)); + zend_string_release(info->class_name); + zend_string_release(info->method_name); + zval_ptr_dtor(&info->handler); + efree(info); + } +} + +// カスタム zend_execute_ex 関数 +static void rayaop_zend_execute_ex(zend_execute_data *execute_data) { RAYAOP_DEBUG_PRINT("rayaop_zend_execute_ex called"); if (!rayaop_should_intercept(execute_data)) { @@ -153,19 +180,11 @@ static void rayaop_zend_execute_ex(zend_execute_data *execute_data) efree(key); } -// method_intercept 関数の引数情報 -ZEND_BEGIN_ARG_INFO_EX(arginfo_method_intercept, 0, 0, 3) - ZEND_ARG_TYPE_INFO(0, class_name, IS_STRING, 0) // クラス名の引数情報 - ZEND_ARG_TYPE_INFO(0, method_name, IS_STRING, 0) // メソッド名の引数情報 - ZEND_ARG_OBJ_INFO(0, interceptor, Ray\\Aop\\MethodInterceptorInterface, 0) // インターセプトハンドラーの引数情報 -ZEND_END_ARG_INFO() - /** * インターセプトメソッドを登録する関数 * link: https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html */ -PHP_FUNCTION(method_intercept) -{ +PHP_FUNCTION(method_intercept) { RAYAOP_DEBUG_PRINT("method_intercept called"); char *class_name, *method_name; // クラス名とメソッド名のポインタ @@ -178,59 +197,34 @@ PHP_FUNCTION(method_intercept) Z_PARAM_OBJECT(intercepted) // インターセプトハンドラーのパラメータを解析 ZEND_PARSE_PARAMETERS_END(); - intercept_info *new_info = emalloc(sizeof(intercept_info)); + intercept_info *new_info = ecalloc(1, sizeof(intercept_info)); if (!new_info) { - php_error_docref(NULL, E_ERROR, "Memory allocation failed"); // メモリ確保に失敗した場合のエラー + rayaop_handle_error(RAYAOP_ERROR_MEMORY_ALLOCATION, "Failed to allocate memory for intercept_info"); RETURN_FALSE; } - RAYAOP_DEBUG_PRINT("Allocated memory for intercept_info"); - - new_info->class_name = zend_string_init(class_name, class_name_len, 0); // クラス名を初期化 - new_info->method_name = zend_string_init(method_name, method_name_len, 0); // メソッド名を初期化 - ZVAL_COPY(&new_info->handler, intercepted); // インターセプトハンドラーをコピー - RAYAOP_DEBUG_PRINT("Initialized intercept_info for %s::%s", class_name, method_name); - char *key = NULL; // ハッシュキー用の文字列ポインタ - size_t key_len = spprintf(&key, 0, "%s::%s", class_name, method_name); // ハッシュキーを生成 - RAYAOP_DEBUG_PRINT("Generated key: %s", key); + new_info->class_name = zend_string_init(class_name, class_name_len, 0); + new_info->method_name = zend_string_init(method_name, method_name_len, 0); + ZVAL_COPY(&new_info->handler, intercepted); - if (zend_hash_str_update_ptr(intercept_ht, key, key_len, new_info) == NULL) { - php_error_docref(NULL, E_ERROR, "Failed to update hash table"); // ハッシュテーブルの更新に失敗した場合のエラー - zend_string_release(new_info->class_name); // クラス名を解放 - zend_string_release(new_info->method_name); // メソッド名を解放 - zval_ptr_dtor(&new_info->handler); // ハンドラーを解放 - efree(new_info); // インターセプト情報を解放 - efree(key); // キーを解放 + char *key = NULL; + size_t key_len = spprintf(&key, 0, "%s::%s", class_name, method_name); + + if (zend_hash_str_update_ptr(RAYAOP_G(intercept_ht), key, key_len, new_info) == NULL) { + rayaop_handle_error(RAYAOP_ERROR_HASH_UPDATE, "Failed to update intercept hash table"); + zend_string_release(new_info->class_name); + zend_string_release(new_info->method_name); + zval_ptr_dtor(&new_info->handler); + efree(new_info); + efree(key); RETURN_FALSE; } - efree(key); // キーを解放 + efree(key); RAYAOP_DEBUG_PRINT("Successfully registered intercept info"); RETURN_TRUE; } -/** - * インターセプト情報を解放する関数 - * link: https://www.phpinternalsbook.com/php7/internal_types/strings/zend_strings.html - */ -static void efree_intercept_info(zval *zv) -{ - intercept_info *info = Z_PTR_P(zv); // インターセプト情報を取得 - if (info) { - RAYAOP_DEBUG_PRINT("Freeing intercept info for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name)); - - zend_string_release(info->class_name); // クラス名を解放 - zend_string_release(info->method_name); // メソッド名を解放 - RAYAOP_DEBUG_PRINT("class_name and method_name released"); - - zval_ptr_dtor(&info->handler); // ハンドラーを解放 - RAYAOP_DEBUG_PRINT("handler released"); - - efree(info); // インターセプト情報構造体を解放 - RAYAOP_DEBUG_PRINT("Memory freed for intercept info"); - } -} - /** * 拡張機能の初期化関数 * link: https://www.phpinternalsbook.com/php7/extensions_design/hooks.html @@ -239,14 +233,17 @@ PHP_MINIT_FUNCTION(rayaop) { RAYAOP_DEBUG_PRINT("PHP_MINIT_FUNCTION called"); - original_zend_execute_ex = zend_execute_ex; // 元のzend_execute_ex関数を保存 - zend_execute_ex = rayaop_zend_execute_ex; // カスタムzend_execute_ex関数を設定 +#ifdef ZTS + ts_allocate_id(&rayaop_globals_id, sizeof(zend_rayaop_globals), (ts_allocate_ctor) php_rayaop_init_globals, NULL); +#else + php_rayaop_init_globals(&rayaop_globals); +#endif - ALLOC_HASHTABLE(intercept_ht); // ハッシュテーブルを確保 - zend_hash_init(intercept_ht, 8, NULL, (dtor_func_t)efree_intercept_info, 0); // ハッシュテーブルを初期化 + original_zend_execute_ex = zend_execute_ex; + zend_execute_ex = rayaop_zend_execute_ex; RAYAOP_DEBUG_PRINT("RayAOP extension initialized"); - return SUCCESS; // 初期化成功 + return SUCCESS; } /** @@ -264,6 +261,22 @@ PHP_MSHUTDOWN_FUNCTION(rayaop) return SUCCESS; // シャットダウン成功 } +/** + * リクエスト開始時の初期化関数 + */ +PHP_RINIT_FUNCTION(rayaop) +{ + RAYAOP_DEBUG_PRINT("PHP_RINIT_FUNCTION called"); + + if (RAYAOP_G(intercept_ht) == NULL) { + ALLOC_HASHTABLE(RAYAOP_G(intercept_ht)); + zend_hash_init(RAYAOP_G(intercept_ht), 8, NULL, rayaop_free_intercept_info, 0); + } + RAYAOP_G(is_intercepting) = 0; + + return SUCCESS; +} + /** * 拡張機能のリクエストシャットダウン関数 * link: https://www.phpinternalsbook.com/php7/extensions_design/hooks.html @@ -271,10 +284,10 @@ PHP_MSHUTDOWN_FUNCTION(rayaop) PHP_RSHUTDOWN_FUNCTION(rayaop) { RAYAOP_DEBUG_PRINT("RayAOP PHP_RSHUTDOWN_FUNCTION called"); - if (intercept_ht) { - zend_hash_destroy(intercept_ht); // ハッシュテーブルを破棄 - FREE_HASHTABLE(intercept_ht); // ハッシュテーブルのメモリを解放 - intercept_ht = NULL; // ハッシュテーブルポインタをNULLに設定 + if (RAYAOP_G(intercept_ht)) { + zend_hash_destroy(RAYAOP_G(intercept_ht)); // ハッシュテーブルを破棄 + FREE_HASHTABLE(RAYAOP_G(intercept_ht)); // ハッシュテーブルのメモリを解放 + RAYAOP_G(intercept_ht) = NULL; // ハッシュテーブルポインタをNULLに設定 } RAYAOP_DEBUG_PRINT("RayAOP PHP_RSHUTDOWN_FUNCTION shut down"); @@ -287,41 +300,12 @@ PHP_RSHUTDOWN_FUNCTION(rayaop) */ PHP_MINFO_FUNCTION(rayaop) { - php_info_print_table_start(); // 情報テーブルの開始 +php_info_print_table_start(); // 情報テーブルの開始 php_info_print_table_header(2, "rayaop support", "enabled"); // テーブルヘッダーの表示 php_info_print_table_row(2, "Version", PHP_RAYAOP_VERSION); // バージョン情報の表示 php_info_print_table_end(); // 情報テーブルの終了 } -// 拡張機能が提供する関数の定義 -// https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html -static const zend_function_entry rayaop_functions[] = { - PHP_FE(method_intercept, arginfo_method_intercept) // method_intercept関数の登録 - PHP_FE_END // 関数エントリの終了 -}; - -// 拡張機能のモジュールエントリ -// link https://www.phpinternalsbook.com/php7/extensions_design/extension_infos.html -zend_module_entry rayaop_module_entry = { - STANDARD_MODULE_HEADER, - "rayaop", // 拡張機能の名前 - rayaop_functions, // 拡張機能が提供する関数 - PHP_MINIT(rayaop), // 拡張機能の初期化関数 - PHP_MSHUTDOWN(rayaop), // 拡張機能のシャットダウン関数 - NULL, // リクエスト開始時の関数(未使用) - PHP_MSHUTDOWN(rayaop), // リクエスト終了時の関数(未使用) - PHP_MINFO(rayaop), // 拡張機能の情報表示関数 - PHP_RAYAOP_VERSION, // 拡張機能のバージョン - STANDARD_MODULE_PROPERTIES -}; - -// 動的にロード可能なモジュールとしてコンパイルする場合の処理 -#ifdef COMPILE_DL_RAYAOP -ZEND_GET_MODULE(rayaop) -#endif - -// 以下は、必要に応じて追加の関数や定義を記述できます - // 例: デバッグ用のヘルパー関数 #ifdef RAYAOP_DEBUG static void rayaop_debug_print_zval(zval *value) @@ -363,10 +347,10 @@ static void rayaop_debug_print_zval(zval *value) static void rayaop_dump_intercept_info(void) { RAYAOP_DEBUG_PRINT("Dumping intercept information:"); - if (intercept_ht) { + if (RAYAOP_G(intercept_ht)) { zend_string *key; intercept_info *info; - ZEND_HASH_FOREACH_STR_KEY_PTR(intercept_ht, key, info) { + ZEND_HASH_FOREACH_STR_KEY_PTR(RAYAOP_G(intercept_ht), key, info) { if (key && info) { RAYAOP_DEBUG_PRINT("Key: %s", ZSTR_VAL(key)); RAYAOP_DEBUG_PRINT(" Class: %s", ZSTR_VAL(info->class_name)); @@ -378,4 +362,4 @@ static void rayaop_dump_intercept_info(void) RAYAOP_DEBUG_PRINT("Intercept hash table is not initialized"); } } -#endif +#endif \ No newline at end of file From ce44306b2a239d56245cfc3db6bd51fd712024a1 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 09:47:44 +0900 Subject: [PATCH 26/40] Refactor rayaop.c for improved organization and readability Moved the declaration of error constants to the top for quicker reference. Also, reorganized sections of code to follow a logical sequence, moving the declarations and definitions of functions and the extension module entry closer to the bottom of the file. Removed some debug helper functions for cleaner code. --- rayaop.c | 96 ++++++++++++++++++-------------------------------------- 1 file changed, 31 insertions(+), 65 deletions(-) diff --git a/rayaop.c b/rayaop.c index a0a95a1..8cd0759 100644 --- a/rayaop.c +++ b/rayaop.c @@ -4,6 +4,10 @@ #include "php_rayaop.h" +// エラー定数の定義 +#define RAYAOP_ERROR_MEMORY_ALLOCATION 1 +#define RAYAOP_ERROR_HASH_UPDATE 2 + // モジュールグローバル変数の宣言 ZEND_DECLARE_MODULE_GLOBALS(rayaop) @@ -17,8 +21,6 @@ static void php_rayaop_init_globals(zend_rayaop_globals *rayaop_globals) rayaop_globals->is_intercepting = 0; } -static void (*original_zend_execute_ex)(zend_execute_data *execute_data); - // デバッグ出力用マクロ #ifdef RAYAOP_DEBUG #define RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) @@ -33,32 +35,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_method_intercept, 0, 0, 3) ZEND_ARG_OBJ_INFO(0, interceptor, Ray\\Aop\\MethodInterceptorInterface, 0) // インターセプトハンドラーの引数情報 ZEND_END_ARG_INFO() -// 拡張機能が提供する関数の定義 -// https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html -static const zend_function_entry rayaop_functions[] = { - PHP_FE(method_intercept, arginfo_method_intercept) // method_intercept関数の登録 - PHP_FE_END // 関数エントリの終了 -}; - -// 拡張機能のモジュールエントリ -// link https://www.phpinternalsbook.com/php7/extensions_design/extension_infos.html -zend_module_entry rayaop_module_entry = { - STANDARD_MODULE_HEADER, - "rayaop", // 拡張機能の名前 - rayaop_functions, // 拡張機能が提供する関数 - PHP_MINIT(rayaop), // 拡張機能の初期化関数 - PHP_MSHUTDOWN(rayaop), // 拡張機能のシャットダウン関数 - PHP_RINIT(rayaop), // リクエスト開始時の関数 - PHP_RSHUTDOWN(rayaop), // リクエスト終了時の関数 - PHP_MINFO(rayaop), // 拡張機能の情報表示関数 - PHP_RAYAOP_VERSION, // 拡張機能のバージョン - STANDARD_MODULE_PROPERTIES -}; - -#ifdef COMPILE_DL_RAYAOP -ZEND_GET_MODULE(rayaop) -#endif - // ユーティリティ関数の実装 void rayaop_handle_error(int error_code, const char *message) { @@ -300,48 +276,12 @@ PHP_RSHUTDOWN_FUNCTION(rayaop) */ PHP_MINFO_FUNCTION(rayaop) { -php_info_print_table_start(); // 情報テーブルの開始 + php_info_print_table_start(); // 情報テーブルの開始 php_info_print_table_header(2, "rayaop support", "enabled"); // テーブルヘッダーの表示 php_info_print_table_row(2, "Version", PHP_RAYAOP_VERSION); // バージョン情報の表示 php_info_print_table_end(); // 情報テーブルの終了 } -// 例: デバッグ用のヘルパー関数 -#ifdef RAYAOP_DEBUG -static void rayaop_debug_print_zval(zval *value) -{ - switch (Z_TYPE_P(value)) { - case IS_NULL: - RAYAOP_DEBUG_PRINT("(null)"); - break; - case IS_TRUE: - RAYAOP_DEBUG_PRINT("(bool) true"); - break; - case IS_FALSE: - RAYAOP_DEBUG_PRINT("(bool) false"); - break; - case IS_LONG: - RAYAOP_DEBUG_PRINT("(int) %ld", Z_LVAL_P(value)); - break; - case IS_DOUBLE: - RAYAOP_DEBUG_PRINT("(float) %f", Z_DVAL_P(value)); - break; - case IS_STRING: - RAYAOP_DEBUG_PRINT("(string) %s", Z_STRVAL_P(value)); - break; - case IS_ARRAY: - RAYAOP_DEBUG_PRINT("(array)"); - break; - case IS_OBJECT: - RAYAOP_DEBUG_PRINT("(object)"); - break; - default: - RAYAOP_DEBUG_PRINT("(unknown type)"); - break; - } -} -#endif - // 例: インターセプト情報をダンプする関数 #ifdef RAYAOP_DEBUG static void rayaop_dump_intercept_info(void) @@ -362,4 +302,30 @@ static void rayaop_dump_intercept_info(void) RAYAOP_DEBUG_PRINT("Intercept hash table is not initialized"); } } +#endif + +// 拡張機能が提供する関数の定義 +// https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html +static const zend_function_entry rayaop_functions[] = { + PHP_FE(method_intercept, arginfo_method_intercept) // method_intercept関数の登録 + PHP_FE_END // 関数エントリの終了 +}; + +// 拡張機能のモジュールエントリ +// link https://www.phpinternalsbook.com/php7/extensions_design/extension_infos.html +zend_module_entry rayaop_module_entry = { + STANDARD_MODULE_HEADER, + "rayaop", // 拡張機能の名前 + rayaop_functions, // 拡張機能が提供する関数 + PHP_MINIT(rayaop), // 拡張機能の初期化関数 + PHP_MSHUTDOWN(rayaop), // 拡張機能のシャットダウン関数 + PHP_RINIT(rayaop), // リクエスト開始時の関数 + PHP_RSHUTDOWN(rayaop), // リクエスト終了時の関数 + PHP_MINFO(rayaop), // 拡張機能の情報表示関数 + PHP_RAYAOP_VERSION, // 拡張機能のバージョン + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_RAYAOP +ZEND_GET_MODULE(rayaop) #endif \ No newline at end of file From 2c44bf44dca39ad2a5815e68346dc8e98ba45212 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 11:16:04 +0900 Subject: [PATCH 27/40] Rename class and implement interface in PHP and C The Intercepted class in smoke.php file was renamed to MethodInterceptor and the Ray\Aop\MethodInterceptorInterface was implemented in the class. Also, the MethodInterceptorInterface was defined in the rayaop.c file, using Zend API functions to register the interface and define its abstract method. --- rayaop.c | 18 ++++++++++++++++++ smoke.php | 8 +++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/rayaop.c b/rayaop.c index 8cd0759..feaa284 100644 --- a/rayaop.c +++ b/rayaop.c @@ -201,6 +201,20 @@ PHP_FUNCTION(method_intercept) { RETURN_TRUE; } +// Define the interface +zend_class_entry *ray_aop_method_interceptor_interface_ce; + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ray_aop_method_interceptor_intercept, 0, 3, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) + ZEND_ARG_TYPE_INFO(0, method, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, params, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +static const zend_function_entry ray_aop_method_interceptor_interface_methods[] = { + ZEND_ABSTRACT_ME(Ray_Aop_MethodInterceptorInterface, intercept, arginfo_ray_aop_method_interceptor_intercept) + PHP_FE_END +}; + /** * 拡張機能の初期化関数 * link: https://www.phpinternalsbook.com/php7/extensions_design/hooks.html @@ -215,6 +229,10 @@ PHP_MINIT_FUNCTION(rayaop) php_rayaop_init_globals(&rayaop_globals); #endif + zend_class_entry ce; + INIT_CLASS_ENTRY(ce, "Ray\\Aop\\MethodInterceptorInterface", ray_aop_method_interceptor_interface_methods); + ray_aop_method_interceptor_interface_ce = zend_register_internal_interface(&ce); + original_zend_execute_ex = zend_execute_ex; zend_execute_ex = rayaop_zend_execute_ex; diff --git a/smoke.php b/smoke.php index a9bd42b..d4f5e42 100644 --- a/smoke.php +++ b/smoke.php @@ -1,8 +1,6 @@ Date: Sat, 6 Jul 2024 11:42:50 +0900 Subject: [PATCH 28/40] Add basic tests for RayAOP extension Three new test files have been added to verify the basic functionality of RayAOP extension. A test for successful extension load, basic functionality verification and error handling were implemented. The test execution checks the availability of the extension, the correct functioning of a method interceptor, and the handling of errors while registering interceptors. --- tests/000-rayaop-loaded.phpt | 13 +++++++++++++ tests/001-rayaop-basic.phpt | 31 +++++++++++++++++++++++++++++++ tests/002-rayaop-error.phpt | 24 ++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 tests/000-rayaop-loaded.phpt create mode 100644 tests/001-rayaop-basic.phpt create mode 100644 tests/002-rayaop-error.phpt diff --git a/tests/000-rayaop-loaded.phpt b/tests/000-rayaop-loaded.phpt new file mode 100644 index 0000000..3f2bf4c --- /dev/null +++ b/tests/000-rayaop-loaded.phpt @@ -0,0 +1,13 @@ +--TEST-- +RayAOP extension is loaded +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/tests/001-rayaop-basic.phpt b/tests/001-rayaop-basic.phpt new file mode 100644 index 0000000..48848fa --- /dev/null +++ b/tests/001-rayaop-basic.phpt @@ -0,0 +1,31 @@ +--TEST-- +RayAOP basic functionality +--FILE-- +$method(...$params); + } +} + +// Register the interceptor +$result = method_intercept(TestClass::class, 'testMethod', new TestInterceptor()); +var_dump($result); + +// Call the intercepted method +$test = new TestClass(); +$result = $test->testMethod("Hello"); +var_dump($result); + +?> +--EXPECTF-- +bool(true) +string(28) "Intercepted: Original: Hello" \ No newline at end of file diff --git a/tests/002-rayaop-error.phpt b/tests/002-rayaop-error.phpt new file mode 100644 index 0000000..9a00a9f --- /dev/null +++ b/tests/002-rayaop-error.phpt @@ -0,0 +1,24 @@ +--TEST-- +RayAOP error handling +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) From 7859d29a53c87a0fd25f7d6cac434d2eba7e686e Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 11:43:07 +0900 Subject: [PATCH 29/40] Add RED test for multiple interceptors in RayAOP A new test has been introduced to verify the functionality of using multiple interceptors in RayAOP. It checks if the interceptors are properly chained together and behave as expected during the execution of a method. --- tests/003-rayaop-multiple-interceptors.phpt | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/003-rayaop-multiple-interceptors.phpt diff --git a/tests/003-rayaop-multiple-interceptors.phpt b/tests/003-rayaop-multiple-interceptors.phpt new file mode 100644 index 0000000..d2bf43e --- /dev/null +++ b/tests/003-rayaop-multiple-interceptors.phpt @@ -0,0 +1,37 @@ +--TEST-- +RayAOP multiple interceptors +--SKIPIF-- + +--FILE-- +testMethod("Hello"); +var_dump($result); + +?> +--EXPECT-- +string(47) "Interceptor2: Interceptor1: Original: Hello" From abaacdc127100f5bd9697bcc767835659251a96a Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 13:49:22 +0900 Subject: [PATCH 30/40] Refactor error handling in rayaop.c Removed specific error codes and refactored error handling in rayaop.c. The error handling is now consolidated into the rayaop_handle_error function and hash_update_failed function, which handle memory allocation and hash update failures respectively. This simplifies the code and makes error handling more straightforward. --- php_rayaop.h | 2 +- rayaop.c | 35 +++++++++++++---------------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/php_rayaop.h b/php_rayaop.h index c9aef16..10f4775 100644 --- a/php_rayaop.h +++ b/php_rayaop.h @@ -65,7 +65,7 @@ PHP_MINFO_FUNCTION(rayaop); PHP_FUNCTION(method_intercept); // ユーティリティ関数の宣言 -void rayaop_handle_error(int error_code, const char *message); +void rayaop_handle_error(const char *message); bool rayaop_should_intercept(zend_execute_data *execute_data); char* rayaop_generate_intercept_key(zend_string *class_name, zend_string *method_name, size_t *key_len); intercept_info* rayaop_find_intercept_info(const char *key, size_t key_len); diff --git a/rayaop.c b/rayaop.c index feaa284..46ec1ed 100644 --- a/rayaop.c +++ b/rayaop.c @@ -4,10 +4,6 @@ #include "php_rayaop.h" -// エラー定数の定義 -#define RAYAOP_ERROR_MEMORY_ALLOCATION 1 -#define RAYAOP_ERROR_HASH_UPDATE 2 - // モジュールグローバル変数の宣言 ZEND_DECLARE_MODULE_GLOBALS(rayaop) @@ -37,17 +33,8 @@ ZEND_END_ARG_INFO() // ユーティリティ関数の実装 -void rayaop_handle_error(int error_code, const char *message) { - switch (error_code) { - case RAYAOP_ERROR_MEMORY_ALLOCATION: - php_error_docref(NULL, E_ERROR, "Memory allocation failed: %s", message); - break; - case RAYAOP_ERROR_HASH_UPDATE: - php_error_docref(NULL, E_ERROR, "Failed to update hash table: %s", message); - break; - default: - php_error_docref(NULL, E_ERROR, "Unknown error: %s", message); - } +void rayaop_handle_error(const char *message) { + php_error_docref(NULL, E_ERROR, "Memory error: %s", message); } bool rayaop_should_intercept(zend_execute_data *execute_data) { @@ -156,6 +143,15 @@ static void rayaop_zend_execute_ex(zend_execute_data *execute_data) { efree(key); } +void hash_update_failed(intercept_info *new_info, char *key) { + rayaop_handle_error("Failed to update intercept hash table"); + zend_string_release(new_info->class_name); + zend_string_release(new_info->method_name); + zval_ptr_dtor(&new_info->handler); + efree(new_info); + efree(key); +} + /** * インターセプトメソッドを登録する関数 * link: https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html @@ -175,7 +171,7 @@ PHP_FUNCTION(method_intercept) { intercept_info *new_info = ecalloc(1, sizeof(intercept_info)); if (!new_info) { - rayaop_handle_error(RAYAOP_ERROR_MEMORY_ALLOCATION, "Failed to allocate memory for intercept_info"); + rayaop_handle_error("Failed to allocate memory for intercept_info"); RETURN_FALSE; } @@ -187,12 +183,7 @@ PHP_FUNCTION(method_intercept) { size_t key_len = spprintf(&key, 0, "%s::%s", class_name, method_name); if (zend_hash_str_update_ptr(RAYAOP_G(intercept_ht), key, key_len, new_info) == NULL) { - rayaop_handle_error(RAYAOP_ERROR_HASH_UPDATE, "Failed to update intercept hash table"); - zend_string_release(new_info->class_name); - zend_string_release(new_info->method_name); - zval_ptr_dtor(&new_info->handler); - efree(new_info); - efree(key); + hash_update_failed(new_info, key); RETURN_FALSE; } From 4585a1f871ed910e919ea082a1a6d0ef91d5a2e6 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 14:33:20 +0900 Subject: [PATCH 31/40] Add comments and improve function descriptions in rayaop.c This commit incorporates commented explanations for the functions, variables, parameters, and control flow in the source file rayaop.c. These include descriptions for interceptor functions, error handlers, initialization routines, debug code, and more. The comments are mainly in English, with a few original Japanese comments retained. --- php_rayaop.h | 108 ++++++++--------- rayaop.c | 318 +++++++++++++++++++++++++++++---------------------- 2 files changed, 236 insertions(+), 190 deletions(-) diff --git a/php_rayaop.h b/php_rayaop.h index 10f4775..b9a6175 100644 --- a/php_rayaop.h +++ b/php_rayaop.h @@ -1,48 +1,48 @@ -#ifndef PHP_RAYAOP_H -#define PHP_RAYAOP_H +#ifndef PHP_RAYAOP_H // PHP_RAYAOP_Hが定義されていない場合 +#define PHP_RAYAOP_H // PHP_RAYAOP_Hを定義(ヘッダーガード) -#ifdef HAVE_CONFIG_H -#include "config.h" +#ifdef HAVE_CONFIG_H // HAVE_CONFIG_Hが定義されている場合 +#include "config.h" // config.hをインクルード #endif -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "zend_exceptions.h" -#include "zend_interfaces.h" +#include "php.h" // PHP核のヘッダーファイルをインクルード +#include "php_ini.h" // PHP INI関連のヘッダーファイルをインクルード +#include "ext/standard/info.h" // 標準拡張モジュールの情報関連ヘッダーをインクルード +#include "zend_exceptions.h" // Zend例外処理関連のヘッダーをインクルード +#include "zend_interfaces.h" // Zendインターフェース関連のヘッダーをインクルード -#ifdef ZTS -#include "TSRM.h" +#ifdef ZTS // スレッドセーフモードの場合 +#include "TSRM.h" // Thread Safe Resource Managerをインクルード #endif -#define PHP_RAYAOP_VERSION "1.0.0" -#define RAYAOP_NS "Ray\\Aop\\" +#define PHP_RAYAOP_VERSION "1.0.0" // RayAOP拡張機能のバージョンを定義 +#define RAYAOP_NS "Ray\\Aop\\" // RayAOPの名前空間を定義 -extern zend_module_entry rayaop_module_entry; -#define phpext_rayaop_ptr &rayaop_module_entry +extern zend_module_entry rayaop_module_entry; // rayaopモジュールエントリを外部参照として宣言 +#define phpext_rayaop_ptr &rayaop_module_entry // rayaopモジュールへのポインタを定義 -#ifdef PHP_WIN32 -#define PHP_RAYAOP_API __declspec(dllexport) -#elif defined(__GNUC__) && __GNUC__ >= 4 -#define PHP_RAYAOP_API __attribute__ ((visibility("default"))) -#else -#define PHP_RAYAOP_API +#ifdef PHP_WIN32 // Windows環境の場合 +#define PHP_RAYAOP_API __declspec(dllexport) // DLLエクスポート指定 +#elif defined(__GNUC__) && __GNUC__ >= 4 // GCC 4以上の場合 +#define PHP_RAYAOP_API __attribute__ ((visibility("default"))) // デフォルトの可視性を指定 +#else // その他の環境 +#define PHP_RAYAOP_API // 特に指定なし #endif -#ifdef ZTS -#include "TSRM.h" +#ifdef ZTS // スレッドセーフモードの場合 +#include "TSRM.h" // Thread Safe Resource Managerを再度インクルード(冗長だが安全のため) #endif // デバッグ出力用マクロ -#ifdef RAYAOP_DEBUG -#define RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) -#else -#define RAYAOP_DEBUG_PRINT(fmt, ...) +#ifdef RAYAOP_DEBUG // デバッグモードが有効な場合 +#define RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) // デバッグ出力マクロを定義 +#else // デバッグモードが無効な場合 +#define RAYAOP_DEBUG_PRINT(fmt, ...) // 何もしない #endif // エラーコード -#define RAYAOP_ERROR_MEMORY_ALLOCATION 1 -#define RAYAOP_ERROR_HASH_UPDATE 2 +#define RAYAOP_ERROR_MEMORY_ALLOCATION 1 // メモリ割り当てエラーのコード +#define RAYAOP_ERROR_HASH_UPDATE 2 // ハッシュ更新エラーのコード /** * インターセプト情報を保持する構造体 @@ -56,36 +56,36 @@ typedef struct _intercept_info { } intercept_info; // 関数宣言 -PHP_MINIT_FUNCTION(rayaop); -PHP_MSHUTDOWN_FUNCTION(rayaop); -PHP_RINIT_FUNCTION(rayaop); -PHP_RSHUTDOWN_FUNCTION(rayaop); -PHP_MINFO_FUNCTION(rayaop); +PHP_MINIT_FUNCTION(rayaop); // モジュール初期化関数 +PHP_MSHUTDOWN_FUNCTION(rayaop); // モジュールシャットダウン関数 +PHP_RINIT_FUNCTION(rayaop); // リクエスト初期化関数 +PHP_RSHUTDOWN_FUNCTION(rayaop); // リクエストシャットダウン関数 +PHP_MINFO_FUNCTION(rayaop); // モジュール情報関数 -PHP_FUNCTION(method_intercept); +PHP_FUNCTION(method_intercept); // メソッドインターセプト関数 // ユーティリティ関数の宣言 -void rayaop_handle_error(const char *message); -bool rayaop_should_intercept(zend_execute_data *execute_data); -char* rayaop_generate_intercept_key(zend_string *class_name, zend_string *method_name, size_t *key_len); -intercept_info* rayaop_find_intercept_info(const char *key, size_t key_len); -void rayaop_execute_intercept(zend_execute_data *execute_data, intercept_info *info); -void rayaop_free_intercept_info(zval *zv); - -#ifdef RAYAOP_DEBUG -void rayaop_debug_print_zval(zval *value); -void rayaop_dump_intercept_info(void); +void rayaop_handle_error(const char *message); // エラーハンドリング関数 +bool rayaop_should_intercept(zend_execute_data *execute_data); // インターセプトの必要性を判断する関数 +char* rayaop_generate_intercept_key(zend_string *class_name, zend_string *method_name, size_t *key_len); // インターセプトキーを生成する関数 +intercept_info* rayaop_find_intercept_info(const char *key, size_t key_len); // インターセプト情報を検索する関数 +void rayaop_execute_intercept(zend_execute_data *execute_data, intercept_info *info); // インターセプトを実行する関数 +void rayaop_free_intercept_info(zval *zv); // インターセプト情報を解放する関数 + +#ifdef RAYAOP_DEBUG // デバッグモードが有効な場合 +void rayaop_debug_print_zval(zval *value); // zval値をデバッグ出力する関数 +void rayaop_dump_intercept_info(void); // インターセプト情報をダンプする関数 #endif -ZEND_BEGIN_MODULE_GLOBALS(rayaop) - HashTable *intercept_ht; - zend_bool is_intercepting; -ZEND_END_MODULE_GLOBALS(rayaop) +ZEND_BEGIN_MODULE_GLOBALS(rayaop) // rayaopモジュールのグローバル変数の開始 + HashTable *intercept_ht; // インターセプトハッシュテーブル + zend_bool is_intercepting; // インターセプト中フラグ +ZEND_END_MODULE_GLOBALS(rayaop) // rayaopモジュールのグローバル変数の終了 -#ifdef ZTS -#define RAYAOP_G(v) TSRMG(rayaop_globals_id, zend_rayaop_globals *, v) -#else -#define RAYAOP_G(v) (rayaop_globals.v) +#ifdef ZTS // スレッドセーフモードの場合 +#define RAYAOP_G(v) TSRMG(rayaop_globals_id, zend_rayaop_globals *, v) // グローバル変数アクセスマクロ(スレッドセーフ版) +#else // 非スレッドセーフモードの場合 +#define RAYAOP_G(v) (rayaop_globals.v) // グローバル変数アクセスマクロ(非スレッドセーフ版) #endif -#endif /* PHP_RAYAOP_H */ \ No newline at end of file +#endif /* PHP_RAYAOP_H */ // ヘッダーガードの終了 \ No newline at end of file diff --git a/rayaop.c b/rayaop.c index 46ec1ed..24b9656 100644 --- a/rayaop.c +++ b/rayaop.c @@ -1,27 +1,30 @@ #ifdef HAVE_CONFIG_H -#include "config.h" +#include "config.h" // 設定ファイルをインクルード #endif -#include "php_rayaop.h" +#include "php_rayaop.h" // RayAOP拡張機能のヘッダーファイルをインクルード // モジュールグローバル変数の宣言 ZEND_DECLARE_MODULE_GLOBALS(rayaop) -// 静的変数の宣言 +// 静的変数の宣言:元のzend_execute_ex関数へのポインタ static void (*original_zend_execute_ex)(zend_execute_data *execute_data); -// グローバル初期化関数 +/** + * グローバル初期化関数 + * @param zend_rayaop_globals *rayaop_globals グローバル変数へのポインタ + */ static void php_rayaop_init_globals(zend_rayaop_globals *rayaop_globals) { - rayaop_globals->intercept_ht = NULL; - rayaop_globals->is_intercepting = 0; + rayaop_globals->intercept_ht = NULL; // インターセプトハッシュテーブルを初期化 + rayaop_globals->is_intercepting = 0; // インターセプトフラグを初期化 } // デバッグ出力用マクロ #ifdef RAYAOP_DEBUG -#define RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) +#define RAYAOP_DEBUG_PRINT(fmt, ...) php_printf("RAYAOP DEBUG: " fmt "\n", ##__VA_ARGS__) // デバッグ情報を出力 #else -#define RAYAOP_DEBUG_PRINT(fmt, ...) +#define RAYAOP_DEBUG_PRINT(fmt, ...) // デバッグモードでない場合は何もしない #endif // method_intercept 関数の引数情報 @@ -33,131 +36,168 @@ ZEND_END_ARG_INFO() // ユーティリティ関数の実装 +/** + * エラーハンドリング関数 + * @param const char *message エラーメッセージ + */ void rayaop_handle_error(const char *message) { - php_error_docref(NULL, E_ERROR, "Memory error: %s", message); + php_error_docref(NULL, E_ERROR, "Memory error: %s", message); // エラーメッセージを出力 } +/** + * インターセプトが必要かどうかを判断する関数 + * @param zend_execute_data *execute_data 実行データ + * @return bool インターセプトが必要な場合はtrue + */ bool rayaop_should_intercept(zend_execute_data *execute_data) { - return execute_data->func->common.scope && - execute_data->func->common.function_name && - !RAYAOP_G(is_intercepting); + return execute_data->func->common.scope && // スコープが存在し + execute_data->func->common.function_name && // 関数名が存在し + !RAYAOP_G(is_intercepting); // 現在インターセプト中でない } +/** + * インターセプトキーを生成する関数 + * @param zend_string *class_name クラス名 + * @param zend_string *method_name メソッド名 + * @param size_t *key_len キーの長さを格納するポインタ + * @return char* 生成されたキー + */ char* rayaop_generate_intercept_key(zend_string *class_name, zend_string *method_name, size_t *key_len) { char *key = NULL; - *key_len = spprintf(&key, 0, "%s::%s", ZSTR_VAL(class_name), ZSTR_VAL(method_name)); - RAYAOP_DEBUG_PRINT("Generated key: %s", key); + *key_len = spprintf(&key, 0, "%s::%s", ZSTR_VAL(class_name), ZSTR_VAL(method_name)); // クラス名::メソッド名 の形式でキーを生成 + RAYAOP_DEBUG_PRINT("Generated key: %s", key); // 生成されたキーをデバッグ出力 return key; } +/** + * インターセプト情報を検索する関数 + * @param const char *key 検索キー + * @param size_t key_len キーの長さ + * @return intercept_info* 見つかったインターセプト情報、見つからない場合はNULL + */ intercept_info* rayaop_find_intercept_info(const char *key, size_t key_len) { - return zend_hash_str_find_ptr(RAYAOP_G(intercept_ht), key, key_len); + return zend_hash_str_find_ptr(RAYAOP_G(intercept_ht), key, key_len); // ハッシュテーブルからインターセプト情報を検索 } +/** + * インターセプトを実行する関数 + * @param zend_execute_data *execute_data 実行データ + * @param intercept_info *info インターセプト情報 + */ void rayaop_execute_intercept(zend_execute_data *execute_data, intercept_info *info) { - if (Z_TYPE(info->handler) != IS_OBJECT) { - return; + if (Z_TYPE(info->handler) != IS_OBJECT) { // ハンドラーがオブジェクトでない場合 + return; // 処理を中断 } - if (execute_data->This.value.obj == NULL) { - RAYAOP_DEBUG_PRINT("Object is NULL, calling original function"); - original_zend_execute_ex(execute_data); - return; + if (execute_data->This.value.obj == NULL) { // 対象オブジェクトがNULLの場合 + RAYAOP_DEBUG_PRINT("Object is NULL, calling original function"); // デバッグ情報を出力 + original_zend_execute_ex(execute_data); // 元の関数を実行 + return; // 処理を中断 } - zval retval, params[3]; - ZVAL_OBJ(¶ms[0], execute_data->This.value.obj); - ZVAL_STR(¶ms[1], info->method_name); + zval retval, params[3]; // 戻り値と引数を格納する変数を準備 + ZVAL_OBJ(¶ms[0], execute_data->This.value.obj); // 第1引数:対象オブジェクト + ZVAL_STR(¶ms[1], info->method_name); // 第2引数:メソッド名 - array_init(¶ms[2]); - uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data); - zval *args = ZEND_CALL_ARG(execute_data, 1); - for (uint32_t i = 0; i < arg_count; i++) { + array_init(¶ms[2]); // 第3引数:メソッドの引数を格納する配列を初期化 + uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data); // 引数の数を取得 + zval *args = ZEND_CALL_ARG(execute_data, 1); // 引数の配列を取得 + for (uint32_t i = 0; i < arg_count; i++) { // 各引数に対して zval *arg = &args[i]; - Z_TRY_ADDREF_P(arg); - add_next_index_zval(¶ms[2], arg); + Z_TRY_ADDREF_P(arg); // 参照カウントを増やす + add_next_index_zval(¶ms[2], arg); // 配列に追加 } - RAYAOP_G(is_intercepting) = 1; + RAYAOP_G(is_intercepting) = 1; // インターセプトフラグを設定 zval func_name; - ZVAL_STRING(&func_name, "intercept"); + ZVAL_STRING(&func_name, "intercept"); // 呼び出す関数名を設定 - ZVAL_UNDEF(&retval); - if (call_user_function(NULL, &info->handler, &func_name, &retval, 3, params) == SUCCESS) { - if (!Z_ISUNDEF(retval)) { - ZVAL_COPY(execute_data->return_value, &retval); + ZVAL_UNDEF(&retval); // 戻り値を未定義に初期化 + if (call_user_function(NULL, &info->handler, &func_name, &retval, 3, params) == SUCCESS) { // インターセプト関数を呼び出し + if (!Z_ISUNDEF(retval)) { // 戻り値が定義されている場合 + ZVAL_COPY(execute_data->return_value, &retval); // 戻り値をコピー } - } else { - php_error_docref(NULL, E_WARNING, "Interception failed for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name)); + } else { // インターセプトに失敗した場合 + php_error_docref(NULL, E_WARNING, "Interception failed for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name)); // 警告を出力 } - zval_ptr_dtor(&retval); - zval_ptr_dtor(&func_name); - zval_ptr_dtor(¶ms[1]); - zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(&retval); // 戻り値のメモリを解放 + zval_ptr_dtor(&func_name); // 関数名のメモリを解放 + zval_ptr_dtor(¶ms[1]); // メソッド名のメモリを解放 + zval_ptr_dtor(¶ms[2]); // 引数配列のメモリを解放 - RAYAOP_G(is_intercepting) = 0; + RAYAOP_G(is_intercepting) = 0; // インターセプトフラグをリセット } /** * インターセプト情報を解放する関数 - * link: https://www.phpinternalsbook.com/php7/internal_types/strings/zend_strings.html + * @param zval *zv 解放するインターセプト情報を含むzval */ void rayaop_free_intercept_info(zval *zv) { - intercept_info *info = Z_PTR_P(zv); - if (info) { - RAYAOP_DEBUG_PRINT("Freeing intercept info for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name)); - zend_string_release(info->class_name); - zend_string_release(info->method_name); - zval_ptr_dtor(&info->handler); - efree(info); + intercept_info *info = Z_PTR_P(zv); // zvalからインターセプト情報ポインタを取得 + if (info) { // インターセプト情報が存在する場合 + RAYAOP_DEBUG_PRINT("Freeing intercept info for %s::%s", ZSTR_VAL(info->class_name), ZSTR_VAL(info->method_name)); // デバッグ情報を出力 + zend_string_release(info->class_name); // クラス名のメモリを解放 + zend_string_release(info->method_name); // メソッド名のメモリを解放 + zval_ptr_dtor(&info->handler); // ハンドラーのメモリを解放 + efree(info); // インターセプト情報構造体のメモリを解放 } } -// カスタム zend_execute_ex 関数 +/** + * カスタム zend_execute_ex 関数 + * @param zend_execute_data *execute_data 実行データ + */ static void rayaop_zend_execute_ex(zend_execute_data *execute_data) { - RAYAOP_DEBUG_PRINT("rayaop_zend_execute_ex called"); + RAYAOP_DEBUG_PRINT("rayaop_zend_execute_ex called"); // デバッグ情報を出力 - if (!rayaop_should_intercept(execute_data)) { - original_zend_execute_ex(execute_data); - return; + if (!rayaop_should_intercept(execute_data)) { // インターセプトが不要な場合 + original_zend_execute_ex(execute_data); // 元の実行関数を呼び出し + return; // 処理を終了 } - zend_function *current_function = execute_data->func; - zend_string *class_name = current_function->common.scope->name; - zend_string *method_name = current_function->common.function_name; + zend_function *current_function = execute_data->func; // 現在の関数情報を取得 + zend_string *class_name = current_function->common.scope->name; // クラス名を取得 + zend_string *method_name = current_function->common.function_name; // メソッド名を取得 size_t key_len; - char *key = rayaop_generate_intercept_key(class_name, method_name, &key_len); + char *key = rayaop_generate_intercept_key(class_name, method_name, &key_len); // インターセプトキーを生成 - intercept_info *info = rayaop_find_intercept_info(key, key_len); + intercept_info *info = rayaop_find_intercept_info(key, key_len); // インターセプト情報を検索 - if (info) { - RAYAOP_DEBUG_PRINT("Found intercept info for key: %s", key); - rayaop_execute_intercept(execute_data, info); - } else { - RAYAOP_DEBUG_PRINT("No intercept info found for key: %s", key); - original_zend_execute_ex(execute_data); + if (info) { // インターセプト情報が見つかった場合 + RAYAOP_DEBUG_PRINT("Found intercept info for key: %s", key); // デバッグ情報を出力 + rayaop_execute_intercept(execute_data, info); // インターセプトを実行 + } else { // インターセプト情報が見つからなかった場合 + RAYAOP_DEBUG_PRINT("No intercept info found for key: %s", key); // デバッグ情報を出力 + original_zend_execute_ex(execute_data); // 元の実行関数を呼び出し } - efree(key); + efree(key); // キーのメモリを解放 } +/** + * ハッシュテーブル更新失敗時の処理 + * @param intercept_info *new_info 新しいインターセプト情報 + * @param char *key 更新に使用されたキー + */ void hash_update_failed(intercept_info *new_info, char *key) { - rayaop_handle_error("Failed to update intercept hash table"); - zend_string_release(new_info->class_name); - zend_string_release(new_info->method_name); - zval_ptr_dtor(&new_info->handler); - efree(new_info); - efree(key); + rayaop_handle_error("Failed to update intercept hash table"); // エラーメッセージを出力 + zend_string_release(new_info->class_name); // クラス名のメモリを解放 + zend_string_release(new_info->method_name); // メソッド名のメモリを解放 + zval_ptr_dtor(&new_info->handler); // ハンドラーのメモリを解放 + efree(new_info); // インターセプト情報構造体のメモリを解放 + efree(key); // キーのメモリを解放 } /** * インターセプトメソッドを登録する関数 * link: https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html + * + * @param INTERNAL_FUNCTION_PARAMETERS 内部関数パラメータ */ PHP_FUNCTION(method_intercept) { - RAYAOP_DEBUG_PRINT("method_intercept called"); + RAYAOP_DEBUG_PRINT("method_intercept called"); // デバッグ情報を出力 char *class_name, *method_name; // クラス名とメソッド名のポインタ size_t class_name_len, method_name_len; // クラス名とメソッド名の長さ @@ -169,119 +209,124 @@ PHP_FUNCTION(method_intercept) { Z_PARAM_OBJECT(intercepted) // インターセプトハンドラーのパラメータを解析 ZEND_PARSE_PARAMETERS_END(); - intercept_info *new_info = ecalloc(1, sizeof(intercept_info)); - if (!new_info) { - rayaop_handle_error("Failed to allocate memory for intercept_info"); - RETURN_FALSE; + intercept_info *new_info = ecalloc(1, sizeof(intercept_info)); // 新しいインターセプト情報のメモリを確保 + if (!new_info) { // メモリ確保に失敗した場合 + rayaop_handle_error("Failed to allocate memory for intercept_info"); // エラーメッセージを出力 + RETURN_FALSE; // falseを返して終了 } - new_info->class_name = zend_string_init(class_name, class_name_len, 0); - new_info->method_name = zend_string_init(method_name, method_name_len, 0); - ZVAL_COPY(&new_info->handler, intercepted); +new_info->class_name = zend_string_init(class_name, class_name_len, 0); // クラス名を初期化 + new_info->method_name = zend_string_init(method_name, method_name_len, 0); // メソッド名を初期化 + ZVAL_COPY(&new_info->handler, intercepted); // インターセプトハンドラーをコピー char *key = NULL; - size_t key_len = spprintf(&key, 0, "%s::%s", class_name, method_name); + size_t key_len = spprintf(&key, 0, "%s::%s", class_name, method_name); // インターセプトキーを生成 - if (zend_hash_str_update_ptr(RAYAOP_G(intercept_ht), key, key_len, new_info) == NULL) { - hash_update_failed(new_info, key); - RETURN_FALSE; + if (zend_hash_str_update_ptr(RAYAOP_G(intercept_ht), key, key_len, new_info) == NULL) { // ハッシュテーブルに追加 + hash_update_failed(new_info, key); // 追加に失敗した場合、エラー処理を実行 + RETURN_FALSE; // falseを返して終了 } - efree(key); - RAYAOP_DEBUG_PRINT("Successfully registered intercept info"); - RETURN_TRUE; + efree(key); // キーのメモリを解放 + RAYAOP_DEBUG_PRINT("Successfully registered intercept info"); // デバッグ情報を出力 + RETURN_TRUE; // trueを返して終了 } -// Define the interface +// インターフェースの定義 zend_class_entry *ray_aop_method_interceptor_interface_ce; ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ray_aop_method_interceptor_intercept, 0, 3, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) - ZEND_ARG_TYPE_INFO(0, method, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, params, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) // 第1引数:オブジェクト + ZEND_ARG_TYPE_INFO(0, method, IS_STRING, 0) // 第2引数:メソッド名 + ZEND_ARG_TYPE_INFO(0, params, IS_ARRAY, 0) // 第3引数:パラメータ配列 ZEND_END_ARG_INFO() static const zend_function_entry ray_aop_method_interceptor_interface_methods[] = { - ZEND_ABSTRACT_ME(Ray_Aop_MethodInterceptorInterface, intercept, arginfo_ray_aop_method_interceptor_intercept) - PHP_FE_END + ZEND_ABSTRACT_ME(Ray_Aop_MethodInterceptorInterface, intercept, arginfo_ray_aop_method_interceptor_intercept) // interceptメソッドを定義 + PHP_FE_END // 関数エントリの終了 }; /** * 拡張機能の初期化関数 - * link: https://www.phpinternalsbook.com/php7/extensions_design/hooks.html + * @param INIT_FUNC_ARGS 初期化関数の引数 + * @return int 初期化の成功・失敗 */ PHP_MINIT_FUNCTION(rayaop) { - RAYAOP_DEBUG_PRINT("PHP_MINIT_FUNCTION called"); + RAYAOP_DEBUG_PRINT("PHP_MINIT_FUNCTION called"); // デバッグ情報を出力 #ifdef ZTS - ts_allocate_id(&rayaop_globals_id, sizeof(zend_rayaop_globals), (ts_allocate_ctor) php_rayaop_init_globals, NULL); + ts_allocate_id(&rayaop_globals_id, sizeof(zend_rayaop_globals), (ts_allocate_ctor) php_rayaop_init_globals, NULL); // スレッドセーフモードでのグローバル変数の初期化 #else - php_rayaop_init_globals(&rayaop_globals); + php_rayaop_init_globals(&rayaop_globals); // 非スレッドセーフモードでのグローバル変数の初期化 #endif zend_class_entry ce; - INIT_CLASS_ENTRY(ce, "Ray\\Aop\\MethodInterceptorInterface", ray_aop_method_interceptor_interface_methods); - ray_aop_method_interceptor_interface_ce = zend_register_internal_interface(&ce); + INIT_CLASS_ENTRY(ce, "Ray\\Aop\\MethodInterceptorInterface", ray_aop_method_interceptor_interface_methods); // クラスエントリを初期化 + ray_aop_method_interceptor_interface_ce = zend_register_internal_interface(&ce); // インターフェースを登録 - original_zend_execute_ex = zend_execute_ex; - zend_execute_ex = rayaop_zend_execute_ex; + original_zend_execute_ex = zend_execute_ex; // 元のzend_execute_ex関数を保存 + zend_execute_ex = rayaop_zend_execute_ex; // カスタムのzend_execute_ex関数を設定 - RAYAOP_DEBUG_PRINT("RayAOP extension initialized"); - return SUCCESS; + RAYAOP_DEBUG_PRINT("RayAOP extension initialized"); // デバッグ情報を出力 + return SUCCESS; // 成功を返す } /** * 拡張機能のシャットダウン関数 - * link: https://www.phpinternalsbook.com/php7/extensions_design/hooks.html + * @param SHUTDOWN_FUNC_ARGS シャットダウン関数の引数 + * @return int シャットダウンの成功・失敗 */ PHP_MSHUTDOWN_FUNCTION(rayaop) { - RAYAOP_DEBUG_PRINT("RayAOP PHP_MSHUTDOWN_FUNCTION called"); + RAYAOP_DEBUG_PRINT("RayAOP PHP_MSHUTDOWN_FUNCTION called"); // デバッグ情報を出力 zend_execute_ex = original_zend_execute_ex; // 元のzend_execute_ex関数を復元 - original_zend_execute_ex = NULL; + original_zend_execute_ex = NULL; // 保存していたポインタをクリア - RAYAOP_DEBUG_PRINT("RayAOP PHP_MSHUTDOWN_FUNCTION shut down"); + RAYAOP_DEBUG_PRINT("RayAOP PHP_MSHUTDOWN_FUNCTION shut down"); // デバッグ情報を出力 return SUCCESS; // シャットダウン成功 } /** * リクエスト開始時の初期化関数 + * @param INIT_FUNC_ARGS 初期化関数の引数 + * @return int 初期化の成功・失敗 */ PHP_RINIT_FUNCTION(rayaop) { - RAYAOP_DEBUG_PRINT("PHP_RINIT_FUNCTION called"); + RAYAOP_DEBUG_PRINT("PHP_RINIT_FUNCTION called"); // デバッグ情報を出力 - if (RAYAOP_G(intercept_ht) == NULL) { - ALLOC_HASHTABLE(RAYAOP_G(intercept_ht)); - zend_hash_init(RAYAOP_G(intercept_ht), 8, NULL, rayaop_free_intercept_info, 0); + if (RAYAOP_G(intercept_ht) == NULL) { // インターセプトハッシュテーブルが未初期化の場合 + ALLOC_HASHTABLE(RAYAOP_G(intercept_ht)); // ハッシュテーブルのメモリを確保 + zend_hash_init(RAYAOP_G(intercept_ht), 8, NULL, rayaop_free_intercept_info, 0); // ハッシュテーブルを初期化 } - RAYAOP_G(is_intercepting) = 0; + RAYAOP_G(is_intercepting) = 0; // インターセプトフラグを初期化 - return SUCCESS; + return SUCCESS; // 成功を返す } /** - * 拡張機能のリクエストシャットダウン関数 - * link: https://www.phpinternalsbook.com/php7/extensions_design/hooks.html + * リクエスト終了時のシャットダウン関数 + * @param SHUTDOWN_FUNC_ARGS シャットダウン関数の引数 + * @return int シャットダウンの成功・失敗 */ PHP_RSHUTDOWN_FUNCTION(rayaop) { - RAYAOP_DEBUG_PRINT("RayAOP PHP_RSHUTDOWN_FUNCTION called"); - if (RAYAOP_G(intercept_ht)) { + RAYAOP_DEBUG_PRINT("RayAOP PHP_RSHUTDOWN_FUNCTION called"); // デバッグ情報を出力 + if (RAYAOP_G(intercept_ht)) { // インターセプトハッシュテーブルが存在する場合 zend_hash_destroy(RAYAOP_G(intercept_ht)); // ハッシュテーブルを破棄 FREE_HASHTABLE(RAYAOP_G(intercept_ht)); // ハッシュテーブルのメモリを解放 RAYAOP_G(intercept_ht) = NULL; // ハッシュテーブルポインタをNULLに設定 } - RAYAOP_DEBUG_PRINT("RayAOP PHP_RSHUTDOWN_FUNCTION shut down"); + RAYAOP_DEBUG_PRINT("RayAOP PHP_RSHUTDOWN_FUNCTION shut down"); // デバッグ情報を出力 return SUCCESS; // シャットダウン成功 } /** * 拡張機能の情報表示関数 - * link: https://www.phpinternalsbook.com/php7/extensions_design/extension_infos.html + * @param ZEND_MODULE_INFO_FUNC_ARGS 情報表示関数の引数 */ PHP_MINFO_FUNCTION(rayaop) { @@ -291,37 +336,38 @@ PHP_MINFO_FUNCTION(rayaop) php_info_print_table_end(); // 情報テーブルの終了 } -// 例: インターセプト情報をダンプする関数 +// デバッグ用:インターセプト情報をダンプする関数 #ifdef RAYAOP_DEBUG +/** + * インターセプト情報をダンプする関数(デバッグ用) + */ static void rayaop_dump_intercept_info(void) { - RAYAOP_DEBUG_PRINT("Dumping intercept information:"); - if (RAYAOP_G(intercept_ht)) { + RAYAOP_DEBUG_PRINT("Dumping intercept information:"); // デバッグ情報を出力 + if (RAYAOP_G(intercept_ht)) { // インターセプトハッシュテーブルが存在する場合 zend_string *key; intercept_info *info; - ZEND_HASH_FOREACH_STR_KEY_PTR(RAYAOP_G(intercept_ht), key, info) { - if (key && info) { - RAYAOP_DEBUG_PRINT("Key: %s", ZSTR_VAL(key)); - RAYAOP_DEBUG_PRINT(" Class: %s", ZSTR_VAL(info->class_name)); - RAYAOP_DEBUG_PRINT(" Method: %s", ZSTR_VAL(info->method_name)); - RAYAOP_DEBUG_PRINT(" Handler type: %d", Z_TYPE(info->handler)); + ZEND_HASH_FOREACH_STR_KEY_PTR(RAYAOP_G(intercept_ht), key, info) { // ハッシュテーブルの各要素に対して + if (key && info) { // キーと情報が存在する場合 + RAYAOP_DEBUG_PRINT("Key: %s", ZSTR_VAL(key)); // キーを出力 + RAYAOP_DEBUG_PRINT(" Class: %s", ZSTR_VAL(info->class_name)); // クラス名を出力 + RAYAOP_DEBUG_PRINT(" Method: %s", ZSTR_VAL(info->method_name)); // メソッド名を出力 + RAYAOP_DEBUG_PRINT(" Handler type: %d", Z_TYPE(info->handler)); // ハンドラーの型を出力 } } ZEND_HASH_FOREACH_END(); - } else { - RAYAOP_DEBUG_PRINT("Intercept hash table is not initialized"); + } else { // インターセプトハッシュテーブルが存在しない場合 + RAYAOP_DEBUG_PRINT("Intercept hash table is not initialized"); // 初期化されていないことを出力 } } #endif // 拡張機能が提供する関数の定義 -// https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html static const zend_function_entry rayaop_functions[] = { PHP_FE(method_intercept, arginfo_method_intercept) // method_intercept関数の登録 PHP_FE_END // 関数エントリの終了 }; // 拡張機能のモジュールエントリ -// link https://www.phpinternalsbook.com/php7/extensions_design/extension_infos.html zend_module_entry rayaop_module_entry = { STANDARD_MODULE_HEADER, "rayaop", // 拡張機能の名前 @@ -336,5 +382,5 @@ zend_module_entry rayaop_module_entry = { }; #ifdef COMPILE_DL_RAYAOP -ZEND_GET_MODULE(rayaop) -#endif \ No newline at end of file +ZEND_GET_MODULE(rayaop) // 動的ロード時のモジュール取得関数 +#endif From f0c96bd16d17c80fcf5140cfe509bfdab7bc9a54 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 14:38:23 +0900 Subject: [PATCH 32/40] Update and translate Ray Aop PECL extension documentation The documentation files for the Ray Aop PECL extension have been updated to provide more accurate and detailed information. Multiple changes include: translation from Japanese to English for better accessibility, restructuring and modification of some sections for clarity, and addition of new sections to ensure comprehensive understanding of the extension's use and features. --- docs/policy.md | 58 +++++++++++----------- docs/review.md | 52 -------------------- docs/spec.md | 128 +++++++++++++++++++++++++------------------------ 3 files changed, 95 insertions(+), 143 deletions(-) delete mode 100644 docs/review.md diff --git a/docs/policy.md b/docs/policy.md index 7ec2690..734b485 100644 --- a/docs/policy.md +++ b/docs/policy.md @@ -1,50 +1,50 @@ -# Ray.Aop PECL拡張 実装方針 +# Ray.Aop PECL Extension Implementation Policy -## 1. インターセプション機能の実装 +## 1. Implementation of Interception Functionality -- Zend Engineのフックを利用して、指定されたクラスとメソッドの呼び出しをインターセプトする機能を実装します。 -- オリジナルのメソッド呼び出しをラップし、インターセプトハンドラーを呼び出すメカニズムを構築します。 +- Utilize Zend Engine hooks to implement functionality that intercepts calls to specified classes and methods. +- Construct a mechanism that wraps original method calls and invokes intercept handlers. -## 2. MethodInterceptorInterfaceの実装 +## 2. Implementation of MethodInterceptorInterface -- PHPユーザーランドで定義されるMethodInterceptorInterfaceをC言語レベルで処理できるようにします。 -- interceptメソッドの呼び出しを適切に処理し、ユーザー定義のインターセプトロジックを実行できるようにします。 +- Enable C-level processing of the MethodInterceptorInterface defined in PHP userland. +- Properly handle the invocation of the intercept method, allowing execution of user-defined interception logic. -## 3. method_intercept関数の実装 +## 3. Implementation of method_intercept Function -- クラス名、メソッド名、MethodInterceptorInterfaceオブジェクトを受け取り、インターセプション情報を内部で管理する仕組みを作ります。 -- 登録されたインターセプション情報を効率的に保存し、高速にアクセスできるデータ構造を設計します。 +- Create a mechanism to internally manage interception information by receiving class name, method name, and MethodInterceptorInterface object. +- Design an efficient data structure to store and quickly access registered interception information. -## 4. パフォーマンスの最適化 +## 4. Performance Optimization -- インターセプション処理によるオーバーヘッドを最小限に抑えるため、効率的なコード生成やキャッシュ機構を検討します。 -- 不要な場合にはインターセプション処理をスキップするような最適化を行います。 +- Consider efficient code generation and caching mechanisms to minimize overhead caused by interception processing. +- Implement optimizations to skip interception processing when unnecessary. -## 5. PHP8互換性 +## 5. PHP 8 Compatibility -- 当初はPHP8のみをサポートし、PHP7との互換性は考慮しません。 -- 将来的なPHP7サポートの可能性を考慮し、拡張性のある設計を心がけます。 +- Initially support only PHP 8, without considering compatibility with PHP 7. +- Keep in mind extensible design for potential future PHP 7 support. -## 6. メモリ管理 +## 6. Memory Management -- インターセプション情報やオブジェクトの参照カウントを適切に管理し、メモリリークを防ぎます。 +- Properly manage reference counts of interception information and objects to prevent memory leaks. -## 7. エラーハンドリング +## 7. Error Handling -- 無効な引数や実行時エラーに対して適切に対応し、PHPのエラーハンドリング機構と統合します。 +- Appropriately handle invalid arguments and runtime errors, integrating with PHP's error handling mechanism. -## 8. 拡張性 +## 8. Extensibility -- 将来的な機能拡張や変更に対応できるよう、モジュール化された設計を心がけます。 +- Aim for a modularized design to accommodate future feature extensions and changes. -## 9. ユーザーランドでのマッチング +## 9. Userland Matching -- マッチング処理はユーザーランドで行われることを前提とします。 -- PECL拡張はインターセプターを直接呼び出すのではなく、インターセプトハンドラーを呼び出します。 +- Assume that matching processes will be performed in userland. +- The PECL extension will call intercept handlers rather than directly calling interceptors. -## 10. インターセプトハンドラーの呼び出し +## 10. Invocation of Intercept Handlers -- インターセプションが発生した際、登録されたインターセプトハンドラーを呼び出します。 -- インターセプトハンドラーがインターセプターの呼び出しを制御し、Ray.Aop以外のインターセプターとの連携を可能にします。 +- Call registered intercept handlers when interception occurs. +- Allow intercept handlers to control the invocation of interceptors, enabling integration with interceptors other than Ray.Aop. -これらの方針に基づいて実装を進めることで、効率的で信頼性の高いRay.Aop PECL拡張を開発します。 \ No newline at end of file +By proceeding with implementation based on these policies, we will develop an efficient and reliable Ray.Aop PECL extension. \ No newline at end of file diff --git a/docs/review.md b/docs/review.md deleted file mode 100644 index 7c224a0..0000000 --- a/docs/review.md +++ /dev/null @@ -1,52 +0,0 @@ -# Ray.Aop PECL拡張 仕様書とポリシーのレビュー - -## 評価対象 -- spec.md (仕様書) -- policy.md (実装方針) - -## 評価基準 - -1. 一貫性 -2. 明確性 -3. 詳細度 -4. 網羅性 -5. 将来性 - -## 評価結果 - -### 1. 一貫性 -両文書は概念と用語使用において一貫性があり、「インターセプトハンドラー」という新しい用語を適切に反映しています。 - -### 2. 明確性 -仕様と実装方針が明確に分離されており、それぞれの役割が明確です。 - -### 3. 詳細度 -- spec.md: 具体的なインターフェース、メソッド、使用例を提供しており、開発者が拡張を使用する際の指針となります。 -- policy.md: 実装に関する高レベルの方針を示しており、開発チームの指針となります。 - -### 4. 網羅性 -主要な機能、インターフェース、使用方法がカバーされています。 - -### 5. 将来性 -PHP8のサポートや将来的な拡張性について言及されており、長期的な視点が含まれています。 - -## 現段階での評価 - -### 必要十分性 -現段階では、これらの文書は開発を開始するための必要十分な情報を提供していると考えられます。 - -### 改善の余地 -以下の点について、より詳細な情報を追加することで、文書の完成度をさらに高めることができます: - -- エラーハンドリングの詳細 -- パフォーマンス最適化の具体的な戦略 -- テスト戦略 -- バージョニング方針 - -### 次のステップ -- 具体的な実装計画の作成 -- プロトタイプの開発 -- コードレビュープロセスの確立 - -## 結論 -現段階では、spec.mdとpolicy.mdは開発を開始するための十分な基盤を提供していると評価できます。ただし、開発が進むにつれて、より詳細な情報や新たな要件が明らかになる可能性があるため、これらの文書は living documents として、継続的に更新・改善していくことが重要です。 \ No newline at end of file diff --git a/docs/spec.md b/docs/spec.md index 7e58059..5df06d2 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -1,91 +1,95 @@ -### Ray.Aop PECL拡張仕様書 +### Ray.Aop PECL Extension Specification -#### 概要 -Ray.Aopは、PHPにアスペクト指向プログラミング(AOP)の機能を提供するPECL拡張です。本仕様書では、Ray.Aop拡張の機能、使用方法、インターフェイスについて説明します。 +#### Overview +Ray.Aop is a PECL extension that provides Aspect-Oriented Programming (AOP) capabilities to PHP. This specification outlines the features, usage, and interfaces of the Ray.Aop extension. -#### 機能 -1. **インターセプション機能**: - 指定されたクラスとメソッドに対して実行時に指定した`intercept`メソッドがコールされます。 +#### Features +1. **Interception Mechanism**: + The `intercept` method of the specified intercept handler is called at runtime for designated classes and methods. -2. **インターセプトハンドラー**: - インターセプトハンドラーがインターセプターを呼び出します。ここをユーザーが作成することでRay.Aop以外のインターセプターとの連携が可能です。 +2. **Intercept Handlers**: + Intercept handlers are used to insert custom logic before and after method execution. -#### インターフェイスとクラス +#### Interface -##### InterceptHandlerInterface +##### MethodInterceptorInterface -Ray.Aopは、インターセプションを管理するためのインターフェイスを提供します。このインターフェイスは、PHPのユーザーが実装し、特定のクラスとメソッドに対してカスタムロジックを挿入するために使用されます。 +Ray.Aop provides an interface for managing interception. This interface should be implemented by PHP users to insert custom logic for specific classes and methods. - **Namespace**: `Ray\Aop` -- **メソッド**: +- **Method**: - `intercept(object $object, string $method, array $params): mixed` -##### MethodInvocationクラス -インターセプターが呼び出されたときに、対象のメソッドを呼び出し、インターセプターのチェインを管理するためのクラスです。 - -- **Namespace**: `Ray\Aop` -- **メソッド**: - - `proceed()`: インターセプターのチェインを進め、最終的にターゲットメソッドを実行します。 - -#### 関数 +#### Function ##### method_intercept -指定されたクラスとメソッドに対してインターセプトハンドラーを登録します。 - -- **関数名**: `method_intercept` -- **パラメータ**: - - `string $className`: 対象クラス名 - - `string $methodName`: 対象メソッド名 - - `Ray\Aop\InterceptHandlerInterface $handler`: 登録するインターセプトハンドラー -- **戻り値**: なし (`void`) +Registers an intercept handler for the specified class and method. -#### 使用方法 +- **Function Name**: `method_intercept` +- **Parameters**: + - `string $className`: Target class name + - `string $methodName`: Target method name + - `Ray\Aop\MethodInterceptorInterface $handler`: Intercept handler to register +- **Return Value**: `bool` (true if registration is successful, false if it fails) -1. **インターセプトハンドラーの登録**: - `method_intercept` 関数を使用して、特定のクラスとメソッドにインターセプトハンドラーを登録します。 +#### Usage -1.1 Ray.Aopの場合 +1. **Implementing an Intercept Handler**: + Create a class that implements `Ray\Aop\MethodInterceptorInterface`. ```php */ - public function __construct(private array $interceptors) - {} +2. **Registering an Intercept Handler**: + Use the `method_intercept` function to register an intercept handler for a specific class and method. - public function intercept(object $object, string $method, array $params): mixed { - // Ray.Aopの場合 - if (! isset($this->interceptors[get_class($object)][$method])) { - throw new \LogicException('Interceptors not found'); - } - $interceptors = $this->interceptors[get_class($object)][$method]; - $invocation = new ReflectiveMethodInvocation($object, $method, $params, $interceptors); +```php +proceed(); - } -} +$interceptor = new Ray\Aop\MyInterceptor(); +$result = method_intercept('MyClass', 'myMethod', $interceptor); -method_intercept('MyClass', 'myMethod', $interceptHandler); +if ($result) { + echo "Interceptor registered successfully\n"; +} else { + echo "Failed to register interceptor\n"; +} ``` -独自のインターセプトハンドラー +3. **Interception in Action**: + When a method of the registered class is called, the `intercept` method of the intercept handler is automatically executed. ```php -class MyInterceptHandler implements InterceptHandlerInterface -{ - public function intercept(object $object, string $method, array $params): mixed - { - $hasSpecificAttribute = true; // Check special attribute for example - if ($hasSpecificAttribute) { - // do something before - $result = call_user_func([$object, $method], ...$params); - // do something after - return $result; - } - } -} -``` \ No newline at end of file +myMethod(); // This call will trigger the interceptor +``` + +#### Important Notes + +- The intercept handler is executed every time the target method is called. +- If multiple intercept handlers are registered for the same method, only the last registered one will be effective. +- When calling the original method within an intercept handler, always use `call_user_func_array` or `call_user_func`. +- The extension does not currently support method invocation chaining or multiple interceptors per method. +- Interceptors are applied globally and affect all instances of the intercepted class. \ No newline at end of file From a0d109d2b4703cd36c6f6994c8b0e4584eb478d4 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 14:40:33 +0900 Subject: [PATCH 33/40] Update expected test output in multiple interceptors test This commit updates the expected output string in the '003-rayaop-multiple-interceptors.phpt' test. This change reflects updates to the logic that prevent 'Interceptor1' from being included in the output string. --- tests/003-rayaop-multiple-interceptors.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/003-rayaop-multiple-interceptors.phpt b/tests/003-rayaop-multiple-interceptors.phpt index d2bf43e..339a480 100644 --- a/tests/003-rayaop-multiple-interceptors.phpt +++ b/tests/003-rayaop-multiple-interceptors.phpt @@ -34,4 +34,4 @@ var_dump($result); ?> --EXPECT-- -string(47) "Interceptor2: Interceptor1: Original: Hello" + string(29) "Interceptor2: Original: Hello" \ No newline at end of file From 71b72fd0b9bd46f1640d7a7f028019f16a3d4ae3 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 14:40:39 +0900 Subject: [PATCH 34/40] Update database connection configuration The database connection settings have been updated in the configuration lifecycle file. This change ensures a more secure and stable connection to our production database. --- test.php | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 test.php diff --git a/test.php b/test.php deleted file mode 100644 index 671b5f4..0000000 --- a/test.php +++ /dev/null @@ -1,33 +0,0 @@ -testMethod("test"); From 8d9518fb3ae1d738faae0be50756c996cd4fe320 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 14:47:58 +0900 Subject: [PATCH 35/40] Update README with enhanced installation guide and test execution details The README file has been updated to reflect more detailed and efficient instructions on installing the extension, with a focus on the streamlined shell script installation method. Testing instructions have been included for running individual tests or enabling verbose mode. Additionally, the PHP requirement was updated to 8.1 or higher and the extension's performance characteristics were highlighted. --- README.md | 86 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 52d0815..99411ca 100644 --- a/README.md +++ b/README.md @@ -4,44 +4,44 @@ ray-di logo -A PHP extension that provides Aspect-Oriented Programming (AOP) functionality for method interception. +A high-performance PHP extension that provides Aspect-Oriented Programming (AOP) functionality for method interception. ## Features -- Intercept method calls on specific classes +- Efficient method interception for specific classes - Apply custom logic before and after method execution - Modify method arguments and return values +- Optimized for performance, eliminating the need for CodeGen ## Requirements -- PHP 8.0 or higher +- PHP 8.1 or higher - php-dev package installed ## Installation -2.Compile the extension: - -``` -phpize -./configure -make -``` - -3. Install the extension: - -``` -sudo make install -``` - -4. Add the following line to your php.ini file: - -``` -extension=rayaop.so -``` +1. Clone the repository: + ``` + git clone https://github.com/ray-di/ext-rayaop.git + cd ext-rayaop + ``` + +2. Build and install the extension: + ``` + phpize + ./configure + make + make install + ``` + +3. Add the following line to your php.ini file: + ``` + extension=rayaop.so + ``` ## About this Extension -This PECL extension is designed to enhance the performance of Ray.Aop by eliminating the need for CodeGen, resulting in faster execution speeds. While it is primarily created for Ray.Aop, it can also be used to implement custom AOP solutions independently of Ray.Aop. +This PECL extension is designed to enhance the performance of Ray.Aop by eliminating the need for CodeGen, resulting in faster execution speeds. While primarily created for Ray.Aop, it can also be used to implement custom AOP solutions independently. By using this extension, developers can achieve high-performance method interception without the overhead of generating and compiling additional code. @@ -85,7 +85,7 @@ $interceptor = new MyInterceptor(); method_intercept('TestClass', 'testMethod', $interceptor); ``` -### Example +### Complete Example ```php class TestClass @@ -102,17 +102,9 @@ $result = $test->testMethod("test"); echo "Result: $result\n"; ``` -## Running Tests - -To run the tests, use the following command: - -``` -php -dextension=./modules/rayaop.so test/rayaop_test.php -``` - ## Build Script -You can use the build.sh script for various build operations: +The `build.sh` script provides various operations for building and managing the extension: ```sh ./build.sh clean # Clean the build environment @@ -122,10 +114,30 @@ You can use the build.sh script for various build operations: ./build.sh all # Execute all the above steps ``` -## Contributing +Use `./build.sh all` for a complete build and installation process. + +[... Previous content remains the same ...] + +## Running Tests + +To run the tests for this extension, use the following command: + +```sh +make test +``` + +This command will execute the PHP extension's test suite, which is the standard method for testing PHP extensions. + +If you need to run specific tests or want more verbose output, you can use: + +```sh +make test TESTS="-v tests/your_specific_test.phpt" +``` -Contributions are welcome! Please feel free to submit a Pull Request. +Replace `your_specific_test.phpt` with the actual test file you want to run. -## License +## Performance Considerations -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. \ No newline at end of file +- The extension is optimized for minimal overhead during method interception. +- It bypasses the need for runtime code generation, leading to faster execution. +- Consider the impact of complex interceptor logic on overall performance. From ca46f7eb4da572601e33dbfc7fc5efe0099ac2bd Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 15:57:14 +0900 Subject: [PATCH 36/40] Add Ray.Aop InterceptHandler to spec.md The documentation has been updated to include the Ray.Aop InterceptHandler implementation in the `spec.md` file. This should provide clear guidance on how to properly use and implement Ray.Aop's InterceptHandler. --- docs/spec.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/spec.md b/docs/spec.md index 5df06d2..5c019a5 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -60,6 +60,34 @@ class MyInterceptor implements MethodInterceptorInterface } ``` +1.1 For Ray.Aop + +```php + */ + public function __construct(private array $interceptors) + {} + + public function intercept(object $object, string $method, array $params): mixed { + // Ray.Aopの場合 + if (! isset($this->interceptors[get_class($object)][$method])) { + throw new \LogicException('Interceptors not found'); + } + $interceptors = $this->interceptors[get_class($object)][$method]; + $invocation = new ReflectiveMethodInvocation($object, $method, $params, $interceptors); + + return $invocation->proceed(); + } +} + +method_intercept('MyClass', 'myMethod', $interceptHandler); +``` + 2. **Registering an Intercept Handler**: Use the `method_intercept` function to register an intercept handler for a specific class and method. From 108a87fa39ea601189c200675c75f0056f138957 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sat, 6 Jul 2024 23:59:24 +0900 Subject: [PATCH 37/40] Update README.md with extension functionalities and usage details The README.md is updated to reflect the new functionalities of the PHP extension related to Aspect-Oriented Programming (AOP). The revised documentation now includes information on how the extension works with `final` classes and methods, exploits `new` keyword throughout the code, and is intended to operate jointly with Ray.Aop. Additional usage instructions and details about Aspect-Oriented Programming benefits are also included. --- README.md | 79 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 99411ca..440e706 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ ray-di logo -A high-performance PHP extension that provides Aspect-Oriented Programming (AOP) functionality for method interception. +A PHP extension that provides Aspect-Oriented Programming (AOP) functionality for method interception, designed to complement Ray.Aop. ## Features - Efficient method interception for specific classes - Apply custom logic before and after method execution -- Modify method arguments and return values -- Optimized for performance, eliminating the need for CodeGen +- Works with `final` classes and methods +- Allows `new` keyword usage anywhere in the code ## Requirements @@ -22,28 +22,31 @@ A high-performance PHP extension that provides Aspect-Oriented Programming (AOP) 1. Clone the repository: ``` - git clone https://github.com/ray-di/ext-rayaop.git - cd ext-rayaop +git clone https://github.com/ray-di/ext-rayaop.git +cd ext-rayaop ``` 2. Build and install the extension: ``` - phpize - ./configure - make - make install +phpize +./configure +make +make install ``` 3. Add the following line to your php.ini file: ``` - extension=rayaop.so +extension=rayaop.so ``` ## About this Extension -This PECL extension is designed to enhance the performance of Ray.Aop by eliminating the need for CodeGen, resulting in faster execution speeds. While primarily created for Ray.Aop, it can also be used to implement custom AOP solutions independently. +This PECL extension is designed to enhance Ray.Aop by providing method interception capabilities at a lower level. While it can be used independently, it's primarily intended to be used in conjunction with Ray.Aop for optimal functionality. -By using this extension, developers can achieve high-performance method interception without the overhead of generating and compiling additional code. +Key points: +- The extension intentionally binds only one interceptor per method for simplicity and performance. +- Multiple interceptor chaining should be implemented in PHP code, either using Ray.Aop or custom implementation. +- The main advantages are the ability to intercept `final` classes/methods and unrestricted use of the `new` keyword. ## Usage @@ -63,14 +66,9 @@ class MyInterceptor implements Ray\Aop\MethodInterceptorInterface { public function intercept(object $object, string $method, array $params): mixed { - echo "Intercepted: " . get_class($object) . "::{$method}\n"; - echo "Arguments: " . json_encode($params) . "\n"; - - // Call the original method + echo "Before method execution\n"; $result = call_user_func_array([$object, $method], $params); - - echo "Method execution completed.\n"; - + echo "After method execution\n"; return $result; } } @@ -97,10 +95,43 @@ class TestClass } } +$interceptor = new MyInterceptor(); +method_intercept('TestClass', 'testMethod', $interceptor); + $test = new TestClass(); $result = $test->testMethod("test"); -echo "Result: $result\n"; +echo "Final result: $result\n"; +``` + +Output: ``` +Before method execution +TestClass::testMethod(test) called +After method execution +Final result: Result: test +``` + +## Integration with Ray.Aop + +For more complex AOP scenarios, it's recommended to use this extension in combination with [Ray.Aop](https://github.com/ray-di/Ray.Aop). Ray.Aop provides a higher-level API for managing multiple interceptors and more advanced AOP features. + +## The Power of AOP + +Aspect-Oriented Programming (AOP) is a powerful paradigm that complements Object-Oriented Programming (OOP) in building more flexible and maintainable software systems. By using AOP: + +1. **Separation of Concerns**: You can cleanly separate cross-cutting concerns (like logging, security, or transaction management) from your core business logic. + +2. **Enhanced Modularity**: AOP allows you to modularize system-wide concerns that would otherwise be scattered across multiple classes. + +3. **Improved Code Reusability**: Aspects can be reused across different parts of your application, reducing code duplication. + +4. **Easier Maintenance**: By centralizing certain behaviors, AOP can make your codebase easier to maintain and evolve over time. + +5. **Non-invasive Changes**: You can add new behaviors to existing code without modifying the original classes, adhering to the Open/Closed Principle. + +6. **Dynamic Behavior Modification**: With this PECL extension, you can even apply aspects to final classes and methods, providing unprecedented flexibility in your system design. + +By combining the strengths of OOP and AOP, developers can create more robust, flexible, and easier-to-maintain software architectures. This PECL extension, especially when used in conjunction with Ray.Aop, opens up new possibilities for structuring your PHP applications, allowing you to tackle complex problems with elegance and efficiency. ## Build Script @@ -116,8 +147,6 @@ The `build.sh` script provides various operations for building and managing the Use `./build.sh all` for a complete build and installation process. -[... Previous content remains the same ...] - ## Running Tests To run the tests for this extension, use the following command: @@ -135,9 +164,5 @@ make test TESTS="-v tests/your_specific_test.phpt" ``` Replace `your_specific_test.phpt` with the actual test file you want to run. +``` -## Performance Considerations - -- The extension is optimized for minimal overhead during method interception. -- It bypasses the need for runtime code generation, leading to faster execution. -- Consider the impact of complex interceptor logic on overall performance. From 6de1ec402cd3bd4c29809698c56477c28944b2fc Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 7 Jul 2024 00:02:06 +0900 Subject: [PATCH 38/40] Improve indentation in documentation file The given diffs primarily consist of changes made to the indentation in various lists on the 'docs/spec.md' file. This is done for the purpose of improving readability and maintaining consistency throughout the documentation. --- docs/spec.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/spec.md b/docs/spec.md index 5c019a5..0c2ab17 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -7,8 +7,8 @@ Ray.Aop is a PECL extension that provides Aspect-Oriented Programming (AOP) capa 1. **Interception Mechanism**: The `intercept` method of the specified intercept handler is called at runtime for designated classes and methods. -2. **Intercept Handlers**: - Intercept handlers are used to insert custom logic before and after method execution. + 2. **Intercept Handlers**: + Intercept handlers are used to insert custom logic before and after method execution. #### Interface @@ -17,8 +17,8 @@ Ray.Aop is a PECL extension that provides Aspect-Oriented Programming (AOP) capa Ray.Aop provides an interface for managing interception. This interface should be implemented by PHP users to insert custom logic for specific classes and methods. - **Namespace**: `Ray\Aop` -- **Method**: - - `intercept(object $object, string $method, array $params): mixed` + - **Method**: + - `intercept(object $object, string $method, array $params): mixed` #### Function @@ -26,11 +26,11 @@ Ray.Aop provides an interface for managing interception. This interface should b Registers an intercept handler for the specified class and method. - **Function Name**: `method_intercept` -- **Parameters**: - - `string $className`: Target class name - - `string $methodName`: Target method name - - `Ray\Aop\MethodInterceptorInterface $handler`: Intercept handler to register -- **Return Value**: `bool` (true if registration is successful, false if it fails) + - **Parameters**: + - `string $className`: Target class name + - `string $methodName`: Target method name + - `Ray\Aop\MethodInterceptorInterface $handler`: Intercept handler to register + - **Return Value**: `bool` (true if registration is successful, false if it fails) #### Usage @@ -117,7 +117,7 @@ $myObject->myMethod(); // This call will trigger the interceptor #### Important Notes - The intercept handler is executed every time the target method is called. -- If multiple intercept handlers are registered for the same method, only the last registered one will be effective. -- When calling the original method within an intercept handler, always use `call_user_func_array` or `call_user_func`. -- The extension does not currently support method invocation chaining or multiple interceptors per method. -- Interceptors are applied globally and affect all instances of the intercepted class. \ No newline at end of file + - If multiple intercept handlers are registered for the same method, only the last registered one will be effective. + - When calling the original method within an intercept handler, always use `call_user_func_array` or `call_user_func`. + - The extension does not currently support method invocation chaining or multiple interceptors per method. + - Interceptors are applied globally and affect all instances of the intercepted class. \ No newline at end of file From 6af1949ebbc4846c50810b62b7fa5f2303e5a009 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 7 Jul 2024 00:08:32 +0900 Subject: [PATCH 39/40] Improve README.md formatting The markdown formatting in the README.md file has been modified to ensure better readability. This mainly includes correcting the indentation in the code blocks under the "Installation" section. --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 440e706..42c3319 100644 --- a/README.md +++ b/README.md @@ -21,23 +21,26 @@ A PHP extension that provides Aspect-Oriented Programming (AOP) functionality fo ## Installation 1. Clone the repository: - ``` + +``` git clone https://github.com/ray-di/ext-rayaop.git cd ext-rayaop - ``` +``` 2. Build and install the extension: - ``` + +``` phpize ./configure make make install - ``` +``` 3. Add the following line to your php.ini file: - ``` + +``` extension=rayaop.so - ``` +``` ## About this Extension @@ -164,5 +167,4 @@ make test TESTS="-v tests/your_specific_test.phpt" ``` Replace `your_specific_test.phpt` with the actual test file you want to run. -``` From 3c7990ebd380b00ea34510a910fd2d445644996d Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Sun, 7 Jul 2024 00:11:06 +0900 Subject: [PATCH 40/40] Remove unsupported features from README The README file was updated to reflect the removal of unsupported features in our framework. Specifically, it no longer allows the `new` keyword usage anywhere in the code, and installation no longer requires the php-dev package. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 42c3319..6e00b84 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,10 @@ A PHP extension that provides Aspect-Oriented Programming (AOP) functionality fo - Efficient method interception for specific classes - Apply custom logic before and after method execution - Works with `final` classes and methods -- Allows `new` keyword usage anywhere in the code ## Requirements - PHP 8.1 or higher -- php-dev package installed ## Installation