vendor/jackalope/jackalope/src/Jackalope/NodeType/NodeType.php line 27

Open in your IDE?
  1. <?php
  2. namespace Jackalope\NodeType;
  3. use ArrayIterator;
  4. use Exception;
  5. use PHPCR\PropertyType;
  6. use PHPCR\RepositoryException;
  7. use PHPCR\ValueFormatException;
  8. use PHPCR\NodeType\NodeTypeInterface;
  9. use PHPCR\NodeType\ConstraintViolationException;
  10. use PHPCR\NodeType\NoSuchNodeTypeException;
  11. /**
  12.  * {@inheritDoc}
  13.  *
  14.  * In Jackalope, the only information stored and thus available at
  15.  * instantiation is the list of declared supertype names, child node type names
  16.  * and property definition instances acquired from the NodeTypeDefinition.
  17.  * All other information in this class is deduced from this when requested.
  18.  *
  19.  * @license http://www.apache.org/licenses Apache License Version 2.0, January 2004
  20.  * @license http://opensource.org/licenses/MIT MIT License
  21.  *
  22.  * @api
  23.  */
  24. class NodeType extends NodeTypeDefinition implements NodeTypeInterface
  25. {
  26.     /**
  27.      * Cache of the declared super NodeType instances so they need to be
  28.      * instantiated only once.
  29.      *
  30.      * @var array
  31.      */
  32.     protected $declaredSupertypes null;
  33.     /**
  34.      * Cache of the aggregated super node type names so they need to be
  35.      * aggregated only once.
  36.      * @var array
  37.      */
  38.     protected $superTypeNames null;
  39.     /**
  40.      * Cache of the aggregated super NodeType instances so they need to be
  41.      * instantiated only once.
  42.      *
  43.      * @var NodeTypeInterface[]
  44.      */
  45.     protected $superTypes null;
  46.     /**
  47.      * Cache of the collected property definitions so they need to be
  48.      * instantiated only once.
  49.      * @var array
  50.      */
  51.     protected $propertyDefinitions null;
  52.     /**
  53.      * Cache of the aggregated child node definitions from this type and all
  54.      * its super type so they need to be gathered and instantiated only once.
  55.      * @var array
  56.      */
  57.     protected $childNodeDefinitions null;
  58.     /**
  59.      * {@inheritDoc}
  60.      *
  61.      * @api
  62.      */
  63.     public function getSupertypes()
  64.     {
  65.         if (null === $this->superTypes) {
  66.             $this->superTypes = [];
  67.             foreach ($this->getDeclaredSupertypes() as $superType) {
  68.                 $this->superTypes[] = $superType;
  69.                 $this->superTypes array_merge($this->superTypes$superType->getSupertypes());
  70.             }
  71.         }
  72.         return $this->superTypes;
  73.     }
  74.     /**
  75.      * {@inheritDoc}
  76.      *
  77.      * @api
  78.      */
  79.     public function getSupertypeNames()
  80.     {
  81.         if (null === $this->superTypeNames) {
  82.             $this->superTypeNames = [];
  83.             foreach ($this->getSupertypes() as $superType) {
  84.                 $this->superTypeNames[] = $superType->getName();
  85.             }
  86.         }
  87.         return $this->superTypeNames;
  88.     }
  89.     /**
  90.      * {@inheritDoc}
  91.      *
  92.      * @api
  93.      */
  94.     public function getDeclaredSupertypes()
  95.     {
  96.         if (null === $this->declaredSupertypes) {
  97.             $this->declaredSupertypes = [];
  98.             foreach ($this->declaredSuperTypeNames as $declaredSuperTypeName) {
  99.                 $this->declaredSupertypes[] = $this->nodeTypeManager->getNodeType($declaredSuperTypeName);
  100.             }
  101.         }
  102.         return $this->declaredSupertypes;
  103.     }
  104.     /**
  105.      * {@inheritDoc}
  106.      *
  107.      * @api
  108.      */
  109.     public function getSubtypes()
  110.     {
  111.         return new ArrayIterator($this->nodeTypeManager->getSubtypes($this->name));
  112.     }
  113.     /**
  114.      * {@inheritDoc}
  115.      *
  116.      * @api
  117.      */
  118.     public function getDeclaredSubtypes()
  119.     {
  120.         return new ArrayIterator($this->nodeTypeManager->getDeclaredSubtypes($this->name));
  121.     }
  122.     /**
  123.      * {@inheritDoc}
  124.      *
  125.      * @api
  126.      */
  127.     public function isNodeType($nodeTypeName)
  128.     {
  129.         return $this->getName() === $nodeTypeName || in_array($nodeTypeName$this->getSupertypeNames());
  130.     }
  131.     /**
  132.      * {@inheritDoc}
  133.      *
  134.      * @api
  135.      */
  136.     public function getPropertyDefinitions()
  137.     {
  138.         if (null === $this->propertyDefinitions) {
  139.             $this->propertyDefinitions $this->getDeclaredPropertyDefinitions();
  140.             foreach ($this->getSupertypes() as $nodeType) {
  141.                 $this->propertyDefinitions array_merge($this->propertyDefinitions$nodeType->getDeclaredPropertyDefinitions());
  142.             }
  143.         }
  144.         return $this->propertyDefinitions;
  145.     }
  146.     /**
  147.      * {@inheritDoc}
  148.      *
  149.      * @api
  150.      */
  151.     public function getChildNodeDefinitions()
  152.     {
  153.         if (null === $this->childNodeDefinitions) {
  154.             $this->childNodeDefinitions $this->getDeclaredChildNodeDefinitions();
  155.             foreach ($this->getSupertypes() as $nodeType) {
  156.                 $this->childNodeDefinitions array_merge($this->childNodeDefinitions$nodeType->getDeclaredChildNodeDefinitions());
  157.             }
  158.         }
  159.         return $this->childNodeDefinitions;
  160.     }
  161.     /**
  162.      * {@inheritDoc}
  163.      *
  164.      * @throws ValueFormatException
  165.      * @throws ConstraintViolationException
  166.      * @throws \InvalidArgumentException
  167.      * @throws RepositoryException
  168.      *
  169.      * @api
  170.      */
  171.     public function canSetProperty($propertyName$value$throw false)
  172.     {
  173.         $propDefs $this->getPropertyDefinitions();
  174.         try {
  175.             $type $this->valueConverter->determineType($value);
  176.         } catch (ValueFormatException $e) {
  177.             if ($throw) {
  178.                 throw new ValueFormatException(sprintf('Invalid value for property "%s": %s'$propertyName$e->getMessage()), $e->getCode(), $e);
  179.             }
  180.             return false;
  181.         }
  182.         // check explicit matches first and keep wildcard definitions for later
  183.         $wildcards = [];
  184.         foreach ($propDefs as $prop) {
  185.             if ('*' === $prop->getName()) {
  186.                 $wildcards[] = $prop;
  187.             } elseif ($propertyName === $prop->getName()) {
  188.                 if (is_array($value) !== $prop->isMultiple()) {
  189.                     if ($prop->isMultiple()) {
  190.                         throw new ConstraintViolationException("The property definition is multivalued, but the value '$value' is not.");
  191.                     }
  192.                     if (is_array($value)) {
  193.                         throw new ConstraintViolationException('The value '.$this->getValueAsString($value).' is multivalued, but the property definition for ['.$this->getName()."]:$propertyName is not.");
  194.                     }
  195.                 }
  196.                 $requiredType $prop->getRequiredType();
  197.                 if (PropertyType::UNDEFINED === $requiredType || $type === $requiredType) {
  198.                     return true;
  199.                 }
  200.                 // try if we can convert. OPTIMIZE: would be nice to know without actually attempting to convert
  201.                 try {
  202.                     $this->valueConverter->convertType($value$prop->getRequiredType(), $type);
  203.                     return true;
  204.                 } catch (ValueFormatException $e) {
  205.                     // fall through and return false
  206.                 }
  207.                 if ($throw) {
  208.                     throw new ConstraintViolationException("The property '$propertyName' with value '$value' can't be converted to an existing type.");
  209.                 }
  210.                 return false// if there is an explicit match, it has to fit
  211.             }
  212.         }
  213.         // now check if any of the wildcards matches
  214.         /** @var $prop PropertyDefinition */
  215.         foreach ($wildcards as $prop) {
  216.             if (is_array($value) !== $prop->isMultiple()) {
  217.                 continue;
  218.             }
  219.             $requiredType $prop->getRequiredType();
  220.             if (PropertyType::UNDEFINED === $requiredType || $type === $requiredType) {
  221.                 return true;
  222.             }
  223.             // try if we can convert. OPTIMIZE: would be nice to know without actually attempting to convert
  224.             try {
  225.                 $this->valueConverter->convertType($value$prop->getRequiredType(), $type);
  226.                 return true;
  227.             } catch (ValueFormatException $e) {
  228.                 if ($throw) {
  229.                     throw new ValueFormatException($propertyName.': '.$e->getMessage(), $e->getCode(), $e);
  230.                 }
  231.                 return false// if there is an explicit match, it has to fit
  232.             }
  233.         }
  234.         if ($throw) {
  235.             $val $this->getValueAsString($value);
  236.             throw new ConstraintViolationException("Node type definition ".$this->getName()." does not allow to set the property with name '$propertyName' and value '$val'");
  237.         }
  238.         return false;
  239.     }
  240.     /**
  241.      * {@inheritDoc}
  242.      *
  243.      * @throws ConstraintViolationException
  244.      *
  245.      * @api
  246.      */
  247.     public function canAddChildNode($childNodeName$nodeTypeName null$throw false)
  248.     {
  249.         $childDefs $this->getChildNodeDefinitions();
  250.         if ($nodeTypeName) {
  251.             try {
  252.                 $nodeType $this->nodeTypeManager->getNodeType($nodeTypeName);
  253.                 if ($nodeType->isMixin() || $nodeType->isAbstract()) {
  254.                     if ($throw) {
  255.                         if ($nodeType->isMixin()) {
  256.                             $errorMsg "Can't add the child node '$childNodeName' for node type '$nodeTypeName' because the node type is mixin.";
  257.                         } else {
  258.                             $errorMsg "Can't add the child node '$childNodeName for node type '$nodeTypeName' because the node type is abstract.";
  259.                         }
  260.                         throw new ConstraintViolationException($errorMsg);
  261.                     }
  262.                     return false;
  263.                 }
  264.             } catch (NoSuchNodeTypeException $nsnte) {
  265.                 if ($throw) {
  266.                     throw $nsnte;
  267.                 }
  268.                 return false;
  269.             } catch (Exception $e) {
  270.                 if ($throw) {
  271.                     $errorMsg "Can't add the child node '$childNodeName' for node type '$nodeTypeName' because of an Exception: " $e->getMessage();
  272.                     throw new ConstraintViolationException($errorMsgnull$e);
  273.                 }
  274.                 return false;
  275.             }
  276.         }
  277.         foreach ($childDefs as $child) {
  278.             if ('*' === $child->getName() || $childNodeName === $child->getName()) {
  279.                 if ($nodeTypeName === null) {
  280.                     if ($child->getDefaultPrimaryTypeName() !== null) {
  281.                         return true;
  282.                     }
  283.                 } else {
  284.                     foreach ($child->getRequiredPrimaryTypeNames() as $type) {
  285.                         if ($nodeType->isNodeType($type)) {
  286.                             return true;
  287.                         }
  288.                     }
  289.                 }
  290.             }
  291.         }
  292.         if ($throw) {
  293.             $errorMsg "Can't add the child node '$childNodeName' for node type '$nodeTypeName' because there is no definition for a child with that name.";
  294.             throw new ConstraintViolationException($errorMsg);
  295.         }
  296.         return false;
  297.     }
  298.     /**
  299.      * {@inheritDoc}
  300.      *
  301.      * @throws ConstraintViolationException
  302.      *
  303.      * @api
  304.      */
  305.     public function canRemoveNode($nodeName$throw false)
  306.     {
  307.         $childDefs $this->getChildNodeDefinitions();
  308.         foreach ($childDefs as $child) {
  309.             if ($nodeName === $child->getName() &&
  310.                 ($child->isMandatory() || $child->isProtected())
  311.             ) {
  312.                 if ($throw) {
  313.                     if ($child->isMandatory()) {
  314.                         $errorMsg "Can't remove the mandatory childnode: " $child->getName();
  315.                     } else {
  316.                         $errorMsg "Can't remove the protected childnode: " $child->getName();
  317.                     }
  318.                     throw new ConstraintViolationException($errorMsg);
  319.                 }
  320.                 return false;
  321.             }
  322.         }
  323.         return true;
  324.     }
  325.     /**
  326.      * {@inheritDoc}
  327.      *
  328.      * @throws ConstraintViolationException
  329.      *
  330.      * @api
  331.      */
  332.     public function canRemoveProperty($propertyName$throw false)
  333.     {
  334.         $propDefs $this->getPropertyDefinitions();
  335.         foreach ($propDefs as $prop) {
  336.             if ($propertyName === $prop->getName() &&
  337.                 ($prop->isMandatory() || $prop->isProtected())
  338.             ) {
  339.                 if ($throw) {
  340.                     if ($prop->isMandatory()) {
  341.                         $errorMsg "Can't remove the mandatory property: " $prop->getName();
  342.                     } else {
  343.                         $errorMsg "Can't remove the protected property: " $prop->getName();
  344.                     }
  345.                     throw new ConstraintViolationException($errorMsg);
  346.                 }
  347.                 return false;
  348.             }
  349.         }
  350.         return true;
  351.     }
  352.     /**
  353.      * Get a string representation of the passed value for error reporting.
  354.      *
  355.      * @param mixed $value
  356.      *
  357.      * @return string
  358.      */
  359.     private function getValueAsString($value)
  360.     {
  361.         if (is_object($value)) {
  362.             return get_class($value);
  363.         }
  364.         if (is_scalar($value)) {
  365.             return (string) $value;
  366.         }
  367.         if (is_array($value)) {
  368.             return 'array of length ' count($value);
  369.         }
  370.         return gettype($value);
  371.     }
  372. }