Skip to content

Instantly share code, notes, and snippets.

@ohader
Created May 26, 2016 20:14
Show Gist options
  • Save ohader/72f437972eed4d69280693b82ed1b82c to your computer and use it in GitHub Desktop.
Save ohader/72f437972eed4d69280693b82ed1b82c to your computer and use it in GitHub Desktop.
<?php
class Utility
{
/**
* @param string $data
* @param array $allowedClasses
* @param bool $extend
* @return mixed
*/
static public function deserialize(string $data, array $allowedClasses, $extend = false)
{
$additionalClasses = [];
$result = unserialize($data, ['allowed_classes' => $allowedClasses]);
if (!$extend) {
return $result;
}
$json = json_encode($result);
if (
strpos($json, '__PHP_Incomplete_Class_Name') !== false
&& preg_match_all('#"__PHP_Incomplete_Class_Name":"([^\\"]+)"#', $json, $matches)
) {
foreach ($matches[1] as $index => $checkClass) {
if (static::extensible($allowedClasses, $checkClass)) {
$additionalClasses[] = $checkClass;
}
}
}
if (!empty($additionalClasses)) {
$allowedClasses = array_merge($allowedClasses, $additionalClasses);
$result = unserialize($data, ['allowed_classes' => $allowedClasses]);
}
return $result;
}
/**
* @param array $classes
* @param $checkClass
* @return bool
*/
static protected function extensible(array $classes, string $checkClass): bool
{
foreach ($classes as $class) {
if (is_a($checkClass, $class, true)) {
return true;
}
}
return false;
}
}
interface InterfaceClass
{
}
abstract class AbstractClass
{
protected $value = '';
public function show() { var_dump($this->value); }
}
class AA extends AbstractClass
{
}
class BB extends AA implements InterfaceClass
{
}
class CC extends BB
{
protected $value = 'cc';
}
$array = [
'cc' => new CC()
];
$s = serialize($array);
# $u = Utility::deserialize($s, [InterfaceClass::class], false);
$u = Utility::deserialize($s, [InterfaceClass::class], true);
# $u = unserialize($s, ['allowed_classes' => [InterfaceClass::class]]);
var_dump($u);
@AndreasA
Copy link

AndreasA commented May 28, 2016

Instead of is_a one could also use is_subclass_of here as we only check for subclasses.
However, that is only relevant regarding which method is faster.

However, there is the possibility of a different approach to get the PHP incomplete classname:

    static public function deserialize(string $data, array $allowedClasses, $extend = false)
    {
        $additionalClasses = [];
        $result = unserialize($data, ['allowed_classes' => $allowedClasses]);
        if (!$extend) {
            return $result;
        }

        if ($result instanceof __PHP_Incomplete_Class) {
            $checkClassArray = new \ArrayObject($result);
            if (isset($checkClassArray['__PHP_Incomplete_Class_Name']) && static::extensible($allowedClasses, $checkClassArray['__PHP_Incomplete_Class_Name'])) {
                $additionalClasses[] = $checkClassArray['__PHP_Incomplete_Class_Name'];
            }
        } elseif (is_array($result)) {
            foreach ($result as $checkClassObject) {
                if ($checkClassObject instanceof __PHP_Incomplete_Class) {
                    $checkClassArray = new \ArrayObject($checkClassObject);
                    if (isset($checkClassArray['__PHP_Incomplete_Class_Name']) && static::extensible($allowedClasses, $checkClassArray['__PHP_Incomplete_Class_Name'])) {
                        $additionalClasses[] = $checkClassArray['__PHP_Incomplete_Class_Name'];
                    }
                }
            }  
        }

        if (!empty($additionalClasses)) {
            $allowedClasses = array_merge($allowedClasses, $additionalClasses);
            $result = unserialize($data, ['allowed_classes' => $allowedClasses]);
        }
        return $result;
    }

Not sure which approach is faster.

However, this approach would only work for one array level.
Whereas the other should work for multiple levels.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment