From 25db0e85a6bc5fc8d1e80ea6984568d8df531ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Folgado?= Date: Tue, 5 Jun 2012 10:02:52 +0300 Subject: [PATCH] Added support for fetching remote files in "@import" (supports HTTP and HTTPS). --- lessc.inc.php | 105 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 7 deletions(-) diff --git a/lessc.inc.php b/lessc.inc.php index ed4744fb..6c98aa34 100644 --- a/lessc.inc.php +++ b/lessc.inc.php @@ -65,6 +65,16 @@ class lessc { // attempts to find the path of an import url, returns null for css files function findImport($url) { + if ($this->isRemoteFile($url)) { + // The import is a remote file. Let's try to just open it to check for its existence. + $handle = @fopen($url, 'rb'); + if ($handle) { + fclose($handle); + return $url; + } + return null; + } + foreach ((array)$this->importDir as $dir) { $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url; if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) { @@ -1362,13 +1372,21 @@ function throwError($msg = null) { */ function __construct($fname = null, $opts = null) { if ($fname) { - if (!is_file($fname)) { + $this->fileName = $fname; + if ($this->isRemoteFile($fname)) { + $rc = $this->fetchRemoteFile($fname); + if (is_null($rc)) { + throw new Exception('load error: failed to read remote file '.$fname); + } + $this->buffer = $rc; + } elseif (is_file($fname)) { + $pi = pathinfo($fname); + $this->importDir = $pi['dirname'].'/'; + $this->buffer = file_get_contents($fname); + } else { throw new Exception('load error: failed to find '.$fname); } - $pi = pathinfo($fname); - - $this->fileName = $fname; - $this->importDir = $pi['dirname'].'/'; + $this->addParsedFile($fname); } } @@ -1383,7 +1401,11 @@ public function unregisterFunction($name) { public function allParsedFiles() { return $this->allParsedFiles; } protected function addParsedFile($file) { - $this->allParsedFiles[realpath($file)] = filemtime($file); + if ($this->isRemoteFile($file)) { + $this->allParsedFiles[$file] = $this->getRemoteFileLastModified($file); + } else { + $this->allParsedFiles[realpath($file)] = filemtime($file); + } } @@ -1464,6 +1486,76 @@ public static function cexecute($in, $force = false) { } } + + + /** + * Gets the modification time of a remote $url. + * Based on: http://www.php.net/manual/en/function.filemtime.php#81194 + * @param type $url + * @return The last modified time of the $url file, in Unix timestamp, or null if it can't be read. + */ + function getRemoteFileLastModified($url) { + // default + $unixtime = 0; + + $fp = @fopen($url, 'rb'); + if (!$fp) { + return null; + } + + $metadata = stream_get_meta_data($fp); + foreach ($metadata['wrapper_data'] as $response) { + // case: redirection + if (substr(strtolower($response), 0, 10) == 'location: ') { + $newUri = substr($response, 10); + fclose($fp); + return $this->getRemoteFileLastModified($newUri); + } + // case: last-modified + elseif (substr(strtolower($response), 0, 15) == 'last-modified: ') { + $unixtime = strtotime(substr($response, 15)); + break; + } + } + + fclose($fp); + return $unixtime; + } + + /** + * Gets the contents of a remote file + * @param string $url + * @return string The contents of the $url file of null if it can't be read. + */ + function fetchRemoteFile($url) { + $handle = @fopen($url, 'rb'); + if ($handle) { + $content = stream_get_contents($handle); + fclose($handle); + return $content; + } + + return null; + } + + /** + * Checks if a string represents a remote file + * @param string $url + * @return boolean If $target is a handable remote resource. + */ + function isRemoteFile($url) { + // Patterns for matching readable remote resources. + // Make sure that any included pattern will be accepted by fopen() as well. + $remotePatterns = array( + '/^https?:\/\//i' + ); + foreach ($remotePatterns as $pattern) { + if (preg_match($pattern, $url)) { + return true; + } + } + return false; + } static protected $cssColors = array( 'aliceblue' => '240,248,255', @@ -2757,7 +2849,6 @@ protected function removeComments($text) { return $out.$text; } - } class lessc_formatter {