Skip to content

Commit

Permalink
working well compiled navigator
Browse files Browse the repository at this point in the history
  • Loading branch information
goetas committed Apr 27, 2018
1 parent 2cf7e74 commit 43d3350
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 12 deletions.
5 changes: 5 additions & 0 deletions src/Exclusion/DepthExclusionStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,9 @@ private function isTooDeep(Context $context):bool

return false;
}

public function getSignature(): ?string
{
return null;
}
}
21 changes: 21 additions & 0 deletions src/Exclusion/DisjunctExclusionStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
class DisjunctExclusionStrategy implements ExclusionStrategyInterface
{
private $delegates = array();
private $signature = false;
/**
* @param ExclusionStrategyInterface[] $delegates
*/
Expand All @@ -45,6 +46,7 @@ public function __construct(array $delegates = array())
public function addStrategy(ExclusionStrategyInterface $strategy):void
{
$this->delegates[] = $strategy;
$this->signature = false;
}

/**
Expand Down Expand Up @@ -84,4 +86,23 @@ public function shouldSkipProperty(PropertyMetadata $property, Context $context)

return false;
}

public function getSignature(): ?string
{
if ($this->signature !== false) {
return $this->signature;
}
$signatures = array();
foreach ($this->delegates as $delegate) {
$signature = $delegate->getSignature();
if ($signature === null) {
$this->signature = null;
return $this->signature;
}
$signatures[] = $signature;
}

$this->signature = implode(", ", $signatures);
return $this->signature;
}
}
8 changes: 8 additions & 0 deletions src/Exclusion/ExclusionStrategyInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,12 @@ public function shouldSkipClass(ClassMetadata $metadata, Context $context):bool;
* @return boolean
*/
public function shouldSkipProperty(PropertyMetadata $property, Context $context):bool;

/**
* Return a signature (used for caching) for this exclusion strategy.
* If the exclusion strategy can not be cached, should return NULL.
*
* @return null|string
*/
public function getSignature(): ?string;
}
11 changes: 11 additions & 0 deletions src/Exclusion/GroupsExclusionStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,15 @@ private function getGroupsFor(Context $navigatorContext)

return $groups;
}


public function getSignature(): ?string
{
foreach ($this->groups as $v) {
if(is_array($v)) {
return null;
}
}
return serialize($this->groups);
}
}
5 changes: 5 additions & 0 deletions src/Exclusion/VersionExclusionStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,9 @@ public function shouldSkipProperty(PropertyMetadata $property, Context $navigato

return false;
}

public function getSignature(): ?string
{
return $this->version;
}
}
6 changes: 1 addition & 5 deletions src/Handler/HandlerRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,6 @@ public function registerHandler(int $direction, string $typeName, string $format

public function getHandler(int $direction, string $typeName, string $format)
{
if (!isset($this->handlers[$direction][$typeName][$format])) {
return null;
}

return $this->handlers[$direction][$typeName][$format];
return $this->handlers[$direction][$typeName][$format] ?? null;
}
}
1 change: 1 addition & 0 deletions src/Metadata/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class ClassMetadata extends MergeableClassMetadata
public $postDeserializeMethods = array();

public $xmlRootName;
public $compiled;
public $xmlRootNamespace;
public $xmlNamespaces = array();
public $accessorOrder;
Expand Down
3 changes: 1 addition & 2 deletions src/SerializationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,8 @@ public function stopVisiting($object): void
return;
}
$this->visitingSet->detach($object);
$poppedObject = $this->visitingStack->pop();

if ($object !== $poppedObject) {
if ($object !== $this->visitingStack->pop()) {
throw new RuntimeException('Context visitingStack not working well');
}
}
Expand Down
165 changes: 162 additions & 3 deletions src/SerializationGraphNavigator.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,25 @@
namespace JMS\Serializer;

