From bb36513e49859ae27788ec2a4127a79494e96967 Mon Sep 17 00:00:00 2001 From: Shivam Mathur Date: Wed, 25 Aug 2021 19:00:52 +0530 Subject: [PATCH] Add support to disable dependent extensions --- src/scripts/common.sh | 39 +++++++++-- src/scripts/darwin.sh | 6 +- src/scripts/ext/extension_map.php | 109 ++++++++++++++++++++++++++++++ src/scripts/linux.sh | 8 ++- src/scripts/win32.ps1 | 85 +++++++++++++++++++++-- 5 files changed, 233 insertions(+), 14 deletions(-) create mode 100644 src/scripts/ext/extension_map.php diff --git a/src/scripts/common.sh b/src/scripts/common.sh index 94b1eba8..e7c06ec9 100644 --- a/src/scripts/common.sh +++ b/src/scripts/common.sh @@ -126,6 +126,7 @@ enable_cache_extension() { enable_extension() { modules_dir="/var/lib/php/modules/$version" [ -d "$modules_dir" ] && sudo find "$modules_dir" -path "*disabled*$1" -delete + enable_extension_dependencies "$1" "$2" if [ -d /tmp/extcache/"$1" ]; then enable_cache_extension "$1" "$2" elif ! check_extension "$1" && [ -e "${ext_dir:?}/$1.so" ]; then @@ -133,13 +134,43 @@ enable_extension() { fi } -# Function to disable an extensions. +# Function to get a map of extensions and their dependent shared extensions. +get_extension_map() { + php -d'error_reporting=0' "${dist:?}"/../src/scripts/ext/extension_map.php +} + +# Function to enable extension dependencies which are also extensions. +enable_extension_dependencies() { + extension=$1 + prefix=$2 + if ! [ -e /tmp/map.orig ]; then + get_extension_map | sudo tee /tmp/map.orig >/dev/null + fi + for dependency in $(grep "$extension:" /tmp/map.orig | cut -d ':' -f 2 | tr '\n' ' '); do + enable_extension "$dependency" "$prefix" + done +} + +# Function to disable dependent extensions. +disable_extension_dependents() { + local extension=$1 + for dependent in $(get_extension_map | grep -E ".*:.*\s$extension(\s|$)" | cut -d ':' -f 1 | tr '\n' ' '); do + disable_extension_helper "$dependent" true + add_log "${tick:?}" ":$extension" "Disabled $dependent as it depends on $extension" + done +} + +# Function to disable an extension. disable_extension() { extension=$1 if check_extension "$extension"; then - disable_extension_helper "$extension" - (! check_extension "$extension" && add_log "${tick:?}" ":$extension" "Disabled") || - add_log "${cross:?}" ":$extension" "Could not disable $extension on PHP ${semver:?}" + if [ -e "${ext_dir:?}"/"$extension".so ]; then + disable_extension_helper "$extension" true + (! check_extension "$extension" && add_log "${tick:?}" ":$extension" "Disabled") || + add_log "${cross:?}" ":$extension" "Could not disable $extension on PHP ${semver:?}" + else + add_log "${cross:?}" ":$extension" "Could not disable $extension on PHP $semver as it not a shared extension" + fi else add_log "${tick:?}" ":$extension" "Could not find $extension on PHP $semver" fi diff --git a/src/scripts/darwin.sh b/src/scripts/darwin.sh index cf660734..de1321c4 100644 --- a/src/scripts/darwin.sh +++ b/src/scripts/darwin.sh @@ -9,7 +9,11 @@ self_hosted_helper() { # Helper function to disable an extension. disable_extension_helper() { - extension=$1 + local extension=$1 + local disable_dependents=${2:-false} + if [ "$disable_dependents" = "true" ]; then + disable_extension_dependents "$extension" + fi sudo sed -Ei '' "/=(.*\/)?\"?$extension(.so)?$/d" "${ini_file:?}" sudo rm -rf "$scan_dir"/*"$extension"* } diff --git a/src/scripts/ext/extension_map.php b/src/scripts/ext/extension_map.php new file mode 100644 index 00000000..fda0080d --- /dev/null +++ b/src/scripts/ext/extension_map.php @@ -0,0 +1,109 @@ +extension_dir = ini_get('extension_dir'); + $this->file_extension = (PHP_OS == 'WINNT' ? '.dll' : '.so'); + $this->file_prefix = (PHP_OS == 'WINNT' ? 'php_' : ''); + $this->map = ''; + } + + /** + * Function to check if a shared extension file exists. + * + * @param string $extension + * @return bool + */ + public function checkSharedExtension($extension) { + $extension_file = $this->extension_dir. DIRECTORY_SEPARATOR . $this->file_prefix . $extension . $this->file_extension; + return file_exists($extension_file); + } + + /** + * Function to get all shared extensions. + * + * @return string[] + */ + public function getSharedExtensions() { + $files = scandir($this->extension_dir); + $extensions = array_diff($files, array('.','..')); + $filter_pattern = "/$this->file_extension|$this->file_prefix/"; + return array_map(function ($extension) use($filter_pattern) { + return preg_replace($filter_pattern, '', $extension); + }, $extensions); + } + + /** + * Function to patch dependencies if there are any bugs in Reflection data. + * + * @param string $extension + * @param array $dependencies + * @return array + */ + public function patchDependencies($extension, $dependencies) { + // memcached 2.2.0 has no dependencies in reflection data. + if($extension == 'memcached') { + $dependencies = array_unique(array_merge($dependencies, array('igbinary', 'json', 'msgpack'))); + } + return $dependencies; + } + + /** + * Function to add extension to the map. + * + * @param string $extension + * @throws ReflectionException + */ + public function addExtensionToMap($extension) { + // PHP 5.3 does not allow using $this. + $self = $this; + + $ref = new ReflectionExtension($extension); + $dependencies = array_keys(array_map('strtolower', $ref->getDependencies())); + $dependencies = $this->patchDependencies($extension, $dependencies); + $dependencies = array_filter($dependencies, function ($dependency) use ($self) { + return $self->checkSharedExtension($dependency); + }); + $self->map .= $extension . ': ' . implode(' ', $dependencies) . PHP_EOL; + } + + /** + * Function to print the map of shared extensions and their dependent extensions. + * + * @return string + */ + public function __toString() { + $extensions = array_map('strtolower', $this->getSharedExtensions()); + foreach ($extensions as $extension) { + try { + $this->addExtensionToMap($extension); + } catch (ReflectionException $e) { + + } + } + return $this->map; + } +} + +$extension_map = new ExtensionMap(); +echo $extension_map; diff --git a/src/scripts/linux.sh b/src/scripts/linux.sh index cef7c135..335b0fe5 100644 --- a/src/scripts/linux.sh +++ b/src/scripts/linux.sh @@ -20,9 +20,13 @@ install_packages() { # Function to disable an extension. disable_extension_helper() { - extension=$1 + local extension=$1 + local disable_dependents=${2:-false} + if [ "$disable_dependents" = "true" ]; then + disable_extension_dependents "$extension" + fi sudo sed -Ei "/=(.*\/)?\"?$extension(.so)?$/d" "${ini_file[@]}" "$pecl_file" - sudo find "$ini_dir"/.. -name "*$extension.ini" -delete >/dev/null 2>&1 || true + sudo find "$ini_dir"/.. -name "*$extension.ini" -not -path "*mods-available*" -delete >/dev/null 2>&1 || true } # Function to add PDO extension. diff --git a/src/scripts/win32.ps1 b/src/scripts/win32.ps1 index cbb7c45c..975fcceb 100644 --- a/src/scripts/win32.ps1 +++ b/src/scripts/win32.ps1 @@ -192,6 +192,7 @@ Function Add-Extension { } default { $deps_dir = Get-ExtensionPrerequisites $extension + Enable-ExtensionDependencies $extension Enable-PhpExtension -Extension $extension_info.Handle -Path $php_dir Set-ExtensionPrerequisites $deps_dir Add-Log $tick $extension "Enabled" @@ -214,8 +215,13 @@ Function Add-Extension { } } -# Function to disable an extension. -Function Disable-Extension() { +# Function to get a map of extensions and their dependent shared extensions. +Function Get-ExtensionMap { + php -d'error_reporting=0' $dist\..\src\scripts\ext\extension_map.php +} + +# Function to enable extension dependencies which are also extensions. +Function Enable-ExtensionDependencies { Param ( [Parameter(Position = 0, Mandatory = $true)] [ValidateNotNull()] @@ -223,12 +229,76 @@ Function Disable-Extension() { [string] $extension ) + if (-not(Test-Path $env:TEMP\map.orig)) { + Get-ExtensionMap | Set-Content -Path $env:TEMP\map.orig + } + $entry = findstr /r "$extension`:.*" $env:TEMP\map.orig + if($entry) { + $entry.split(':')[1].trim().split(' ') | ForEach-Object { + if (-not(php -m | findstr -i $_)) { + Enable-PhpExtension -Extension $_ -Path $php_dir + } + } + } +} + +# Function to disable dependent extensions. +Function Disable-DependentExtensions() { + Param ( + [Parameter(Position = 0, Mandatory = $true)] + [ValidateNotNull()] + [ValidateLength(1, [int]::MaxValue)] + [string] + $extension + ) + Get-ExtensionMap | Select-String -Pattern ".*:.*\s$extension(\s|$)" | ForEach-Object { + $dependent = $_.Matches[0].Value.split(':')[0]; + Disable-ExtensionHelper -Extension $dependent -DisableDependents + Add-Log $tick ":$extension" "Disabled $dependent as it depends on $extension" + } +} + +# Helper function to disable an extension. +Function Disable-ExtensionHelper() { + Param ( + [Parameter(Position = 0, Mandatory = $true)] + [ValidateNotNull()] + [ValidateLength(1, [int]::MaxValue)] + [string] + $extension, + [switch] $DisableDependents + ) + if($DisableDependents) { + Disable-DependentExtensions $extension + } + Disable-PhpExtension -Extension $extension -Path $php_dir +} + +# Function to disable an extension. +Function Disable-Extension() { + Param ( + [Parameter(Position = 0, Mandatory = $true)] + [ValidateNotNull()] + [ValidateLength(1, [int]::MaxValue)] + [string] + $extension, + [Parameter(Position = 1, Mandatory = $false)] + [ValidateNotNull()] + [ValidateLength(1, [int]::MaxValue)] + [string] + $DisableDependents + ) if(php -m | findstr -i $extension) { - try { - Disable-PhpExtension $extension $php_dir - Add-Log $tick ":$extension" "Disabled" - } catch { - Add-Log $cross ":$extension" "Could not disable $extension on PHP $($installed.FullVersion)" + if(Test-Path $ext_dir\php_$extension.dll) { + try { + $params = @{ Extension = $extension; DisableDependents = ($DisableDependents -ne 'false') } + Disable-ExtensionHelper @params + Add-Log $tick ":$extension" "Disabled" + } catch { + Add-Log $cross ":$extension" "Could not disable $extension on PHP $($installed.FullVersion)" + } + } else { + Add-Log $cross ":$extension" "Could not disable $extension on PHP $($installed.FullVersion) as it not a shared extension" } } else { Add-Log $tick ":$extension" "Could not find $extension on PHP $($installed.FullVersion)" @@ -461,6 +531,7 @@ if ($null -eq $installed -or -not("$($installed.Version).".StartsWith(($version } } catch { } } else { + Set-PhpIniKey -Key 'extension_dir' -Value $ext_dir -Path $php_dir if($version -match $jit_versions) { ('opcache.enable=1', 'opcache.jit_buffer_size=256M', 'opcache.jit=1235') | ForEach-Object { $p=$_.split('='); Set-PhpIniKey -Key $p[0] -Value $p[1] -Path $php_dir } }