Esempio n. 1
0
 /**
  * Write session data
  * @private For internal use only
  * @param string $id Session id
  * @param string $dataStr Session data. Not that you should ever call this
  *   directly, but note that this has the same issues with code injection
  *   via user-controlled data as does PHP's unserialize function.
  * @return bool Success
  */
 public function write($id, $dataStr)
 {
     if (self::$instance !== $this) {
         throw new \UnexpectedValueException(__METHOD__ . ': Wrong instance called!');
     }
     if (!$this->enable) {
         throw new \BadMethodCallException('Attempt to use PHP session management');
     }
     $session = $this->manager->getSessionById($id, true);
     if (!$session) {
         // This can happen under normal circumstances, if the session exists but is
         // invalid. Let's emit a log warning instead of a PHP warning.
         $this->logger->warning(__METHOD__ . ": Session \"{$id}\" cannot be loaded, skipping write.");
         return true;
     }
     // First, decode the string PHP handed us
     $data = \Wikimedia\PhpSessionSerializer::decode($dataStr);
     if ($data === null) {
         // @codeCoverageIgnoreStart
         return false;
         // @codeCoverageIgnoreEnd
     }
     // Now merge the data into the Session object.
     $changed = false;
     $cache = isset($this->sessionFieldCache[$id]) ? $this->sessionFieldCache[$id] : array();
     foreach ($data as $key => $value) {
         if (!array_key_exists($key, $cache)) {
             if ($session->exists($key)) {
                 // New in both, so ignore and log
                 $this->logger->warning(__METHOD__ . ": Key \"{$key}\" added in both Session and \$_SESSION!");
             } else {
                 // New in $_SESSION, keep it
                 $session->set($key, $value);
                 $changed = true;
             }
         } elseif ($cache[$key] === $value) {
             // Unchanged in $_SESSION, so ignore it
         } elseif (!$session->exists($key)) {
             // Deleted in Session, keep but log
             $this->logger->warning(__METHOD__ . ": Key \"{$key}\" deleted in Session and changed in \$_SESSION!");
             $session->set($key, $value);
             $changed = true;
         } elseif ($cache[$key] === $session->get($key)) {
             // Unchanged in Session, so keep it
             $session->set($key, $value);
             $changed = true;
         } else {
             // Changed in both, so ignore and log
             $this->logger->warning(__METHOD__ . ": Key \"{$key}\" changed in both Session and \$_SESSION!");
         }
     }
     // Anything deleted in $_SESSION and unchanged in Session should be deleted too
     // (but not if $_SESSION can't represent it at all)
     \Wikimedia\PhpSessionSerializer::setLogger(new \Psr\Log\NullLogger());
     foreach ($cache as $key => $value) {
         if (!array_key_exists($key, $data) && $session->exists($key) && \Wikimedia\PhpSessionSerializer::encode(array($key => true))) {
             if ($cache[$key] === $session->get($key)) {
                 // Unchanged in Session, delete it
                 $session->remove($key);
                 $changed = true;
             } else {
                 // Changed in Session, ignore deletion and log
                 $this->logger->warning(__METHOD__ . ": Key \"{$key}\" changed in Session and deleted in \$_SESSION!");
             }
         }
     }
     \Wikimedia\PhpSessionSerializer::setLogger($this->logger);
     // Save and update cache if anything changed
     if ($changed) {
         if ($this->warn) {
             wfDeprecated('$_SESSION', '1.27');
             $this->logger->warning('Something wrote to $_SESSION!');
         }
         $session->save();
         $this->sessionFieldCache[$id] = iterator_to_array($session);
     }
     $session->persist();
     return true;
 }
     *   user-controlled data here that you would to PHP's unserialize function.
     * @return array|null Data, or null on failure
     * @throws \\InvalidArgumentException
     */
    public static function decodePhpSerialize($data)
    {
        if (!is_string($data)) {
            throw new \InvalidArgumentException('$data must be a string');
        }
        $error = null;
        set_error_handler(function ($errno, $errstr) use(&$error) {
            $error = $errstr;
            return true;
        });
        $ret = unserialize($data);
        restore_error_handler();
        if ($error !== null) {
            self::$logger->error('PHP unserialization failed: ' . $error);
            return null;
        }
        // PHP strangely allows non-arrays to session_decode(), even though
        // that breaks $_SESSION. Let's not do that.
        if (!is_array($ret)) {
            self::$logger->error('PHP unserialization failed (value was not an array)');
            return null;
        }
        return $ret;
    }
}
PhpSessionSerializer::setLogger(new \Psr\Log\NullLogger());