use JMS\Serializer\Accessor\AccessorStrategyInterface;
use JMS\Serializer\Annotation\VirtualProperty;
use JMS\Serializer\Construction\ObjectConstructorInterface;
use JMS\Serializer\EventDispatcher\EventDispatcher;
use JMS\Serializer\EventDispatcher\EventDispatcherInterface;
use JMS\Serializer\EventDispatcher\ObjectEvent;
use JMS\Serializer\EventDispatcher\PreDeserializeEvent;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;
use JMS\Serializer\Exception\CircularReferenceDetectedException;
use JMS\Serializer\Exception\ExcludedClassException;
use JMS\Serializer\Exception\ExpressionLanguageRequiredException;
use JMS\Serializer\Exception\InvalidArgumentException;
use JMS\Serializer\Exception\NotAcceptableException;
use JMS\Serializer\Exception\RuntimeException;
use JMS\Serializer\Exclusion\ExclusionStrategyInterface;
use JMS\Serializer\Exclusion\ExpressionLanguageExclusionStrategy;
use JMS\Serializer\Expression\ExpressionEvaluatorInterface;
use JMS\Serializer\Handler\HandlerRegistryInterface;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
use JMS\Serializer\Metadata\StaticPropertyMetadata;
use Metadata\MetadataFactoryInterface;

/**
Expand Down Expand Up @@ -68,8 +71,7 @@ public function __construct(
AccessorStrategyInterface $accessor,
EventDispatcherInterface $dispatcher = null,
ExpressionEvaluatorInterface $expressionEvaluator = null
)
{
) {
$this->dispatcher = $dispatcher ?: new EventDispatcher();
$this->metadataFactory = $metadataFactory;
$this->handlerRegistry = $handlerRegistry;
Expand Down Expand Up @@ -188,10 +190,23 @@ public function accept($data, array $type = null, Context $context)
/** @var $metadata ClassMetadata */
$metadata = $this->metadataFactory->getMetadataForClass($type['name']);

$key = $metadata->name . $exclusionStrategy->getSignature() .$format . GraphNavigatorInterface::DIRECTION_SERIALIZATION.$shouldSerializeNull;

if (!empty($metadata->compiled[$key])) {
return $metadata->compiled[$key]->accept($data, $visitor, $context);
}

if ($metadata->usingExpression && $this->expressionExclusionStrategy === null) {
throw new ExpressionLanguageRequiredException("To use conditional exclude/expose in {$metadata->name} you must configure the expression language.");
}

if (!$metadata->usingExpression && $exclusionStrategy->getSignature() !== null) {

$metadata->compiled[$key] = $compiledNavigator = $this->createCompiledHandler($key, $this->accessor, $exclusionStrategy, $metadata, $context, $format, $type, $shouldSerializeNull);

return $compiledNavigator->accept($data, $visitor, $context);
}

if ($exclusionStrategy->shouldSkipClass($metadata, $context)) {
$context->stopVisiting($data);

Expand All @@ -205,6 +220,7 @@ public function accept($data, array $type = null, Context $context)
}

$visitor->startVisitingObject($metadata, $data, $type, $context);

