mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 06:37:55 +08:00 
			
		
		
		
	Fix #20140: Fix compatibility with PHP 8.4: calling session_set_save_handler()
				
					
				
			This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							5df412df2c
						
					
				
				
					commit
					65e3369e16
				
			@ -18,6 +18,7 @@ Yii Framework 2 Change Log
 | 
				
			|||||||
- New #20279: Add to the `\yii\web\Request` CSRF validation by custom HTTP header (olegbaturin)
 | 
					- New #20279: Add to the `\yii\web\Request` CSRF validation by custom HTTP header (olegbaturin)
 | 
				
			||||||
- Enh #20279: Add to the `\yii\web\Request` `csrfHeader` property to configure a custom  HTTP header for CSRF validation (olegbaturin)
 | 
					- Enh #20279: Add to the `\yii\web\Request` `csrfHeader` property to configure a custom  HTTP header for CSRF validation (olegbaturin)
 | 
				
			||||||
- Enh #20279: Add to the `\yii\web\Request` `csrfTokenSafeMethods` property to configure a custom safe HTTP methods list (olegbaturin)
 | 
					- Enh #20279: Add to the `\yii\web\Request` `csrfTokenSafeMethods` property to configure a custom safe HTTP methods list (olegbaturin)
 | 
				
			||||||
 | 
					- Bug #20140: Fix compatibility with PHP 8.4: calling `session_set_save_handler()` (Izumi-kun)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2.0.51 July 18, 2024
 | 
					2.0.51 July 18, 2024
 | 
				
			||||||
--------------------
 | 
					--------------------
 | 
				
			||||||
 | 
				
			|||||||
@ -51,6 +51,13 @@ if you want to upgrade from version A to version C and there is
 | 
				
			|||||||
version B between A and C, you need to follow the instructions
 | 
					version B between A and C, you need to follow the instructions
 | 
				
			||||||
for both A and B.
 | 
					for both A and B.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Upgrade from Yii 2.0.51
 | 
				
			||||||
 | 
					-----------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* The function signature for `yii\web\Session::readSession()` and `yii\web\Session::gcSession()` have been changed.
 | 
				
			||||||
 | 
					  They now have the same return types as `\SessionHandlerInterface::read()` and `\SessionHandlerInterface::gc()` respectively.
 | 
				
			||||||
 | 
					  In case those methods have overwritten you will need to update your child classes accordingly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Upgrade from Yii 2.0.50
 | 
					Upgrade from Yii 2.0.50
 | 
				
			||||||
-----------------------
 | 
					-----------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -92,7 +92,7 @@ class CacheSession extends Session
 | 
				
			|||||||
     * Session read handler.
 | 
					     * Session read handler.
 | 
				
			||||||
     * @internal Do not call this method directly.
 | 
					     * @internal Do not call this method directly.
 | 
				
			||||||
     * @param string $id session ID
 | 
					     * @param string $id session ID
 | 
				
			||||||
     * @return string the session data
 | 
					     * @return string|false the session data, or false on failure
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function readSession($id)
 | 
					    public function readSession($id)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
				
			|||||||
@ -171,7 +171,7 @@ class DbSession extends MultiFieldSession
 | 
				
			|||||||
     * Session read handler.
 | 
					     * Session read handler.
 | 
				
			||||||
     * @internal Do not call this method directly.
 | 
					     * @internal Do not call this method directly.
 | 
				
			||||||
     * @param string $id session ID
 | 
					     * @param string $id session ID
 | 
				
			||||||
     * @return string the session data
 | 
					     * @return string|false the session data, or false on failure
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function readSession($id)
 | 
					    public function readSession($id)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -247,15 +247,13 @@ class DbSession extends MultiFieldSession
 | 
				
			|||||||
     * Session GC (garbage collection) handler.
 | 
					     * Session GC (garbage collection) handler.
 | 
				
			||||||
     * @internal Do not call this method directly.
 | 
					     * @internal Do not call this method directly.
 | 
				
			||||||
     * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
 | 
					     * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
 | 
				
			||||||
     * @return bool whether session is GCed successfully
 | 
					     * @return int|false the number of deleted sessions on success, or false on failure
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function gcSession($maxLifetime)
 | 
					    public function gcSession($maxLifetime)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->db->createCommand()
 | 
					        return $this->db->createCommand()
 | 
				
			||||||
            ->delete($this->sessionTable, '[[expire]]<:expire', [':expire' => time()])
 | 
					            ->delete($this->sessionTable, '[[expire]]<:expire', [':expire' => time()])
 | 
				
			||||||
            ->execute();
 | 
					            ->execute();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 | 
				
			|||||||
