Вход Регистрация
Файл: gapps/vendor/jeremeamia/SuperClosure/src/Serializer.php
Строк: 239
<?php namespace SuperClosure;

use 
SuperClosureAnalyzerAstAnalyzer as DefaultAnalyzer;
use 
SuperClosureAnalyzerClosureAnalyzer;
use 
SuperClosureExceptionClosureSerializationException;
use 
SuperClosureExceptionClosureUnserializationException;

/**
 * This is the serializer class used for serializing Closure objects.
 *
 * We're abstracting away all the details, impossibilities, and scary things
 * that happen within.
 */
class Serializer implements SerializerInterface
{
    
/**
     * The special value marking a recursive reference to a closure.
     *
     * @var string
     */
    
const RECURSION "{{RECURSION}}";

    
/**
     * The keys of closure data required for serialization.
     *
     * @var array
     */
    
private static $dataToKeep = [
        
'code'     => true,
        
'context'  => true,
        
'binding'  => true,
        
'scope'    => true,
        
'isStatic' => true,
    ];

    
/**
     * The closure analyzer instance.
     *
     * @var ClosureAnalyzer
     */
    
private $analyzer;

    
/**
     * The HMAC key to sign serialized closures.
     *
     * @var string
     */
    
private $signingKey;

    
/**
     * Create a new serializer instance.
     *
     * @param ClosureAnalyzer|null $analyzer   Closure analyzer instance.
     * @param string|null          $signingKey HMAC key to sign closure data.
     */
    
public function __construct(
        
ClosureAnalyzer $analyzer null,
        
$signingKey null
    
) {
        
$this->analyzer $analyzer ?: new DefaultAnalyzer;
        
$this->signingKey $signingKey;
    }

    
/**
     * @inheritDoc
     */
    
public function serialize(Closure $closure)
    {
        
$serialized serialize(new SerializableClosure($closure$this));
        
        if (
$serialized === null) {
            throw new 
ClosureSerializationException(
                
'The closure could not be serialized.'
            
);
        }

        if (
$this->signingKey) {
            
$signature $this->calculateSignature($serialized);
            
$serialized '%' base64_encode($signature) . $serialized;
        }

        return 
$serialized;
    }

    
/**
     * @inheritDoc
     */
    
public function unserialize($serialized)
    {
        
// Strip off the signature from the front of the string.
        
$signature null;
        if (
$serialized[0] === '%') {
            
$signature base64_decode(substr($serialized144));
            
$serialized substr($serialized45);
        }

        
// If a key was provided, then verify the signature.
        
if ($this->signingKey) {
            
$this->verifySignature($signature$serialized);
        }

        
set_error_handler(function () {});
        
$unserialized unserialize($serialized);
        
restore_error_handler();
        if (
$unserialized === false) {
            throw new 
ClosureUnserializationException(
                
'The closure could not be unserialized.'
            
);
        } elseif (!
$unserialized instanceof SerializableClosure) {
            throw new 
ClosureUnserializationException(
                
'The closure did not unserialize to a SuperClosure.'
            
);
        }

        return 
$unserialized->getClosure();
    }

    
/**
     * @inheritDoc
     */
    
public function getData(Closure $closure$forSerialization false)
    {
        
// Use the closure analyzer to get data about the closure.
        
$data $this->analyzer->analyze($closure);

        
// If the closure data is getting retrieved solely for the purpose of
        // serializing the closure, then make some modifications to the data.
        
if ($forSerialization) {
            
// If there is no reference to the binding, don't serialize it.
            
if (!$data['hasThis']) {
                
$data['binding'] = null;
            }

            
// Remove data about the closure that does not get serialized.
            
$data array_intersect_key($dataself::$dataToKeep);

            
// Wrap any other closures within the context.
            
foreach ($data['context'] as &$value) {
                if (
$value instanceof Closure) {
                    
$value = ($value === $closure)
                        ? 
self::RECURSION
                        
: new SerializableClosure($value$this);
                }
            }
        }

        return 
$data;
    }

    
/**
     * Recursively traverses and wraps all Closure objects within the value.
     *
     * NOTE: THIS MAY NOT WORK IN ALL USE CASES, SO USE AT YOUR OWN RISK.
     *
     * @param mixed $data Any variable that contains closures.
     * @param SerializerInterface $serializer The serializer to use.
     */
    
public static function wrapClosures(&$dataSerializerInterface $serializer)
    {
        if (
$data instanceof Closure) {
            
// Handle and wrap closure objects.
            
$reflection = new ReflectionFunction($data);
            if (
$binding $reflection->getClosureThis()) {
                
self::wrapClosures($binding$serializer);
                
$scope $reflection->getClosureScopeClass();
                
$scope $scope $scope->getName() : 'static';
                
$data $data->bindTo($binding$scope);
            }
            
$data = new SerializableClosure($data$serializer);
        } elseif (
is_array($data) || $data instanceof stdClass || $data instanceof Traversable) {
            
// Handle members of traversable values.
            
foreach ($data as &$value) {
                
self::wrapClosures($value$serializer);
            }
        } elseif (
is_object($data) && !$data instanceof Serializable) {
            
// Handle objects that are not already explicitly serializable.
            
$reflection = new ReflectionObject($data);
            if (!
$reflection->hasMethod('__sleep')) {
                foreach (
$reflection->getProperties() as $property) {
                    if (
$property->isPrivate() || $property->isProtected()) {
                        
$property->setAccessible(true);
                    }
                    
$value $property->getValue($data);
                    
self::wrapClosures($value$serializer);
                    
$property->setValue($data$value);
                }
            }
        }
    }

    
/**
     * Calculates a signature for a closure's serialized data.
     *
     * @param string $data Serialized closure data.
     *
     * @return string Signature of the closure's data.
     */
    
private function calculateSignature($data)
    {
        return 
hash_hmac('sha256'$data$this->signingKeytrue);
    }

    
/**
     * Verifies the signature for a closure's serialized data.
     *
     * @param string $signature The provided signature of the data.
     * @param string $data      The data for which to verify the signature.
     *
     * @throws ClosureUnserializationException if the signature is invalid.
     */
    
private function verifySignature($signature$data)
    {
        
// Verify that the provided signature matches the calculated signature.
        
if (!hash_equals($signature$this->calculateSignature($data))) {
            throw new 
ClosureUnserializationException('The signature of the'
                
' closure's data is invalidwhich means the serialized '
                . '
closure has been modified and is unsafe to unserialize.'
            );
        }
    }
}
Онлайн: 1
Реклама