foreach ($metadata->propertyMetadata as $propertyMetadata) {
if ($exclusionStrategy->shouldSkipProperty($propertyMetadata, $context)) {
continue;
Expand Down Expand Up @@ -244,4 +260,147 @@ private function afterVisitingObject(ClassMetadata $metadata, $object, array $ty
$this->dispatcher->dispatch('serializer.post_serialize', $metadata->name, $format, new ObjectEvent($context, $object, $type));
}
}

protected $cache = array();

private function createCompiledHandler(
$key,
AccessorStrategyInterface $accessorStrategy,
ExclusionStrategyInterface $exclusionStrategy,
ClassMetadata $metadata, Context $context, $format, $type, $shouldSerializeNull)
{



if (!isset($this->cache[$key])) {

$identity = md5($key);

$vapart = "JMS\\__CC__\\Id" . $identity;

$cls = "$vapart\\Navigator";

if (!class_exists($cls, false)) {
$str = "namespace $vapart;\n";
$str .= "class Navigator\n{\n";
$str .= "\tprotected \$propertyMetadata;\n";
$str .= "\tprotected \$metadata;\n";
$str .= "\tprotected \$accessor;\n";

foreach ($metadata->propertyMetadata as $k => $propertyMetadata) {
if ($exclusionStrategy->shouldSkipProperty($propertyMetadata, $context)) {
continue;
}
$str .= "\tprotected \$v_$propertyMetadata->name;\n";

$str .= "\n";
}


$str .= "public function __construct(\JMS\Serializer\Metadata\ClassMetadata \$metadata, \$accessor)\n{\n";
$str .= "\t\$this->propertyMetadata = \$metadata->propertyMetadata;\n";
$str .= "\t\$this->metadata = \$metadata;\n";
$str .= "\t\$this->accessor = \$accessor;\n";

foreach ($metadata->propertyMetadata as $k => $propertyMetadata) {
if ($exclusionStrategy->shouldSkipProperty($propertyMetadata, $context)) {
continue;
}

$str .= "\t\$this->v_$propertyMetadata->name = \Closure::bind(function (\$o) {
return \$o->$propertyMetadata->name;
}, null, ".var_export($propertyMetadata->class, true).");\n";

$str .= "\n";
}



$str .= "\n}\n";
$str .= "public function accept(\$data, \$visitor, \\JMS\\Serializer\\Context \$context)\n{\n";

if ($exclusionStrategy->shouldSkipClass($metadata, $context)) {
$str .= "\t\$context->stopVisiting(\$data);\n";
$str .= "\tthrow new \JMS\Serializer\Exception\ExcludedClassException();\n";
} else {

$str .= "\n\$context->pushClassMetadata(\$this->metadata);\n";

if ($metadata->preSerializeMethods) {
$str .= "\nforeach (\$this->metadata->preSerializeMethods as \$method) {\n";
$str .= "\n\$method->invoke(\$data);\n";
$str .= "\n}\n";
}

$str .= "\n\$visitor->startVisitingObject(\$this->metadata, \$data, " . var_export($type, true) . ", \$context);\n";

foreach ($metadata->propertyMetadata as $k => $propertyMetadata) {
if ($exclusionStrategy->shouldSkipProperty($propertyMetadata, $context)) {
continue;
}

$str .= "\t\$m = \$this->propertyMetadata['$k'];\n";


if ($propertyMetadata->getter) {
$str .= "\t\$v = \$data->$propertyMetadata->getter();\n";
} elseif (!($propertyMetadata instanceof ExpressionPropertyMetadata) && !($propertyMetadata instanceof VirtualProperty) && !($propertyMetadata instanceof StaticPropertyMetadata)) {
$str .= "\t\$v = (\$this->v_$propertyMetadata->name)(\$data);\n";
} else {
$str .= "\t\$v = \$this->accessor->getValue(\$data, \$m);\n";
}



if (!$shouldSerializeNull){
$str .= "if(\$v !== null){\n";
}

$str .= "\t\$context->pushPropertyMetadata(\$m);\n";
$str .= "\t\$visitor->visitProperty(\$m, \$v, \$context);\n";
$str .= "\t\$context->popPropertyMetadata();\n";

if (!$shouldSerializeNull){
$str .= "\t}\n";
}

$str .= "\n";
}

$str .= "\n\$context->stopVisiting(\$data);\n";
$str .= "\n\$context->popClassMetadata();\n";

if ($metadata->postSerializeMethods) {
$str .= "\nforeach (\$this->metadata->postSerializeMethods as \$method) {\n";
$str .= "\n\$method->invoke(\$data);\n";
$str .= "\n}\n";
}

if ($this->dispatcher->hasListeners('serializer.post_serialize', $metadata->name, $format)) {
$str .= "\n\$this->dispatcher->dispatch('serializer.post_serialize',
\$this->metadata->name, " . var_export($format, true) . ", new \JMS\Serializer\EventDispatcher\ObjectEvent(\$context, \$data, " . var_export($type, true) . ")
);\n";
}

$str .= "\nreturn \$visitor->endVisitingObject(\$this->metadata, \$data, " . var_export($type, true) . ", \$context);\n";
}

$str .= "\n}\n";


$str .= "\n}\n";
$name = sys_get_temp_dir() . "/$identity";
file_put_contents($name, "<?php $str");
require $name;

}

$this->cache[$key] = new $cls($metadata, $accessorStrategy);
}
return $this->cache[$key];
}
}




Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@

class AlwaysExcludeExclusionStrategy implements ExclusionStrategyInterface
{
public function shouldSkipClass(ClassMetadata $metadata, Context $context):bool
public function shouldSkipClass(ClassMetadata $metadata, Context $context): bool
{
return true;
}

public function shouldSkipProperty(PropertyMetadata $property, Context $context):bool
public function shouldSkipProperty(PropertyMetadata $property, Context $context): bool
{
return false;
}

public function getSignature(): ?string
{
return self::class;
}
}

0 comments on commit 43d3350

Please sign in to comment.