Chris@0: parts = self::prepareName($name); Chris@0: } Chris@0: Chris@0: public function getSubNodeNames() { Chris@0: return array('parts'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the first part of the name, i.e. everything before the first namespace separator. Chris@0: * Chris@0: * @return string First part of the name Chris@0: */ Chris@0: public function getFirst() { Chris@0: return $this->parts[0]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the last part of the name, i.e. everything after the last namespace separator. Chris@0: * Chris@0: * @return string Last part of the name Chris@0: */ Chris@0: public function getLast() { Chris@0: return $this->parts[count($this->parts) - 1]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks whether the name is unqualified. (E.g. Name) Chris@0: * Chris@0: * @return bool Whether the name is unqualified Chris@0: */ Chris@0: public function isUnqualified() { Chris@0: return 1 == count($this->parts); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks whether the name is qualified. (E.g. Name\Name) Chris@0: * Chris@0: * @return bool Whether the name is qualified Chris@0: */ Chris@0: public function isQualified() { Chris@0: return 1 < count($this->parts); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks whether the name is fully qualified. (E.g. \Name) Chris@0: * Chris@0: * @return bool Whether the name is fully qualified Chris@0: */ Chris@0: public function isFullyQualified() { Chris@0: return false; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name) Chris@0: * Chris@0: * @return bool Whether the name is relative Chris@0: */ Chris@0: public function isRelative() { Chris@0: return false; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns a string representation of the name by imploding the namespace parts with the Chris@0: * namespace separator. Chris@0: * Chris@0: * @return string String representation Chris@0: */ Chris@0: public function toString() { Chris@0: return implode('\\', $this->parts); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns a string representation of the name by imploding the namespace parts with the Chris@0: * namespace separator. Chris@0: * Chris@0: * @return string String representation Chris@0: */ Chris@0: public function __toString() { Chris@0: return implode('\\', $this->parts); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets a slice of a name (similar to array_slice). Chris@0: * Chris@0: * This method returns a new instance of the same type as the original and with the same Chris@0: * attributes. Chris@0: * Chris@0: * If the slice is empty, null is returned. The null value will be correctly handled in Chris@0: * concatenations using concat(). Chris@0: * Chris@0: * Offset and length have the same meaning as in array_slice(). Chris@0: * Chris@0: * @param int $offset Offset to start the slice at (may be negative) Chris@0: * @param int|null $length Length of the slice (may be negative) Chris@0: * Chris@0: * @return static|null Sliced name Chris@0: */ Chris@0: public function slice($offset, $length = null) { Chris@0: $numParts = count($this->parts); Chris@0: Chris@0: $realOffset = $offset < 0 ? $offset + $numParts : $offset; Chris@0: if ($realOffset < 0 || $realOffset > $numParts) { Chris@0: throw new \OutOfBoundsException(sprintf('Offset %d is out of bounds', $offset)); Chris@0: } Chris@0: Chris@0: if (null === $length) { Chris@0: $realLength = $numParts - $realOffset; Chris@0: } else { Chris@0: $realLength = $length < 0 ? $length + $numParts - $realOffset : $length; Chris@0: if ($realLength < 0 || $realLength > $numParts) { Chris@0: throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length)); Chris@0: } Chris@0: } Chris@0: Chris@0: if ($realLength === 0) { Chris@0: // Empty slice is represented as null Chris@0: return null; Chris@0: } Chris@0: Chris@0: return new static(array_slice($this->parts, $realOffset, $realLength), $this->attributes); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Concatenate two names, yielding a new Name instance. Chris@0: * Chris@0: * The type of the generated instance depends on which class this method is called on, for Chris@0: * example Name\FullyQualified::concat() will yield a Name\FullyQualified instance. Chris@0: * Chris@0: * If one of the arguments is null, a new instance of the other name will be returned. If both Chris@0: * arguments are null, null will be returned. As such, writing Chris@0: * Name::concat($namespace, $shortName) Chris@0: * where $namespace is a Name node or null will work as expected. Chris@0: * Chris@0: * @param string|array|self|null $name1 The first name Chris@0: * @param string|array|self|null $name2 The second name Chris@0: * @param array $attributes Attributes to assign to concatenated name Chris@0: * Chris@0: * @return static|null Concatenated name Chris@0: */ Chris@0: public static function concat($name1, $name2, array $attributes = []) { Chris@0: if (null === $name1 && null === $name2) { Chris@0: return null; Chris@0: } elseif (null === $name1) { Chris@0: return new static(self::prepareName($name2), $attributes); Chris@0: } else if (null === $name2) { Chris@0: return new static(self::prepareName($name1), $attributes); Chris@0: } else { Chris@0: return new static( Chris@0: array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes Chris@0: ); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Prepares a (string, array or Name node) name for use in name changing methods by converting Chris@0: * it to an array. Chris@0: * Chris@0: * @param string|array|self $name Name to prepare Chris@0: * Chris@0: * @return array Prepared name Chris@0: */ Chris@0: private static function prepareName($name) { Chris@0: if (\is_string($name)) { Chris@0: return explode('\\', $name); Chris@0: } elseif (\is_array($name)) { Chris@0: return $name; Chris@0: } elseif ($name instanceof self) { Chris@0: return $name->parts; Chris@0: } Chris@0: Chris@0: throw new \InvalidArgumentException( Chris@0: 'Expected string, array of parts or Name instance' Chris@0: ); Chris@0: } Chris@0: }