Repository navigation
fix(templates): drop deprecated session ID ini settings from the PHP 8.4 and 8.5 configs #519
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mrrobot47
wants to merge
7
commits into
EasyEngine:develop
Choose a base branch
from
mrrobot47:fix/php-session-sid-deprecations
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
5f2fff3
fix(templates): drop deprecated session ID ini settings from the PHP …
mrrobot47 e2b97b0
feat(migrations): drop deprecated session ID settings from PHP 8.4 an…
mrrobot47 f43f991
chore(templates): remove the unused phplatest.zip
mrrobot47 d5a67dc
fix(migrations): rewrite php.ini in place instead of through a temp file
mrrobot47 3f9436c
docs(migrations): correct why php.ini is rewritten in place
mrrobot47 98b44c5
fix(templates): turn off session upload progress in the PHP 8.1 to 8.…
mrrobot47 f8e6049
fix(migrations): leave php.ini as it is when removing the session ID …
mrrobot47 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
269 changes: 269 additions & 0 deletions
269
migrations/container/20261023193000_site-command_drop_deprecated_session_sid_settings.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,269 @@ | ||
| <?php | ||
|
|
||
| namespace EE\Migration; | ||
|
|
||
| use EE; | ||
| use EE\Model\Site; | ||
|
|
||
| /** | ||
| * Removes session.sid_length and session.sid_bits_per_character, deprecated as of PHP 8.4, from the php.ini of PHP 8.4 and 8.5 sites. | ||
| * | ||
| * A site's php.ini comes from its image at creation and from the version's archive on a PHP switch, so existing sites keep them otherwise. | ||
| */ | ||
| class DropDeprecatedSessionSidSettings extends Base { | ||
|
|
||
| /** `latest` sites run the php8.4 image. */ | ||
| const PHP_VERSIONS = [ '8.4', '8.5', 'latest' ]; | ||
|
|
||
| /** Values EasyEngine shipped; any other value was set by the user. */ | ||
| const SHIPPED_VALUES = [ | ||
| 'session.sid_length' => '26', | ||
| 'session.sid_bits_per_character' => '5', | ||
| ]; | ||
|
|
||
| /** Comments shipped right above each directive, removed together with it. */ | ||
| const SHIPPED_COMMENTS = [ | ||
| 'session.sid_length' => [ | ||
| '; Set session ID character length. This value could be between 22 to 256.', | ||
| '; Shorter length than default is supported only for compatibility reason.', | ||
| '; Users should use 32 or more chars.', | ||
| '; http://php.net/session.sid-length', | ||
| '; Default Value: 32', | ||
| '; Development Value: 26', | ||
| '; Production Value: 26', | ||
| ], | ||
| 'session.sid_bits_per_character' => [ | ||
| '; Define how many bits are stored in each character when converting', | ||
| '; the binary hash data to something readable.', | ||
| '; Possible values:', | ||
| '; 4 (4 bits: 0-9, a-f)', | ||
| '; 5 (5 bits: 0-9, a-v)', | ||
| '; 6 (6 bits: 0-9, a-z, A-Z, "-", ",")', | ||
| '; Default Value: 4', | ||
| '; Development Value: 5', | ||
| '; Production Value: 5', | ||
| '; http://php.net/session.hash-bits-per-character', | ||
| ], | ||
| ]; | ||
|
|
||
| /** Entry of the quick reference at the top of the shipped php.ini. */ | ||
| const SHIPPED_QUICK_REFERENCE = [ | ||
| '; session.sid_bits_per_character', | ||
| '; Default Value: 4', | ||
| '; Development Value: 5', | ||
| '; Production Value: 5', | ||
| ]; | ||
|
|
||
| private $sites; | ||
|
|
||
| /** @var array Files changed by up(), restored by down(). */ | ||
| private $changed = []; | ||
|
|
||
| public function __construct() { | ||
|
|
||
| parent::__construct(); | ||
| $this->sites = Site::all(); | ||
| if ( $this->is_first_execution || ! $this->sites ) { | ||
| $this->skip_this_migration = true; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @throws EE\ExitException | ||
| */ | ||
| public function up() { | ||
|
|
||
| if ( $this->skip_this_migration ) { | ||
| EE::debug( 'Skipping drop-deprecated-session-sid-settings migration as it is not needed.' ); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| foreach ( $this->sites as $site ) { | ||
| if ( ! in_array( $site->site_type, [ 'php', 'wp' ], true ) || ! in_array( (string) $site->php_version, self::PHP_VERSIONS, true ) ) { | ||
| continue; | ||
| } | ||
|
|
||
| // One broken site must not block the upgrade of the others. | ||
| try { | ||
| $this->migrate_site( $site ); | ||
| } catch ( \Throwable $e ) { | ||
| EE::warning( sprintf( 'Could not remove the deprecated session ID settings from the php.ini of %s: %s', $site->site_url, $e->getMessage() ) ); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Restores the files changed by up(). | ||
| * | ||
| * @throws EE\ExitException | ||
| */ | ||
| public function down() { | ||
|
|
||
| foreach ( array_reverse( $this->changed ) as $change ) { | ||
| try { | ||
| $old = file_get_contents( $change['backup'] ); | ||
| if ( false === $old ) { | ||
| throw new \Exception( 'Could not read ' . $change['backup'] ); | ||
| } | ||
| self::replace_file( $change['ini'], $old ); | ||
| $this->fs->remove( $change['backup'] ); | ||
| self::reload_fpm( $change['site'] ); | ||
| } catch ( \Throwable $e ) { | ||
| EE::warning( sprintf( 'Could not restore the php.ini of %s from %s: %s', $change['site']->site_url, $change['backup'], $e->getMessage() ) ); | ||
| } | ||
| } | ||
| $this->changed = []; | ||
| } | ||
|
|
||
| /** | ||
| * @param Site $site PHP 8.4 or 8.5 site. | ||
| * | ||
| * @throws \Exception | ||
| */ | ||
| private function migrate_site( $site ) { | ||
|
|
||
| $ini = $site->site_fs_path . '/config/php/php/php.ini'; | ||
|
|
||
| // Missing on macOS, where php.ini stays in the container. | ||
| if ( is_link( $ini ) || ! is_file( $ini ) ) { | ||
| EE::debug( "Skipping $site->site_url: no php.ini at $ini." ); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| $content = file_get_contents( $ini ); | ||
| if ( false === $content ) { | ||
| throw new \Exception( "Could not read $ini." ); | ||
| } | ||
|
|
||
| list( $new_content, $removed ) = self::strip_settings( $content ); | ||
| if ( $new_content === $content ) { | ||
| EE::debug( "$ini has no deprecated session ID settings." ); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| $backup = EE_BACKUP_DIR . '/' . $site->site_url . '/php.ini.with-session-sid-settings'; | ||
| $this->fs->mkdir( dirname( $backup ) ); | ||
| $this->fs->copy( $ini, $backup, true ); | ||
|
|
||
| self::replace_file( $ini, $new_content ); | ||
| $this->changed[] = [ | ||
| 'site' => $site, | ||
| 'ini' => $ini, | ||
| 'backup' => $backup, | ||
| ]; | ||
| EE::debug( "Removed the deprecated session ID settings from $ini; the previous file is $backup." ); | ||
|
|
||
| foreach ( $removed as $line => $shipped ) { | ||
| if ( ! $shipped ) { | ||
| EE::warning( sprintf( 'Removed `%s` from the php.ini of %s: it is deprecated as of PHP 8.4, and PHP now uses its default. The previous file is saved as %s.', $line, $site->site_url, $backup ) ); | ||
| } | ||
| } | ||
|
|
||
| self::reload_fpm( $site ); | ||
| } | ||
|
|
||
| /** | ||
| * Removes the two directives, wherever they are set, and the shipped comments that document them. | ||
| * Every other line is kept byte for byte. | ||
| * | ||
| * @param string $content php.ini content. | ||
| * | ||
| * @return array New content, and the removed directive lines => whether the value is the shipped one. | ||
| */ | ||
| public static function strip_settings( $content ) { | ||
|
|
||
| $lines = preg_split( '/(?<=\n)/', $content, -1, PREG_SPLIT_NO_EMPTY ); | ||
| $norm = array_map( | ||
| function ( $line ) { | ||
| return rtrim( $line, " \t\r\n" ); | ||
| }, | ||
| $lines | ||
| ); | ||
| $drop = []; | ||
| $removed = []; | ||
|
|
||
| $quick_count = count( self::SHIPPED_QUICK_REFERENCE ); | ||
| foreach ( array_keys( $norm ) as $i ) { | ||
| if ( array_slice( $norm, $i, $quick_count ) === self::SHIPPED_QUICK_REFERENCE ) { | ||
| $drop += array_fill( $i, $quick_count, true ); | ||
| if ( isset( $norm[ $i + $quick_count ] ) && '' === $norm[ $i + $quick_count ] ) { | ||
| $drop[ $i + $quick_count ] = true; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| foreach ( $norm as $i => $line ) { | ||
| if ( ! preg_match( '/^\s*(session\.sid_length|session\.sid_bits_per_character)\s*=(.*)$/', $line, $matches ) ) { | ||
| continue; | ||
| } | ||
| $name = $matches[1]; | ||
| $drop[ $i ] = true; | ||
|
|
||
| // Inline comment and quotes don't change the value. | ||
| $value = trim( preg_replace( '/;.*$/', '', $matches[2] ), " \t\"'" ); | ||
| $removed[ trim( $line ) ] = self::SHIPPED_VALUES[ $name ] === $value; | ||
|
|
||
| $comments = self::SHIPPED_COMMENTS[ $name ]; | ||
| $count = count( $comments ); | ||
| if ( $i >= $count && array_slice( $norm, $i - $count, $count ) === $comments ) { | ||
| $drop += array_fill( $i - $count, $count, true ); | ||
| if ( isset( $norm[ $i + 1 ] ) && '' === $norm[ $i + 1 ] ) { | ||
| $drop[ $i + 1 ] = true; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return [ implode( '', array_diff_key( $lines, $drop ) ), $removed ]; | ||
| } | ||
|
|
||
| /** | ||
| * Replaces the file only once the new content is complete, keeping its mode and owner. | ||
| * | ||
| * @param string $file File to replace. | ||
| * @param string $content New content. | ||
| * | ||
| * @throws \Exception | ||
| */ | ||
| private static function replace_file( $file, $content ) { | ||
|
|
||
| $tmp = $file . '.ee-tmp'; | ||
| if ( strlen( $content ) !== file_put_contents( $tmp, $content ) | ||
| || ! chmod( $tmp, fileperms( $file ) & 0777 ) | ||
| || ! chown( $tmp, fileowner( $file ) ) | ||
| || ! chgrp( $tmp, filegroup( $file ) ) | ||
| || ! rename( $tmp, $file ) ) { | ||
| if ( is_file( $tmp ) ) { | ||
| unlink( $tmp ); | ||
| } | ||
| throw new \Exception( "Could not write $file." ); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * PHP-FPM reads php.ini only when it starts; a graceful reload (as `ee site reload --php` does) applies it. | ||
| * Stopped containers read it when they start. | ||
| * | ||
| * @param Site $site Site. | ||
| */ | ||
| private static function reload_fpm( $site ) { | ||
|
|
||
| if ( ! $site->site_enabled || ! chdir( $site->site_fs_path ) ) { | ||
| return; | ||
| } | ||
|
|
||
| $compose = \EE_DOCKER::docker_compose_with_custom(); | ||
| $id = trim( EE::launch( "$compose ps -q php" )->stdout ); | ||
| if ( '' === $id || 'running' !== \EE_DOCKER::container_status( $id ) ) { | ||
| EE::debug( "The php container of $site->site_url is not running." ); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| if ( EE::launch( "$compose exec -T php bash -c 'kill -USR2 1'" )->return_code ) { | ||
| EE::warning( sprintf( 'Could not reload PHP-FPM of %1$s. Run `ee site restart %1$s --php` to apply its new php.ini.', $site->site_url ) ); | ||
| } | ||
| } | ||
| } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.