vendor/jackalope/jackalope/src/Jackalope/Session.php line 271
<?phpnamespace Jackalope;use PHPCR\Util\PathHelper;use Exception;use PHPCR\RepositoryInterface;use PHPCR\SessionInterface;use PHPCR\SimpleCredentials;use PHPCR\CredentialsInterface;use PHPCR\PathNotFoundException;use PHPCR\ItemNotFoundException;use PHPCR\ItemExistsException;use PHPCR\RepositoryException;use PHPCR\UnsupportedRepositoryOperationException;use InvalidArgumentException;use PHPCR\Security\AccessControlException;use Jackalope\ImportExport\ImportExport;use Jackalope\Transport\TransportInterface;use Jackalope\Transport\TransactionInterface;use Traversable;/*** {@inheritDoc}** Jackalope adds the SessionOption concept to handle session specific tweaking* and optimization. We distinguish between options that are purely* optimization but do not affect the behaviour and those that are change the* behaviour.** @license http://www.apache.org/licenses Apache License Version 2.0, January 2004* @license http://opensource.org/licenses/MIT MIT License** @api*/class Session implements SessionInterface{/*** Constant for setSessionOption to manage the fetch depth.** This option is used to set the depth with which nodes should be fetched from the backend to optimize* performance when you know you will need the child nodes.*/const OPTION_FETCH_DEPTH = 'jackalope.fetch_depth';/*** Constant for setSessionOption to manage whether nodes having mix:lastModified should automatically be updated.** Disable if you want to manually control this information, e.g. in a PHPCR-ODM listener.*/const OPTION_AUTO_LASTMODIFIED = 'jackalope.auto_lastmodified';/*** A registry for all created sessions to be able to reference them by id in* the stream wrapper for lazy loading binary properties.** Keys are spl_object_hash'es for the sessions which are the values** @var array*/protected static $sessionRegistry = [];/*** The factory to instantiate objects** @var FactoryInterface*/protected $factory;/*** @var Repository*/protected $repository;/*** @var Workspace*/protected $workspace;/*** @var ObjectManager*/protected $objectManager;/*** @var SimpleCredentials*/protected $credentials;/*** Whether this session is in logged out state and can not be used anymore** @var bool*/protected $logout = false;/*** The namespace registry.** It is only used to check prefixes and at setup. Session namespace remapping must be handled locally.** @var NamespaceRegistry*/protected $namespaceRegistry;/*** List of local namespaces** TODO: implement local namespace rewriting* see jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/PathParser.java and friends* for how this is done in jackrabbit*///protected $localNamespaces;/** Creates a session** Builds the corresponding workspace instance** @param FactoryInterface $factory the object factory* @param Repository $repository* @param string $workspaceName the workspace name that is used* @param SimpleCredentials $credentials the credentials that where* used to log in, in order to implement Session::getUserID()* if they are null, getUserID returns null* @param TransportInterface $transport the transport implementation*/public function __construct(FactoryInterface $factory, Repository $repository, $workspaceName, SimpleCredentials $credentials = null, TransportInterface $transport){$this->factory = $factory;$this->repository = $repository;$this->objectManager = $this->factory->get(ObjectManager::class, [$transport, $this]);$this->workspace = $this->factory->get(Workspace::class, [$this, $this->objectManager, $workspaceName]);$this->credentials = $credentials;$this->namespaceRegistry = $this->workspace->getNamespaceRegistry();self::registerSession($this);$transport->setNodeTypeManager($this->workspace->getNodeTypeManager());}/*** {@inheritDoc}** @api*/public function getRepository(){return $this->repository;}/*** {@inheritDoc}** @api*/public function getUserID(){if (null === $this->credentials) {return null;}return $this->credentials->getUserID(); //TODO: what if its not simple credentials? what about anonymous login?}/*** {@inheritDoc}** @api*/public function getAttributeNames(){if (null === $this->credentials) {return [];}return $this->credentials->getAttributeNames();}/*** {@inheritDoc}** @api*/public function getAttribute($name){if (null === $this->credentials) {return null;}return $this->credentials->getAttribute($name);}/*** {@inheritDoc}** @api*/public function getWorkspace(){return $this->workspace;}/*** {@inheritDoc}** @api*/public function getRootNode(){return $this->getNode('/');}/*** {@inheritDoc}** @api*/public function impersonate(CredentialsInterface $credentials){throw new UnsupportedRepositoryOperationException('Not supported');}/*** {@inheritDoc}** @api*/public function getNodeByIdentifier($id){return $this->objectManager->getNodeByIdentifier($id);}/*** {@inheritDoc}** @api*/public function getNodesByIdentifier($ids){if (! is_array($ids) && ! $ids instanceof Traversable) {$hint = is_object($ids) ? get_class($ids) : gettype($ids);throw new InvalidArgumentException("Not a valid array or Traversable: $hint");}return $this->objectManager->getNodesByIdentifier($ids);}/*** {@inheritDoc}** @api*/public function getItem($absPath){if (! is_string($absPath) || strlen($absPath) === 0 || '/' !== $absPath[0]) {throw new PathNotFoundException('It is forbidden to call getItem on session with a relative path');}if ($this->nodeExists($absPath)) {return $this->getNode($absPath);}return $this->getProperty($absPath);}/*** {@inheritDoc}** @api*/public function getNode($absPath, $depthHint = -1){if (-1 !== $depthHint) {$depth = $this->getSessionOption(self::OPTION_FETCH_DEPTH);$this->setSessionOption(self::OPTION_FETCH_DEPTH, $depthHint);}try {$node = $this->objectManager->getNodeByPath($absPath);if (isset($depth)) {$this->setSessionOption(self::OPTION_FETCH_DEPTH, $depth);}return $node;} catch (ItemNotFoundException $e) {if (isset($depth)) {$this->setSessionOption(self::OPTION_FETCH_DEPTH, $depth);}throw new PathNotFoundException($e->getMessage(), $e->getCode(), $e);}}/*** {@inheritDoc}** @api*/public function getNodes($absPaths){if (! is_array($absPaths) && ! $absPaths instanceof Traversable) {$hint = is_object($absPaths) ? get_class($absPaths) : gettype($absPaths);throw new InvalidArgumentException("Not a valid array or Traversable: $hint");}return $this->objectManager->getNodesByPath($absPaths);}/*** {@inheritDoc}** @api*/public function getProperty($absPath){try {return $this->objectManager->getPropertyByPath($absPath);} catch (ItemNotFoundException $e) {throw new PathNotFoundException($e->getMessage(), $e->getCode(), $e);}}public function getProperties($absPaths){if (! is_array($absPaths) && ! $absPaths instanceof Traversable) {$hint = is_object($absPaths) ? get_class($absPaths) : gettype($absPaths);throw new InvalidArgumentException("Not a valid array or Traversable: $hint");}return $this->objectManager->getPropertiesByPath($absPaths);}/*** {@inheritDoc}** @api*/public function itemExists($absPath){if ($absPath === '/') {return true;}return $this->nodeExists($absPath) || $this->propertyExists($absPath);}/*** {@inheritDoc}** @api*/public function nodeExists($absPath){if ($absPath === '/') {return true;}try {//OPTIMIZE: avoid throwing and catching errors would improve performance if many node exists calls are made//would need to communicate to the lower layer that we do not want exceptions$this->objectManager->getNodeByPath($absPath);} catch (ItemNotFoundException $e) {return false;}return true;}/*** {@inheritDoc}** @api*/public function propertyExists($absPath){try {//OPTIMIZE: avoid throwing and catching errors would improve performance if many node exists calls are made//would need to communicate to the lower layer that we do not want exceptions$this->getProperty($absPath);} catch (PathNotFoundException $e) {return false;}return true;}/*** {@inheritDoc}** @api*/public function move($srcAbsPath, $destAbsPath){try {$parent = $this->objectManager->getNodeByPath(PathHelper::getParentPath($destAbsPath));} catch (ItemNotFoundException $e) {throw new PathNotFoundException("Target path can not be found: $destAbsPath", $e->getCode(), $e);}if ($parent->hasNode(PathHelper::getNodeName($destAbsPath))) {// TODO same-name siblingsthrow new ItemExistsException('Target node already exists at '.$destAbsPath);}if ($parent->hasProperty(PathHelper::getNodeName($destAbsPath))) {throw new ItemExistsException('Target property already exists at '.$destAbsPath);}$this->objectManager->moveNode($srcAbsPath, $destAbsPath);}/*** {@inheritDoc}** @api*/public function removeItem($absPath){$item = $this->getItem($absPath);$item->remove();}/*** {@inheritDoc}** Wraps the save operation into a transaction if transactions are enabled* but we are not currently inside a transaction and rolls back on error.** If transactions are disabled, errors on save can lead to partial saves* and inconsistent data.** @api*/public function save(){if ($this->getTransport() instanceof TransactionInterface) {try {$utx = $this->workspace->getTransactionManager();} catch (UnsupportedRepositoryOperationException $e) {// user transactions where disabled for this session, do no automatic transaction.}}if (isset($utx) && !$utx->inTransaction()) {// do the operation in a short transaction$utx->begin();try {$this->objectManager->save();$utx->commit();} catch (Exception $e) {// if anything goes wrong, rollback this messtry {$utx->rollback();} catch (Exception $rollbackException) {// ignore this exception}// but do not eat this exceptionthrow $e;}} else {$this->objectManager->save();}}/*** {@inheritDoc}** @api*/public function refresh($keepChanges){$this->objectManager->refresh($keepChanges);}/*** Jackalope specific hack to drop the state of the current session** Removes all cached objects, planned changes etc without making the* objects aware of it. Was done as a cheap replacement for refresh* in testing.** @deprecated: this will screw up major, as the user of the api can still have references to nodes. USE refresh instead!*/public function clear(){trigger_error('Use Session::refresh instead, this method is extremely unsafe', E_USER_DEPRECATED);$this->objectManager->clear();}/*** {@inheritDoc}** @api*/public function hasPendingChanges(){return $this->objectManager->hasPendingChanges();}/*** {@inheritDoc}** @api*/public function hasPermission($absPath, $actions){$actualPermissions = $this->objectManager->getPermissions($absPath);$requestedPermissions = explode(',', $actions);foreach ($requestedPermissions as $perm) {if (! in_array(strtolower(trim($perm)), $actualPermissions)) {return false;}}return true;}/*** {@inheritDoc}** @api*/public function checkPermission($absPath, $actions){if (! $this->hasPermission($absPath, $actions)) {throw new AccessControlException($absPath);}}/*** {@inheritDoc}** Jackalope does currently not check anything and always return true.** @api*/public function hasCapability($methodName, $target, array $arguments){//we never determine whether operation can be performed as it is optional ;-)//TODO: could implement somereturn true;}/*** {@inheritDoc}** @api*/public function importXML($parentAbsPath, $uri, $uuidBehavior){ImportExport::importXML($this->getNode($parentAbsPath),$this->workspace->getNamespaceRegistry(),$uri,$uuidBehavior);}/*** {@inheritDoc}** @api*/public function exportSystemView($absPath, $stream, $skipBinary, $noRecurse){ImportExport::exportSystemView($this->getNode($absPath),$this->workspace->getNamespaceRegistry(),$stream,$skipBinary,$noRecurse);}/*** {@inheritDoc}** @api*/public function exportDocumentView($absPath, $stream, $skipBinary, $noRecurse){ImportExport::exportDocumentView($this->getNode($absPath),$this->workspace->getNamespaceRegistry(),$stream,$skipBinary,$noRecurse);}/*** {@inheritDoc}** @api*/public function setNamespacePrefix($prefix, $uri){$this->namespaceRegistry->checkPrefix($prefix);throw new NotImplementedException('TODO: implement session scope remapping of namespaces');//this will lead to rewrite all names and paths in requests and replies. part of this can be done in ObjectManager::normalizePath}/*** {@inheritDoc}** @api*/public function getNamespacePrefixes(){//TODO: once setNamespacePrefix is implemented, must take session remaps into accountreturn $this->namespaceRegistry->getPrefixes();}/*** {@inheritDoc}** @api*/public function getNamespaceURI($prefix){//TODO: once setNamespacePrefix is implemented, must take session remaps into accountreturn $this->namespaceRegistry->getURI($prefix);}/*** {@inheritDoc}** @api*/public function getNamespacePrefix($uri){//TODO: once setNamespacePrefix is implemented, must take session remaps into accountreturn $this->namespaceRegistry->getPrefix($uri);}/*** {@inheritDoc}** @api*/public function logout(){//OPTIMIZATION: flush object manager to help garbage collector$this->logout = true;if ($this->getRepository()->getDescriptor(RepositoryInterface::OPTION_LOCKING_SUPPORTED)) {$this->getWorkspace()->getLockManager()->logout();}self::unregisterSession($this);$this->getTransport()->logout();}/*** {@inheritDoc}** @api*/public function isLive(){return ! $this->logout;}/*** {@inheritDoc}** @api*/public function getAccessControlManager(){throw new UnsupportedRepositoryOperationException();}/*** {@inheritDoc}** @api*/public function getRetentionManager(){throw new UnsupportedRepositoryOperationException();}/*** Implementation specific: The object manager is also used by other components, i.e. the QueryManager.** @return ObjectManager the object manager associated with this session** @private*/public function getObjectManager(){return $this->objectManager;}/*** Implementation specific: The transport implementation is also used by other components,* i.e. the NamespaceRegistry** @return TransportInterface the transport implementation associated with* this session.** @private*/public function getTransport(){return $this->objectManager->getTransport();}/*** Implementation specific: register session in session registry for the stream wrapper.** @param Session $session the session to register** @private*/protected static function registerSession(Session $session){$key = $session->getRegistryKey();self::$sessionRegistry[$key] = $session;}/*** Implementation specific: unregister session in session registry on logout.** @param Session $session the session to unregister** @private*/protected static function unregisterSession(Session $session){$key = $session->getRegistryKey();unset(self::$sessionRegistry[$key]);}/*** Implementation specific: create an id for the session registry so that the stream wrapper can identify it.** @private** @return string an id for this session*/public function getRegistryKey(){return spl_object_hash($this);}/*** Implementation specific: get a session from the session registry for the stream wrapper.** @param string $key key for the session** @return Session|null the session or null if none is registered with the given key** @private*/public static function getSessionFromRegistry($key){if (isset(self::$sessionRegistry[$key])) {return self::$sessionRegistry[$key];}return null;}/*** Sets a session specific option.** @param string $key the key to be set* @param mixed $value the value to be set** @throws InvalidArgumentException if the option is unknown* @throws RepositoryException if this option is not supported and is* a behaviour relevant option** @see BaseTransport::setFetchDepth($value);*/public function setSessionOption($key, $value){switch ($key) {case self::OPTION_FETCH_DEPTH:$this->getTransport()->setFetchDepth($value);break;case self::OPTION_AUTO_LASTMODIFIED:$this->getTransport()->setAutoLastModified($value);break;default:throw new InvalidArgumentException("Unknown option: $key");}}/*** Gets a session specific option.** @param string $key the key to be gotten** @return bool** @throws InvalidArgumentException if the option is unknown** @see setSessionOption($key, $value);*/public function getSessionOption($key){switch ($key) {case self::OPTION_FETCH_DEPTH:return $this->getTransport()->getFetchDepth();case self::OPTION_AUTO_LASTMODIFIED:return $this->getTransport()->getAutoLastModified();}throw new InvalidArgumentException("Unknown option: $key");}}