+ /**
+ * @param string $origin
+ * @param string $target
+ * @param string $linkType Name of the link type, typically 'symbolic' or 'hard'
+ */
+ private function linkException($origin, $target, $linkType)
+ {
+ if (self::$lastError) {
+ if ('\\' === DIRECTORY_SEPARATOR && false !== strpos(self::$lastError, 'error code(1314)')) {
+ throw new IOException(sprintf('Unable to create %s link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target);
+ }
+ }
+ throw new IOException(sprintf('Failed to create %s link from "%s" to "%s".', $linkType, $origin, $target), 0, null, $target);
+ }
+
+ /**
+ * Resolves links in paths.
+ *
+ * With $canonicalize = false (default)
+ * - if $path does not exist or is not a link, returns null
+ * - if $path is a link, returns the next direct target of the link without considering the existence of the target
+ *
+ * With $canonicalize = true
+ * - if $path does not exist, returns null
+ * - if $path exists, returns its absolute fully resolved final version
+ *
+ * @param string $path A filesystem path
+ * @param bool $canonicalize Whether or not to return a canonicalized path
+ *
+ * @return string|null
+ */
+ public function readlink($path, $canonicalize = false)
+ {
+ if (!$canonicalize && !is_link($path)) {
+ return;
+ }
+
+ if ($canonicalize) {
+ if (!$this->exists($path)) {
+ return;
+ }
+
+ if ('\\' === DIRECTORY_SEPARATOR) {
+ $path = readlink($path);
+ }
+
+ return realpath($path);
+ }
+
+ if ('\\' === DIRECTORY_SEPARATOR) {
+ return realpath($path);
+ }
+
+ return readlink($path);
+ }
+