vendor/jackalope/jackalope/src/Jackalope/NodeType/NodeTypeManager.php line 113

Open in your IDE?
  1. <?php
  2. namespace Jackalope\NodeType;
  3. use Iterator;
  4. use IteratorAggregate;
  5. use ArrayIterator;
  6. use PHPCR\AccessDeniedException;
  7. use PHPCR\NamespaceException;
  8. use PHPCR\NamespaceRegistryInterface;
  9. use PHPCR\NodeType\NodeTypeInterface;
  10. use PHPCR\NodeType\NodeTypeDefinitionInterface;
  11. use PHPCR\NodeType\NodeTypeManagerInterface;
  12. use PHPCR\NodeType\NoSuchNodeTypeException;
  13. use PHPCR\NodeType\NodeTypeExistsException;
  14. use Jackalope\ObjectManager;
  15. use Jackalope\NotImplementedException;
  16. use Jackalope\FactoryInterface;
  17. use PHPCR\RepositoryException;
  18. /**
  19.  * {@inheritDoc}
  20.  *
  21.  * In Jackalope, we try to do lazy fetching of node types to reduce overhead.
  22.  * Jackalope supports registering node types, and when using the jackrabbit for
  23.  * transport, there is an additional method registerNodeTypesCnd for the
  24.  * jackrabbit specific textual node type specification.
  25.  *
  26.  * @license http://www.apache.org/licenses Apache License Version 2.0, January 2004
  27.  * @license http://opensource.org/licenses/MIT MIT License
  28.  */
  29. class NodeTypeManager implements IteratorAggregateNodeTypeManagerInterface
  30. {
  31.     /**
  32.      * The factory to instantiate objects.
  33.      *
  34.      * @var FactoryInterface
  35.      */
  36.     protected $factory;
  37.     /**
  38.      * @var ObjectManager
  39.      */
  40.     protected $objectManager;
  41.     /**
  42.      * @var NamespaceRegistryInterface
  43.      */
  44.     protected $namespaceRegistry;
  45.     /**
  46.      * Cache of already fetched primary node type instances.
  47.      *
  48.      * @var array
  49.      */
  50.     protected $primaryTypes;
  51.     /**
  52.      * Cache of already fetched mixin node type instances.
  53.      *
  54.      * @var array
  55.      */
  56.     protected $mixinTypes;
  57.     /**
  58.      * Array of arrays with the super type as key and its sub types as values.
  59.      * @var array
  60.      */
  61.     protected $nodeTree = [];
  62.     /**
  63.      * Flag to only load all node types from the backend once.
  64.      *
  65.      * Methods like hasNodeType() need to fetch all node types. Others like
  66.      * getNodeType() do not need all, but just the requested one. Unless we
  67.      * need all, we only load specific ones and cache them.
  68.      *
  69.      * @var boolean
  70.      */
  71.     protected $fetchedAllFromBackend false;
  72.     /**
  73.      * Create the node type manager for a session.
  74.      *
  75.      * There may be only one instance per session
  76.      * @param FactoryInterface  $factory
  77.      * @param ObjectManager     $objectManager
  78.      * @param NamespaceRegistryInterface $namespaceRegistry
  79.      */
  80.     public function __construct(
  81.         FactoryInterface $factory,
  82.         ObjectManager $objectManager,
  83.         NamespaceRegistryInterface $namespaceRegistry
  84.     ) {
  85.         $this->factory $factory;
  86.         $this->objectManager $objectManager;
  87.         $this->namespaceRegistry $namespaceRegistry;
  88.     }
  89.     /**
  90.      * Fetch a node type from backend.
  91.      *
  92.      * Without a filter parameter, this will fetch all node types from the backend.
  93.      *
  94.      * It is no problem to call this method with null as name, it will remember
  95.      * once it fetched all node types and do nothing after that.
  96.      *
  97.      * On fetch all, already cached node types are kept.
  98.      *
  99.      * @param string $name type name to fetch. defaults to null which will
  100.      *      fetch all nodes.
  101.      */
  102.     protected function fetchNodeTypes($name null)
  103.     {
  104.         if ($this->fetchedAllFromBackend) {
  105.             return;
  106.         }
  107.         if (null !== $name) {
  108.             if (!empty($this->primaryTypes[$name]) || !empty($this->mixinTypes[$name])) {
  109.                 return; //we already know this node
  110.             }
  111.             //OPTIMIZE: also avoid trying to fetch nonexisting definitions we already tried to get
  112.             $nodeTypes $this->objectManager->getNodeType($name);
  113.         } else {
  114.             $nodeTypes $this->objectManager->getNodeTypes();
  115.             $this->fetchedAllFromBackend true;
  116.         }
  117.         foreach ($nodeTypes as $nodeType) {
  118.             /** @var NodeType $nodeType */
  119.             $nodeType $this->factory->get(NodeType::class, [$this$nodeType]);
  120.             $name $nodeType->getName();
  121.             //do not overwrite existing types. maybe they where changed locally
  122.             if (empty($this->primaryTypes[$name]) && empty($this->mixinTypes[$name])) {
  123.                 $this->addNodeType($nodeType);
  124.             }
  125.         }
  126.     }
  127.     /**
  128.      * Stores the node type in our internal structures (flat && tree)
  129.      *
  130.      * @param NodeTypeInterface $nodeType The nodetype to add
  131.      */
  132.     protected function addNodeType(NodeTypeInterface $nodeType)
  133.     {
  134.         if ($nodeType->isMixin()) {
  135.             $this->mixinTypes[$nodeType->getName()] = $nodeType;
  136.         } else {
  137.             $this->primaryTypes[$nodeType->getName()] = $nodeType;
  138.         }
  139.         $this->addToNodeTree($nodeType);
  140.     }
  141.     /**
  142.      * Helper method for node types: Returns the declared subtypes of a given
  143.      * nodename.
  144.      *
  145.      * @param string $nodeTypeName
  146.      *
  147.      * @return array with the names of the subnode types pointing to the node type instances
  148.      *
  149.      * @see NodeType::getDeclaredSubtypes
  150.      *
  151.      * @private
  152.      */
  153.     public function getDeclaredSubtypes($nodeTypeName)
  154.     {
  155.         // OPTIMIZE: any way to avoid loading all nodes at this point?
  156.         $this->fetchNodeTypes();
  157.         if (empty($this->nodeTree[$nodeTypeName])) {
  158.             return [];
  159.         }
  160.         return $this->nodeTree[$nodeTypeName];
  161.     }
  162.     /**
  163.      * Helper method for NodeType: Returns all sub types of a node and their
  164.      * sub types.
  165.      *
  166.      * @param string $nodeTypeName
  167.      *
  168.      * @return array with the names of the subnode types pointing to the node type instances
  169.      *
  170.      * @see NodeType::getSubtypes
  171.      *
  172.      * @private
  173.      */
  174.     public function getSubtypes($nodeTypeName)
  175.     {
  176.         // OPTIMIZE: any way to avoid loading all nodes at this point?
  177.         $this->fetchNodeTypes();
  178.         $ret = [];
  179.         if (isset($this->nodeTree[$nodeTypeName])) {
  180.             foreach ($this->nodeTree[$nodeTypeName] as $name => $subnode) {
  181.                 $ret array_merge($ret, [$name => $subnode], $this->getSubtypes($name));
  182.             }
  183.         }
  184.         return $ret;
  185.     }
  186.     /**
  187.      * Adds the declared super types of a node type to the tree to be able to
  188.      * fetch the sub types of those super types later on.
  189.      *
  190.      * Part of addNodeType.
  191.      *
  192.      * @param NodeTypeInterface $nodetype the node type to add.
  193.      */
  194.     private function addToNodeTree($nodetype)
  195.     {
  196.         foreach ($nodetype->getDeclaredSupertypeNames() as $declaredSupertypeName) {
  197.             if (isset($this->nodeTree[$declaredSupertypeName])) {
  198.                 $this->nodeTree[$declaredSupertypeName] =
  199.                     array_merge(
  200.                         $this->nodeTree[$declaredSupertypeName],
  201.                         [$nodetype->getName() => $nodetype]
  202.                     );
  203.             } else {
  204.                 $this->nodeTree[$declaredSupertypeName] = [$nodetype->getName() => $nodetype];
  205.             }
  206.         }
  207.     }
  208.     /**
  209.      * {@inheritDoc}
  210.      *
  211.      * @api
  212.      */
  213.     public function getNodeType($nodeTypeName)
  214.     {
  215.         if (null === $nodeTypeName) {
  216.             throw new NoSuchNodeTypeException('nodeTypeName is <null>');
  217.         }
  218.         if ('' === $nodeTypeName) {
  219.             throw new NoSuchNodeTypeException('nodeTypeName is empty string');
  220.         }
  221.         if ($nodeTypeName[0] === '{') {
  222.             list($uri$name) = explode('}'substr($nodeTypeName1));
  223.             $prefix $this->namespaceRegistry->getPrefix($uri);
  224.             $nodeTypeName "$prefix:$name";
  225.         }
  226.         $this->fetchNodeTypes($nodeTypeName);
  227.         if (isset($this->primaryTypes[$nodeTypeName])) {
  228.             return $this->primaryTypes[$nodeTypeName];
  229.         }
  230.         if (isset($this->mixinTypes[$nodeTypeName])) {
  231.             return $this->mixinTypes[$nodeTypeName];
  232.         }
  233.         throw new NoSuchNodeTypeException($nodeTypeName);
  234.     }
  235.     /**
  236.      * {@inheritDoc}
  237.      *
  238.      * @api
  239.      */
  240.     public function hasNodeType($name)
  241.     {
  242.         try {
  243.             $this->fetchNodeTypes($name);
  244.         } catch (NoSuchNodeTypeException $e) {
  245.             // if we have not yet fetched all types and this type is not existing
  246.             // we get an exception. just ignore the exception, we don't have the type.
  247.             return false;
  248.         }
  249.         return isset($this->primaryTypes[$name]) || isset($this->mixinTypes[$name]);
  250.     }
  251.     /**
  252.      * {@inheritDoc}
  253.      *
  254.      * @api
  255.      */
  256.     public function getAllNodeTypes()
  257.     {
  258.         $this->fetchNodeTypes();
  259.         return new ArrayIterator(array_merge($this->primaryTypes$this->mixinTypes));
  260.     }
  261.     /**
  262.      * {@inheritDoc}
  263.      *
  264.      * @api
  265.      */
  266.     public function getPrimaryNodeTypes()
  267.     {
  268.         $this->fetchNodeTypes();
  269.         return new ArrayIterator($this->primaryTypes);
  270.     }
  271.     /**
  272.      * {@inheritDoc}
  273.      *
  274.      * @api
  275.      */
  276.     public function getMixinNodeTypes()
  277.     {
  278.         $this->fetchNodeTypes();
  279.         return new ArrayIterator($this->mixinTypes);
  280.     }
  281.     /**
  282.      * {@inheritDoc}
  283.      *
  284.      * @api
  285.      */
  286.     public function createNodeTypeTemplate($ntd null)
  287.     {
  288.         return $this->factory->get(NodeTypeTemplate::class, [$this$ntd]);
  289.     }
  290.     /**
  291.      * {@inheritDoc}
  292.      *
  293.      * @api
  294.      */
  295.     public function createNodeDefinitionTemplate()
  296.     {
  297.         return $this->factory->get(NodeDefinitionTemplate::class, [$this]);
  298.     }
  299.     /**
  300.      * {@inheritDoc}
  301.      *
  302.      * @api
  303.      */
  304.     public function createPropertyDefinitionTemplate()
  305.     {
  306.         return $this->factory->get(PropertyDefinitionTemplate::class, [$this]);
  307.     }
  308.     /**
  309.      * {@inheritDoc}
  310.      *
  311.      * @api
  312.      */
  313.     public function registerNodeType(NodeTypeDefinitionInterface $ntd$allowUpdate)
  314.     {
  315.         $this->registerNodeTypes([$ntd], $allowUpdate);
  316.         return $ntd;
  317.     }
  318.     /**
  319.      * Internally create a node type object
  320.      *
  321.      * @param NodeTypeDefinitionInterface $ntd
  322.      * @param bool                        $allowUpdate whether updating the definition is to be allowed or not
  323.      *
  324.      * @return NodeType the new node type
  325.      *
  326.      * @throws RepositoryException
  327.      * @throws NodeTypeExistsException
  328.      */
  329.     protected function createNodeType(NodeTypeDefinitionInterface $ntd$allowUpdate)
  330.     {
  331.         if ($this->hasNodeType($ntd->getName()) && !$allowUpdate) {
  332.             throw new NodeTypeExistsException('NodeType already existing: '.$ntd->getName());
  333.         }
  334.         return $this->factory->get(NodeType::class, [$this$ntd]);
  335.     }
  336.     /**
  337.      * {@inheritDoc}
  338.      *
  339.      * @api
  340.      */
  341.     public function registerNodeTypes(array $definitions$allowUpdate)
  342.     {
  343.         $nts = [];
  344.         // prepare them first (all or nothing)
  345.         foreach ($definitions as $definition) {
  346.             $nts[$definition->getName()] = $this->createNodeType($definition$allowUpdate);
  347.         }
  348.         $this->objectManager->registerNodeTypes($definitions$allowUpdate);
  349.         // no need to fetch the node types as with cnd, we already have the def and can
  350.         // now register them ourselves
  351.         foreach ($nts as $nt) {
  352.             $this->addNodeType($nt);
  353.         }
  354.         return new ArrayIterator($nts);
  355.     }
  356.     /**
  357.      * {@inheritDoc}
  358.      *
  359.      * @throws AccessDeniedException
  360.      * @throws NamespaceException
  361.      *
  362.      * @api
  363.      */
  364.     public function registerNodeTypesCnd($cnd$allowUpdate)
  365.     {
  366.         //set fetched from backend to false to allow to load the new types from backend
  367.         $fetched $this->fetchedAllFromBackend;
  368.         $this->fetchedAllFromBackend false;
  369.         $this->objectManager->registerNodeTypesCnd($cnd$allowUpdate);
  370.         //parse out type names and fetch types to return definitions of the new nodes
  371.         preg_match_all('/\[([^\]]*)\]/'$cnd$names);
  372.         $types = [];
  373.         foreach ($names[1] as $name) {
  374.             $types[$name] = $this->getNodeType($name);
  375.         }
  376.         $this->fetchedAllFromBackend $fetched;
  377.         return $types;
  378.     }
  379.     /**
  380.      * {@inheritDoc}
  381.      *
  382.      * @api
  383.      */
  384.     public function unregisterNodeType($name)
  385.     {
  386.         if (!empty($this->primaryTypes[$name])) {
  387.             unset($this->primaryTypes[$name]);
  388.         } elseif (!empty($this->mixinTypes[$name])) {
  389.             unset($this->mixinTypes[$name]);
  390.         } else {
  391.             throw new NoSuchNodeTypeException('NodeType not found: '.$name);
  392.         }
  393.         throw new NotImplementedException('TODO: remove from nodeTree and register with server (jackrabbit has not implemented this yet)');
  394.     }
  395.     /**
  396.      * {@inheritDoc}
  397.      *
  398.      * @api
  399.      */
  400.     public function unregisterNodeTypes(array $names)
  401.     {
  402.         foreach ($names as $name) {
  403.             $this->unregisterNodeType($name);
  404.         }
  405.     }
  406.     /**
  407.      * Provide Traversable interface: redirect to getAllNodeTypes
  408.      *
  409.      * @return Iterator over all node types
  410.      * @throws RepositoryException
  411.      */
  412.     #[\ReturnTypeWillChange]
  413.     public function getIterator(): \Traversable
  414.     {
  415.         return $this->getAllNodeTypes();
  416.     }
  417. }