/** * Create a reader instance from a stream-wrapped source * * @param string $src Stream-wrapped source * @param string $resourceClass Resource class name * @param array $parameters Reader parameters * @return AbstractResource Resource instance * @throws InvalidReaderArgumentException If an invalid reader stream wrapper is given */ protected static function fromSource($src, $resourceClass, ...$parameters) { $reader = Tools::reader($src, $parameters); if ($reader instanceof ReaderInterface) { return Kernel::create($resourceClass, [$reader]); } throw new InvalidArgumentException('Invalid reader stream wrapper', InvalidReaderArgumentException::INVALID_READER_STREAM_WRAPPER); }
/** * String serialization * * @return string String value (file content) */ public function __toString() { /** @var InMemoryWriter $writer */ $writer = Kernel::create(InMemoryWriter::class); /** @var AbstractResource $this */ $this->dump($writer); return $writer->getData(); }
/** * Create and return a FrontMark resource instance from an object * * @param ObjectInterface $object Object * @return ResourceInterface Object resource */ public static function createFromObject(ObjectInterface $object) { /** @var ResourceInterface $resource */ $resource = Kernel::create(Resource::class, [null]); $resource->setPropertyData($object->getPropertyData()); $resource->setPayload($object->getPayload()); return $resource; }
/** * Instantiate and return an object * * @param string $url Object URL (relative or absolute including the apparat base URL) * @param int $visibility Object visibility * @return ObjectInterface Object */ public static function load($url, $visibility = ObjectTypes::VISIBILITY_ALL) { // Instantiate the object URL /** @var ObjectUrl $objectUrl */ $objectUrl = Kernel::create(ObjectUrl::class, [$url, true]); // Instantiate the local object repository, load and return the object return Repository::instance($objectUrl->getRepositoryUrl())->loadObject($objectUrl, $visibility); }
/** * Match a value against this datatype * * @param mixed $value Value * @return mixed Matched and processed value * @throws DomainException If the value is not a valid Geo URI */ public function match($value) { try { $geo = Kernel::create(GeoUri::class, [$value]); } catch (\Exception $e) { throw new DomainException(); } return $geo; }
/** * Match a value against this datatype * * @param mixed $value Value * @return mixed Matched and processed value * @throws DomainException If the value is not a valid URL */ public function match($value) { try { $url = Kernel::create(\Apparat\Object\Domain\Model\Uri\Url::class, [$value, true]); } catch (\Exception $e) { throw new DomainException(); } return $url; }
/** * Create and return an apparat object decorator * * @param ObjectInterface $object Object * @return ApparatObjectInterface Apparat object */ public static function create(ObjectInterface $object) { $objectType = $object->getObjectType()->getType(); // If the object type doesn't map to known apparat object class if (!array_key_exists($objectType, static::$typeClasses) || !static::$typeClasses[$objectType]) { throw new PortsInvalidArgumentException(sprintf('Unknown apparat object type "%s"', $objectType), PortsInvalidArgumentException::UNKNOWN_APPARAT_OBJECT_TYPE); } /** @var ApparatObjectInterface $apparatObject */ $apparatObject = Kernel::create(self::$typeClasses[$objectType], [$object]); return $apparatObject; }
/** * Auto-connect a repository with URL default settings * * @param string $url Repository URL * @return boolean Success */ public function connect($url) { // If it's an absolute URL /** @var Url $url */ $url = Kernel::create(Url::class, [$url]); $config = $url->isAbsolute() ? $this->getAbsoluteUrlConfig() : $this->getRelativeUrlConfig($url); // If a repository configuration has been created if ($config !== null) { $repository = Repository::register(strval($url), $config); return $repository instanceof DomainRepository; } return true; }
/** * Match a value against this datatype * * @param mixed $value Value * @return mixed Matched and processed value * @throws DomainException If the value is not a valid URL */ public function match($value) { try { /** @var \Apparat\Object\Domain\Model\Uri\ApparatUrl $apparatUrl */ $apparatUrl = Kernel::create(\Apparat\Object\Domain\Model\Uri\ApparatUrl::class, [$value, true, $this->object->getRepositoryLocator()->getRepository()]); // If the apparat URL needs to be filtered if (count($this->filter) && !in_array($apparatUrl->getObjectType()->getType(), $this->filter)) { throw new DomainException(); } } catch (DomainException $e) { throw new \InvalidArgumentException(); } catch (\Exception $e) { throw new DomainException(); } return $apparatUrl; }
/** * Create and return a new object * * @param RepositoryInterface $repository Repository to create the object in * @param Type $type Object type * @param string $payload Object payload * @param array $propertyData Object property data * @param \DateTimeInterface $creationDate Object creation date * @return ObjectInterface Object */ public function createObject(RepositoryInterface $repository, Type $type, $payload = '', array $propertyData = [], \DateTimeInterface $creationDate = null) { // Set the creation date to now if empty if ($creationDate === null) { $creationDate = new \DateTimeImmutable('now'); } // Construct a creation closure $creationClosure = function (Id $uid) use($repository, $type, $payload, $propertyData, $creationDate) { /** @var Revision $revision */ $revision = Kernel::create(Revision::class, [1, true]); /** @var RepositoryLocator $repositoryLocator */ $repositoryLocator = Kernel::create(RepositoryLocator::class, [$repository]); $repositoryLocator = $repositoryLocator->setId($uid); $repositoryLocator = $repositoryLocator->setRevision($revision); $repositoryLocator = $repositoryLocator->setObjectType($type); $repositoryLocator = $repositoryLocator->setCreationDate($creationDate); return ObjectFactory::createFromParams($repositoryLocator, $payload, $propertyData); }; // Wrap the object creation in an ID allocation transaction return $repository->getAdapterStrategy()->createObjectResource($creationClosure); }
/** * Use a specific object revision * * @param Revision $revision Revision to be used * @return ObjectInterface Object * @throws OutOfBoundsException If a revision beyond the latest one is requested */ public function useRevision(Revision $revision) { // If a revision beyond the latest one is requested if (!$revision->isCurrent() && $revision->getRevision() > $this->latestRevision->getRevision()) { throw new OutOfBoundsException(sprintf('Invalid object revision "%s"', $revision->getRevision()), OutOfBoundsException::INVALID_OBJECT_REVISION); } // Determine whether the current revision was requested $currentRevision = $this->getCurrentRevision(); $isCurrentRevision = $revision->isCurrent() || $currentRevision->equals($revision); if ($isCurrentRevision) { $revision = $currentRevision; } // If the requested revision is not already used if (!$this->getRevision()->equals($revision)) { /** @var ManagerInterface $objectManager */ $objectManager = Kernel::create(Service::class)->getObjectManager(); /** @var Revision $newRevision */ $newRevision = $isCurrentRevision ? Revision::current() : $revision; /** @var RepositoryLocator $newRevisionLocator */ $newRevisionLocator = $this->locator->setRevision($newRevision); // Instantiate the requested revision resource $revisionResource = $objectManager->loadObjectResource($newRevisionLocator, SelectorInterface::ALL); // Load the revision resource data $this->loadRevisionData($revisionResource->getPayload(), $revisionResource->getPropertyData()); // Update the object locator $this->updateLocator(); } return $this; }
/** * Load an object from this repository * * @param string $locator Object locator * @param int $visibility Object visibility * @return ApparatObjectInterface Object */ public function loadObject($locator, $visibility = SelectorInterface::ALL) { /** @var LocatorInterface $objectLocator */ $objectLocator = Kernel::create(RepositoryLocator::class, [$this->repository, $locator]); return ApparatObjectFactory::create($this->repository->loadObject($objectLocator, $visibility)); }
/** * Create and return a new object * * @param RepositoryLocatorInterface $locator Repository object locator * @param string $payload Object payload * @param array $propertyData Object property data * @return ObjectInterface Object */ public static function createFromParams(RepositoryLocatorInterface $locator, $payload = '', array $propertyData = []) { // Determine the object class $objectClass = self::objectClassFromType($locator->getObjectType()); // Prepare the system properties collection $systemPropertyData = empty($propertyData[SystemProperties::COLLECTION]) || !is_array($propertyData[SystemProperties::COLLECTION]) ? [] : $propertyData[SystemProperties::COLLECTION]; $systemPropertyData[SystemProperties::PROPERTY_ID] = $locator->getId()->getId(); $systemPropertyData[SystemProperties::PROPERTY_TYPE] = $locator->getObjectType()->getType(); $systemPropertyData[SystemProperties::PROPERTY_REVISION] = $locator->getRevision()->getRevision(); $systemPropertyData[SystemProperties::PROPERTY_CREATED] = $systemPropertyData[SystemProperties::PROPERTY_MODIFIED] = $locator->getCreationDate(); if (empty($systemPropertyData[SystemProperties::PROPERTY_LANGUAGE])) { $systemPropertyData[SystemProperties::PROPERTY_LANGUAGE] = getenv('OBJECT_DEFAULT_LANGUAGE'); } $propertyData[SystemProperties::COLLECTION] = $systemPropertyData; // Prepare the meta properties collection $metaPropertyData = empty($propertyData[MetaProperties::COLLECTION]) || !is_array($propertyData[MetaProperties::COLLECTION]) ? [] : $propertyData[MetaProperties::COLLECTION]; $metaPropertyData[MetaProperties::PROPERTY_PRIVACY] = getenv('OBJECT_DEFAULT_PRIVACY'); $propertyData[MetaProperties::COLLECTION] = $metaPropertyData; // Instantiate the object /** @var ObjectInterface $object */ $object = Kernel::create($objectClass, [$locator, '', $propertyData]); return $object->setPayload($payload); }
/** * Instantiate an object selector from a list of parameters * * @param array $params Object selector parameters * @return SelectorInterface Object selector */ public static function createFromParams(array $params) { $datePrecision = intval(getenv('OBJECT_DATE_PRECISION')); // Object visibility $visibility = empty($params['visibility']) ? SelectorInterface::VISIBLE : ($params['visibility'] == SelectorInterface::INDICATOR_HIDDEN ? SelectorInterface::HIDDEN : SelectorInterface::ALL); // Object draft $draft = empty($params['draft']) ? SelectorInterface::PUBLISHED : ($params['draft'] == SelectorInterface::INDICATOR_DRAFT ? SelectorInterface::DRAFT : SelectorInterface::ALL); $year = $month = $day = $hour = $minute = $second = null; if ($datePrecision > 0) { $year = isset($params['year']) ? self::castInt($params['year']) : SelectorInterface::WILDCARD; } if ($datePrecision > 1) { $month = isset($params['month']) ? self::castInt($params['month']) : SelectorInterface::WILDCARD; } if ($datePrecision > 2) { $day = isset($params['day']) ? self::castInt($params['day']) : SelectorInterface::WILDCARD; } if ($datePrecision > 3) { $hour = isset($params['hour']) ? self::castInt($params['hour']) : SelectorInterface::WILDCARD; } if ($datePrecision > 4) { $minute = isset($params['minute']) ? self::castInt($params['minute']) : SelectorInterface::WILDCARD; } if ($datePrecision > 5) { $second = isset($params['second']) ? self::castInt($params['second']) : SelectorInterface::WILDCARD; } $uid = isset($params['id']) ? self::castInt($params['id']) : SelectorInterface::WILDCARD; $type = empty($params['type']) ? SelectorInterface::WILDCARD : trim($params['type']); $revision = empty($params['revision']) ? Revision::CURRENT : self::castInt($params['revision']); return Kernel::create(Selector::class, [$year, $month, $day, $hour, $minute, $second, $uid, $type, $revision, $visibility, $draft]); }
/** * Process the payload of an object * * @param string $payload Payload * @return string Processed payload */ public function processPayload($payload) { // Reset all relevant relations $this->resetRefersToRelations(); $this->resetEmbedsRelations(); $env = Environment::createCommonMarkEnvironment(); $env->addDocumentProcessor($this); // Parse and process the object payload /** @var DocParser $docParser */ $docParser = Kernel::create(DocParser::class, [$env]); $docParser->parse($payload); return $payload; }
/** * Instantiate and return an adapter strategy * * @param array $config Adapter strategy config * @return AdapterStrategyInterface Repository adapter * @throws InvalidArgumentException If the adapter strategy config is empty * @throws InvalidArgumentException If the adapter strategy type is missing or invalid */ public function createFromConfig(array $config) { // If the adapter strategy config is empty if (!count($config)) { throw new InvalidArgumentException('Empty adapter strategy configuration', InvalidArgumentException::EMPTY_ADAPTER_STRATEGY_CONFIG); } // If the adapter strategy type is missing or invalid if (empty($config['type']) || !array_key_exists($config['type'], self::$types)) { throw new InvalidArgumentException(sprintf('Invalid adapter strategy type "%s"', empty($config['type']) ? '(empty)' : $config['type']), InvalidArgumentException::INVALID_ADAPTER_STRATEGY_TYPE); } // Instantiate the adapter strategy return Kernel::create(self::$types[$config['type']], [$config]); }
/** * Return the enclosed remote object * * @return ObjectInterface Remote object */ protected function object() { // Lazy-load the remote object if necessary if (!$this->object instanceof ObjectInterface) { // Instantiate the local object repository, load and return the object $this->object = Kernel::create(Service::class)->get($this->getUrl())->loadObject($this->getUrl()->getLocator()); } return $this->object; }
/** * Extract the title and abstract out of the payload * * @param string $markdownPayload Markdown payload * @return array Titel and abstract */ protected function extractTitleAndAbstract($markdownPayload) { $abstract = trim($markdownPayload); if (preg_match('%^(.+?)\\R%', $markdownPayload, $firstParagraph)) { $abstract = trim($firstParagraph[1]); } // Strip formatting if (strlen($abstract)) { $environment = Environment::createCommonMarkEnvironment(); /** @var CommonMarkConverter $converter */ $converter = Kernel::create(CommonMarkConverter::class, [[], $environment]); $abstract = trim(strip_tags($converter->convertToHtml($abstract))); } $title = $abstract; return [$title, $abstract]; }
/** * Load an object from this repository * * @param LocatorInterface $locator Object locator * @param int $visibility Object visibility * @return ObjectInterface Object */ public function loadObject(LocatorInterface $locator, $visibility = SelectorInterface::ALL) { /** @var ManagerInterface $objectManager */ $objectManager = Kernel::create(Service::class)->getObjectManager(); /** @var RepositoryLocatorInterface $repositoryLocator */ $repositoryLocator = Kernel::create(RepositoryLocator::class, [$this, $locator]); // Load and return the object return $objectManager->loadObject($repositoryLocator, $visibility); }
/** * Rewind to the first revision */ public function rewind() { $this->nextRevision = $this->latestRevision->getRevision() > 1 ? Kernel::create(Revision::class, [1]) : $this->latestRevision; $this->useRevision($this->nextRevision); }
/** * Traverse the property tree and return a node * * @param array $propertyPath Property name path * @param array $propertyTree Copy of the current property tree * @param boolean $created Property has been created * @param PropertyModel $propertyModel Property model * @return mixed Property node * @throws DomainException If an invalid subproperty should be allocated */ protected function &findPropertyNode(array $propertyPath, array &$propertyTree, &$created, PropertyModel &$propertyModel = null) { $propertyModel = null; $propertyModelName = 'pm'; $propertyPathSteps = []; $data =& $propertyTree; // Run through all sub-properties foreach ($propertyPath as $property) { // If an invalid sub-property should be allocated if ($propertyModel !== null) { throw new DomainException(sprintf('Property data model of "%s" does not allow sub-properties', implode(self::PROPERTY_TRAVERSAL_SEPARATOR, $propertyPathSteps)), DomainException::INVALID_DOMAIN_SUBPROPERTY); } $propertyPathSteps[] = $property; $propertyModelName .= ucfirst($property); /** @var PropertyModel $propertyModel */ $propertyModel = isset($this->{$propertyModelName}) ? Kernel::create(PropertyModel::class, array_merge([$this->object], $this->{$propertyModelName})) : null; // If the sub-property doesn't exist if (!array_key_exists($property, $data)) { $data[$property] = $propertyModel === null || $propertyModel->isMultivalue() ? [] : null; $created = true; } $data =& $data[$property]; } return $data; }
/** * Parse and instantiate a relation URL * * @param string $url URL * @param int $coupling Strong coupling * @param RepositoryInterface $contextRepository Context repository * @return Url URL * @throws InvalidArgumentException If the relation URL is invalid */ protected static function parseRelationUrl($url, &$coupling, RepositoryInterface $contextRepository) { if (strlen($url)) { // If the URL requires tight coupling if (!strncmp('!', $url, 1)) { $coupling = RelationInterface::TIGHT_COUPLING; $url = substr($url, 1); } // Try to instantiate as an apparat URL try { return Kernel::create(ApparatUrl::class, [$url, true, $contextRepository]); // If there's an apparat URL problem: Try to instantiate as a regular URL } catch (\Apparat\Object\Domain\Model\Uri\InvalidArgumentException $e) { /** @var Url $urlInstance */ $urlInstance = Kernel::create(Url::class, [$url]); if ($urlInstance->isAbsolute()) { return $urlInstance; } } } throw new InvalidArgumentException(sprintf('Invalid relation URL "%s"', $url), InvalidArgumentException::INVALID_RELATION_URL); }
/** * Apparat URL constructor * * If the constructor does not throw an exception, the URL is valid and * * 1. either an absolute URL (local or remote) or * 2. a relative URL to a known local repository (respectively the to the context repository if given) * * @param string $url Apparat URL * @param boolean $remote Accept remote URL (less strict date component checking) * @param RepositoryInterface $contextRepository Context repository * @throws ApparatInvalidArgumentException If the URL is absolute but doesn't have the apparat scheme * @throws ApparatInvalidArgumentException If this is a local Apparat URL with an unknown repository */ public function __construct($url, $remote = false, RepositoryInterface $contextRepository = null) { parent::__construct($url, $remote); // If it's an absolute URL if ($this->isAbsolute()) { // If the Apparat URL scheme is invalid if (!array_key_exists($this->urlParts['scheme'], self::$schemes)) { throw new ApparatInvalidArgumentException(sprintf('Invalid absolute apparat URL "%s"', $url), ApparatInvalidArgumentException::INVALID_ABSOLUTE_APPARAT_URL); } return; } // If this URL doesn't have a repository URL and a context repository is given: Inherit its URL if (!strlen($this->getPath()) && $contextRepository instanceof RepositoryInterface) { $this->urlParts['path'] = $contextRepository->getUrl(); } // If the the repository involved is unknown and cannot be auto-connected if (!Kernel::create(Service::class)->isRegistered($this->getPath())) { throw new ApparatInvalidArgumentException(sprintf('Unknown local repository URL "%s"', $this->getPath()), ApparatInvalidArgumentException::UNKNOWN_LOCAL_REPOSITORY_URL); } }
/** * Unserialize the string representation of this property * * @param string $str Serialized property * @return SerializablePropertyInterface Property */ public static function unserialize($str) { return Kernel::create(static::class, [$str]); }
/** * Translate data to a CommonMark resource part * * @param string $data Part data * @return CommonMarkPart CommonMark resource part */ public function hydrate($data) { return Kernel::create(CommonMarkPart::class, [$this, $data]); }
/** * Delete a resource * * @param string $src Stream-wrapped source * @param array ...$parameters Reader parameters * @return Move move handler * @throws \Apparat\Resource\Ports\InvalidArgumentException If the reader stream wrapper is invalid * @api */ public static function delete($src, ...$parameters) { $reader = self::reader($src, $parameters); if ($reader instanceof ReaderInterface) { /** @var Delete $deleter */ $deleter = Kernel::create(Delete::class, [$reader]); return $deleter(); } throw self::failInvalidReader(); }
/** * Build a multipart hydrator * * @param array $config Hydrator configuration * @return HydratorInterface Hydrator */ protected static function buildMultipart(array $config) { // If no multipart hydrator is specified if (count($config) < 2) { throw new InvalidArgumentException('A multipart hydrator must be specified', InvalidArgumentException::MISSING_MULTIPART_HYDRATOR); // Else: if the multipart hydrator is invalid } elseif (!strlen(trim($config[1])) || !(new \ReflectionClass(trim($config[1])))->isSubclassOf(AbstractMultipartHydrator::class)) { throw new InvalidArgumentException(sprintf('Invalid multipart hydrator class "%s"', trim($config[1])), InvalidArgumentException::INVALID_MULTIPART_HYDRATOR_CLASS); // Else: Validate the remaining hydrator arguments } elseif (count($config) > 2 && !call_user_func_array(array($config[1], 'validateParameters'), array_slice($config, 2))) { throw new InvalidArgumentException('Invalid multipart hydrator parameters', InvalidArgumentException::INVALID_MULTIPART_HYDRATOR_PARAMETERS); } // Run through all multipart subhydrators foreach ($config[0] as $multipartHydrator) { // If it's neither a multipart nor a valid single part hydrator if (!is_array($multipartHydrator) && !(new \ReflectionClass($multipartHydrator))->implementsInterface(HydratorInterface::class)) { throw new InvalidArgumentException(sprintf('Invalid multipart subhydrator class "%s"', $multipartHydrator), InvalidArgumentException::INVALID_MULTIPART_SUBHYDRATOR_CLASS); } } // Instantiate the multipart hydrator $multipartHydrator = trim($config[1]); $hydratorParameters = array_slice($config, 2); array_unshift($hydratorParameters, $config[0]); return Kernel::create($multipartHydrator, $hydratorParameters); }
/** * Set the contents of a part * * @param string $data Contents * @param array $subparts Subparts * @return AbstractContentPart New content part */ public function set($data, array $subparts = []) { unset($subparts); $class = get_class($this); return Kernel::create($class, [$this->hydrator, $data]); }
/** * Initialize the aggregate part * * @param string $data Part data * @return AbstractPartAggregate Part aggregate */ public function hydrate($data) { // If the part aggregate class isn't valid if (!$this->aggregateClass || !class_exists($this->aggregateClass) || !(new \ReflectionClass($this->aggregateClass))->implementsInterface(PartAggregateInterface::class)) { throw new RuntimeException(sprintf('Invalid part aggregate class "%s"', $this->aggregateClass), RuntimeException::INVALID_PART_AGGREGATE_CLASS); } unset($data); return Kernel::create($this->aggregateClass, [$this, $this->subhydrators, $this->minimumOccurrences, $this->maximumOccurrences]); }
/** * Translate data to a YAML resource part * * @param string $data Part data * @return YamlPart YAML resource part */ public function hydrate($data) { return Kernel::create(YamlPart::class, [$this, $data]); }