diff --git a/lib/Less/Environment.php b/lib/Less/Environment.php index b92df617..7bf10999 100644 --- a/lib/Less/Environment.php +++ b/lib/Less/Environment.php @@ -50,6 +50,8 @@ class Less_Environment { public $strictMath = false; + public $importCallback = null; + /** * @var array */ @@ -115,6 +117,21 @@ public static function isPathRelative( $path ) { return !preg_match( '/^(?:[a-z-]+:|\/|#)/', $path ); } + /** + * Apply legacy 'import_callback' option. + * + * See Less_Parser::$default_options to learn more about the 'import_callback' option. + * This option is deprecated in favour of Less_Parser::SetImportDirs. + * + * @param Less_Tree_Import $importNode + * @return array{0:string,1:string|null}|null Array containing path and (optional) uri or null + */ + public function callImportCallback( Less_Tree_Import $importNode ) { + if ( is_callable( $this->importCallback ) ) { + return ( $this->importCallback )( $importNode ); + } + } + /** * Canonicalize a path by resolving references to '/./', '/../' * Does not remove leading "../" diff --git a/lib/Less/ImportVisitor.php b/lib/Less/ImportVisitor.php index 215ef06e..06da8dbd 100644 --- a/lib/Less/ImportVisitor.php +++ b/lib/Less/ImportVisitor.php @@ -94,8 +94,7 @@ public function processImportNode( $importNode, $env, &$importParent ) { // import dirs resolution logic. // TODO: We might need upstream's `tryAppendLessExtension` logic here. // We currenlty do do this in getPath instead. - $callback = Less_Parser::$options['import_callback']; - $path_and_uri = is_callable( $callback ) ? $callback( $importNode ) : null; + $path_and_uri = $env->callImportCallback( $importNode ); if ( !$path_and_uri ) { $path_and_uri = $importNode->PathAndUri(); } diff --git a/lib/Less/Parser.php b/lib/Less/Parser.php index c17dfb93..2345b3f9 100644 --- a/lib/Less/Parser.php +++ b/lib/Less/Parser.php @@ -18,6 +18,29 @@ class Less_Parser { 'numPrecision' => 8, 'import_dirs' => [], + + // Override how imported file names are resolved. + // + // This legacy calllback exposes internal objects and their implementation + // details and is therefore deprecated. Use Less_Parser::SetImportDirs instead + // to override the resolution of imported file names. + // + // Example: + // + // $parser = new Less_Parser( [ + // 'import_callback' => function ( $importNode ) { + // $path = $importNode->getPath(); + // if ( $path === 'special.less' ) { + // return [ $mySpecialFilePath, null ]; + // } + // } + // ] ); + // + // @since 1.5.1 + // @deprecated since 4.3.0 + // @see Less_Environment::callImportCallback + // @see Less_Parser::SetImportDirs + // 'import_callback' => null, 'cache_dir' => null, 'cache_method' => 'serialize', // false, 'serialize', 'callback'; @@ -133,6 +156,10 @@ public function SetOption( $option, $value ) { $this->SetImportDirs( $value ); return; + case 'import_callback': + $this->env->importCallback = $value; + return; + case 'cache_dir': if ( is_string( $value ) ) { Less_Cache::SetCacheDir( $value ); diff --git a/lib/Less/Tree/Import.php b/lib/Less/Tree/Import.php index 911e44aa..a2d7b380 100644 --- a/lib/Less/Tree/Import.php +++ b/lib/Less/Tree/Import.php @@ -164,8 +164,7 @@ public function compile( $env ) { // about Less_Tree_Import::PathAndUri() is fixed, this can be removed by letting // skip() call $this->PathAndUri() on its own. // get path & uri - $callback = Less_Parser::$options['import_callback']; - $path_and_uri = is_callable( $callback ) ? $callback( $this ) : null; + $path_and_uri = $env->callImportCallback( $this ); if ( !$path_and_uri ) { $path_and_uri = $this->PathAndUri(); } @@ -217,7 +216,7 @@ public function compile( $env ) { */ public function PathAndUri() { $evald_path = $this->getPath(); - // TODO: Move 'import_callback' and getPath() fallback logic from callers + // TODO: Move callImportCallback() and getPath() fallback logic from callers // to here so that PathAndUri() is equivalent to upstream fileManager.getPath() if ( $evald_path ) { diff --git a/test/phpunit/ImportDirsTest.php b/test/phpunit/ImportDirsTest.php deleted file mode 100644 index cf78984e..00000000 --- a/test/phpunit/ImportDirsTest.php +++ /dev/null @@ -1,76 +0,0 @@ - '', - ], - 'div{font-family:monospace}' - ]; - yield [ - 'from-importdir-file.less', - [ - __DIR__ . '/data/importdir-somevars/' => '', - ], - 'div{font-family:fantasy}' - ]; - yield [ - 'from-importdir-filenosuffix.less', - [ - __DIR__ . '/data/importdir-somevars/' => '', - ], - 'div{font-family:fantasy}' - ]; - yield [ - 'from-importcallback.less', - [ - __DIR__ . '/data/importdir-somevars/' => '', - static function ( $path ) { - // Backwards compatibility with how people used - // less.php 4.0.0 and earlier. - if ( $path === '@wikimedia/example.less' ) { - return [ - Less_Environment::normalizePath( __DIR__ . '/data/importdir-somevars/callme.less' ), - Less_Environment::normalizePath( dirname( $path ) ) - ]; - } - return [ null, null ]; - } - ], - 'div{font-family:Call Me Maybe}' - ]; - yield [ - 'from-importcallback.less', - [ - __DIR__ . '/data/importdir-somevars/' => '', - static function ( $path ) { - if ( $path === '@wikimedia/example.less' ) { - return [ - __DIR__ . '/data/importdir-somevars/callme.less', - null - ]; - } - } - ], - 'div{font-family:Call Me Maybe}' - ]; - } - - /** - * @dataProvider provideConsumeSomevars - */ - public function testConsumeSomevars( string $input, array $importDirs, string $expect ) { - $file = __DIR__ . '/data/consume-somevars/' . $input; - - $parser = new Less_Parser( [ - 'compress' => true, - ] ); - $parser->SetImportDirs( $importDirs ); - $parser->parseFile( $file ); - - $this->assertEquals( $expect, $parser->getCss() ); - } -} diff --git a/test/phpunit/ParserTest.php b/test/phpunit/ParserTest.php index e07594c4..16ff995f 100644 --- a/test/phpunit/ParserTest.php +++ b/test/phpunit/ParserTest.php @@ -64,6 +64,94 @@ public function testGetParsedFiles() { ); } + public static function provideSetImportDirs() { + yield 'from-importdir' => [ + 'from-importdir.less', + [ + __DIR__ . '/data/importdir-somevars/' => '', + ], + 'div{font-family:monospace}' + ]; + yield 'from-importdir-file' => [ + 'from-importdir-file.less', + [ + __DIR__ . '/data/importdir-somevars/' => '', + ], + 'div{font-family:fantasy}' + ]; + yield 'from-importdir-filenosuffix' => [ + 'from-importdir-filenosuffix.less', + [ + __DIR__ . '/data/importdir-somevars/' => '', + ], + 'div{font-family:fantasy}' + ]; + yield 'from-importcallback (backcompat)' => [ + 'from-importcallback.less', + [ + __DIR__ . '/data/importdir-somevars/' => '', + static function ( $path ) { + // Backwards compatibility with how people used + // less.php 4.0.0 and earlier. + if ( $path === '@wikimedia/example.less' ) { + return [ + Less_Environment::normalizePath( __DIR__ . '/data/importdir-somevars/callme.less' ), + Less_Environment::normalizePath( dirname( $path ) ) + ]; + } + return [ null, null ]; + } + ], + 'div{font-family:Call Me Maybe}' + ]; + yield 'from-importcallback (new)' => [ + 'from-importcallback.less', + [ + __DIR__ . '/data/importdir-somevars/' => '', + static function ( $path ) { + if ( $path === '@wikimedia/example.less' ) { + return [ + __DIR__ . '/data/importdir-somevars/callme.less', + null + ]; + } + } + ], + 'div{font-family:Call Me Maybe}' + ]; + } + + /** + * @dataProvider provideSetImportDirs + */ + public function testSetImportDirs( string $input, array $importDirs, string $expect ) { + $file = __DIR__ . '/data/consume-somevars/' . $input; + + $parser = new Less_Parser( [ + 'compress' => true, + ] ); + $parser->SetImportDirs( $importDirs ); + $parser->parseFile( $file ); + + $this->assertEquals( $expect, $parser->getCss() ); + } + + public function testImportCallback() { + $file = __DIR__ . '/data/consume-somevars/from-importcallback.less'; + + $parser = new Less_Parser( [ + 'compress' => true, + 'import_callback' => static function ( $importNode ) { + if ( $importNode->getPath() === '@wikimedia/example.less' ) { + return [ __DIR__ . '/data/importdir-somevars/callme.less', '/site' ]; + } + } + ] ); + $parser->parseFile( $file ); + + $this->assertEquals( 'div{font-family:Call Me Maybe}', $parser->getCss() ); + } + public function testOperationException() { $lessFile = __DIR__ . '/data/exception/f2.less';