@ -175,34 +175,23 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 | 
				
			|||||||
            static::$_originalSessionModule = $sessionModuleName;
 | 
					            static::$_originalSessionModule = $sessionModuleName;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($this->handler === null && $this->getUseCustomStorage()) {
 | 
				
			||||||
 | 
					            $this->handler = Yii::createObject(
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    '__class' => SessionHandler::class,
 | 
				
			||||||
 | 
					                    '__construct()' => [$this],
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($this->handler !== null) {
 | 
					        if ($this->handler !== null) {
 | 
				
			||||||
            if (!is_object($this->handler)) {
 | 
					            if (is_array($this->handler)) {
 | 
				
			||||||
                $this->handler = Yii::createObject($this->handler);
 | 
					                $this->handler = Yii::createObject($this->handler);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (!$this->handler instanceof \SessionHandlerInterface) {
 | 
					            if (!$this->handler instanceof \SessionHandlerInterface) {
 | 
				
			||||||
                throw new InvalidConfigException('"' . get_class($this) . '::handler" must implement the SessionHandlerInterface.');
 | 
					                throw new InvalidConfigException('"' . get_class($this) . '::handler" must implement the SessionHandlerInterface.');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            YII_DEBUG ? session_set_save_handler($this->handler, false) : @session_set_save_handler($this->handler, false);
 | 
					            YII_DEBUG ? session_set_save_handler($this->handler, false) : @session_set_save_handler($this->handler, false);
 | 
				
			||||||
        } elseif ($this->getUseCustomStorage()) {
 | 
					 | 
				
			||||||
            if (YII_DEBUG) {
 | 
					 | 
				
			||||||
                session_set_save_handler(
 | 
					 | 
				
			||||||
                    [$this, 'openSession'],
 | 
					 | 
				
			||||||
                    [$this, 'closeSession'],
 | 
					 | 
				
			||||||
                    [$this, 'readSession'],
 | 
					 | 
				
			||||||
                    [$this, 'writeSession'],
 | 
					 | 
				
			||||||
                    [$this, 'destroySession'],
 | 
					 | 
				
			||||||
                    [$this, 'gcSession']
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                @session_set_save_handler(
 | 
					 | 
				
			||||||
                    [$this, 'openSession'],
 | 
					 | 
				
			||||||
                    [$this, 'closeSession'],
 | 
					 | 
				
			||||||
                    [$this, 'readSession'],
 | 
					 | 
				
			||||||
                    [$this, 'writeSession'],
 | 
					 | 
				
			||||||
                    [$this, 'destroySession'],
 | 
					 | 
				
			||||||
                    [$this, 'gcSession']
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } elseif (
 | 
					        } elseif (
 | 
				
			||||||
            $sessionModuleName !== static::$_originalSessionModule
 | 
					            $sessionModuleName !== static::$_originalSessionModule
 | 
				
			||||||
            && static::$_originalSessionModule !== null
 | 
					            && static::$_originalSessionModule !== null
 | 
				
			||||||
@ -610,7 +599,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 | 
				
			|||||||
     * This method should be overridden if [[useCustomStorage]] returns true.
 | 
					     * This method should be overridden if [[useCustomStorage]] returns true.
 | 
				
			||||||
     * @internal Do not call this method directly.
 | 
					     * @internal Do not call this method directly.
 | 
				
			||||||
     * @param string $id session ID
 | 
					     * @param string $id session ID
 | 
				
			||||||
     * @return string the session data
 | 
					     * @return string|false the session data, or false on failure
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function readSession($id)
 | 
					    public function readSession($id)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -647,11 +636,11 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 | 
				
			|||||||
     * This method should be overridden if [[useCustomStorage]] returns true.
 | 
					     * This method should be overridden if [[useCustomStorage]] returns true.
 | 
				
			||||||
     * @internal Do not call this method directly.
 | 
					     * @internal Do not call this method directly.
 | 
				
			||||||
     * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
 | 
					     * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
 | 
				
			||||||
     * @return bool whether session is GCed successfully
 | 
					     * @return int|false the number of deleted sessions on success, or false on failure
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function gcSession($maxLifetime)
 | 
					    public function gcSession($maxLifetime)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return true;
 | 
					        return 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										79
									
								
								framework/web/SessionHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								framework/web/SessionHandler.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @link https://www.yiiframework.com/
 | 
				
			||||||
 | 
					 * @copyright Copyright (c) 2008 Yii Software LLC
 | 
				
			||||||
 | 
					 * @license https://www.yiiframework.com/license/
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace yii\web;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use SessionHandlerInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * SessionHandler implements an [[\SessionHandlerInterface]] for handling [[Session]] with custom session storage.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Viktor Khokhryakov <viktor.khokhryakov@gmail.com>
 | 
				
			||||||
 | 
					 * @since 2.0.52
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class SessionHandler implements SessionHandlerInterface
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @var Session
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private $_session;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(Session $session)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->_session = $session;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritDoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function close(): bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->_session->closeSession();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritDoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function destroy($id): bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->_session->destroySession($id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritDoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    #[\ReturnTypeWillChange]
 | 
				
			||||||
 | 
					    public function gc($max_lifetime)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->_session->gcSession($max_lifetime);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritDoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function open($path, $name): bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->_session->openSession($path, $name);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritDoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    #[\ReturnTypeWillChange]
 | 
				
			||||||
 | 
					    public function read($id)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->_session->readSession($id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritDoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function write($id, $data): bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->_session->writeSession($id, $data);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -127,8 +127,9 @@ abstract class AbstractDbSessionTest extends TestCase
 | 
				
			|||||||
        $session->db->createCommand()
 | 
					        $session->db->createCommand()
 | 
				
			||||||
            ->update('session', ['expire' => time() - 100], 'id = :id', ['id' => 'expire'])
 | 
					            ->update('session', ['expire' => time() - 100], 'id = :id', ['id' => 'expire'])
 | 
				
			||||||
            ->execute();
 | 
					            ->execute();
 | 
				
			||||||
        $session->gcSession(1);
 | 
					        $deleted = $session->gcSession(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->assertEquals(1, $deleted);
 | 
				
			||||||
        $this->assertEquals('', $session->readSession('expire'));
 | 
					        $this->assertEquals('', $session->readSession('expire'));
 | 
				
			||||||
        $this->assertEquals('new data', $session->readSession('new'));
 | 
					        $this->assertEquals('new data', $session->readSession('new'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user