diff --git a/CHANGELOG.md b/CHANGELOG.md index d611db7384c..075e436610f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Fixes internal cache saving in `Phalcon\Mvc\Model\Binder` when no cache backend is used - Added the ability to get original values from `Phalcon\Mvc\Model\Binder`, added `Phalcon\Mvc\Micro::getModelBinder`, `Phalcon\Dispatcher::getModelBinder` - Added `prepend` parameter to `Phalcon\Loader::register` to specify autoloader's loading order to top most +- Added assets versioning with methods `Phalcon\Assets\Collection::getVersion`, `Phalcon\Assets\Collection::setVersion`, `Phalcon\Assets\Resource::setVersion`, `Phalcon\Assets\Resource::getVersion` # [3.0.4](https://github.com/phalcon/cphalcon/releases/tag/v3.0.4) (XXXX-XX-XX) - Fixed Isnull check is not correct when the model field defaults to an empty string. [#12507](https://github.com/phalcon/cphalcon/issues/12507) diff --git a/phalcon/assets/collection.zep b/phalcon/assets/collection.zep index f72d091c47d..163045c15be 100644 --- a/phalcon/assets/collection.zep +++ b/phalcon/assets/collection.zep @@ -59,6 +59,8 @@ class Collection implements \Countable, \Iterator protected _sourcePath { get }; + protected _version { get }; + /** * Adds a resource to the collection */ @@ -80,7 +82,7 @@ class Collection implements \Countable, \Iterator /** * Adds a CSS resource to the collection */ - public function addCss(string! path, var local = null, boolean filter = true, attributes = null) -> + public function addCss(string! path, var local = null, boolean filter = true, attributes = null, version = null) -> { var collectionLocal, collectionAttributes; @@ -96,7 +98,7 @@ class Collection implements \Countable, \Iterator let collectionAttributes = this->_attributes; } - let this->_resources[] = new ResourceCss(path, collectionLocal, filter, collectionAttributes); + let this->_resources[] = new ResourceCss(path, collectionLocal, filter, collectionAttributes, version); return this; } @@ -127,7 +129,7 @@ class Collection implements \Countable, \Iterator * @param array attributes * @return \Phalcon\Assets\Collection */ - public function addJs(string! path, var local = null, boolean filter = true, attributes = null) -> + public function addJs(string! path, var local = null, boolean filter = true, attributes = null, version = null) -> { var collectionLocal, collectionAttributes; @@ -143,7 +145,7 @@ class Collection implements \Countable, \Iterator let collectionAttributes = this->_attributes; } - let this->_resources[] = new ResourceJs(path, collectionLocal, filter, collectionAttributes); + let this->_resources[] = new ResourceJs(path, collectionLocal, filter, collectionAttributes, version); return this; } @@ -288,6 +290,17 @@ class Collection implements \Countable, \Iterator return this; } + /** + * Sets version + * + * @param int|string|bool version + */ + public function setVersion(var version) -> + { + let this->_version = version; + return this; + } + /** * Sets if all filtered resources in the collection must be joined in a single result file */ diff --git a/phalcon/assets/manager.zep b/phalcon/assets/manager.zep index e70ad48f3a5..b2ea7f935a7 100644 --- a/phalcon/assets/manager.zep +++ b/phalcon/assets/manager.zep @@ -93,9 +93,9 @@ class Manager * $assets->addCss("http://bootstrap.my-cdn.com/style.css", false); * */ - public function addCss(string! path, local = true, filter = true, var attributes = null) -> + public function addCss(string! path, local = true, filter = true, var attributes = null, var version = null) -> { - this->addResourceByType("css", new ResourceCss(path, local, filter, attributes)); + this->addResourceByType("css", new ResourceCss(path, local, filter, attributes, version)); return this; } @@ -116,9 +116,9 @@ class Manager * $assets->addJs("http://jquery.my-cdn.com/jquery.js", false); * */ - public function addJs(string! path, local = true, filter = true, attributes = null) -> + public function addJs(string! path, local = true, filter = true, attributes = null, version = null) -> { - this->addResourceByType("js", new ResourceJs(path, local, filter, attributes)); + this->addResourceByType("js", new ResourceJs(path, local, filter, attributes, version)); return this; } @@ -301,7 +301,7 @@ class Manager collectionTargetPath, completeTargetPath, filteredJoinedContent, join, $resource, filterNeeded, local, sourcePath, targetPath, path, prefixedPath, attributes, parameters, html, useImplicitOutput, content, mustFilter, - filter, filteredContent, typeCss, targetUri; + filter, filteredContent, typeCss, targetUri, version; let useImplicitOutput = this->_implicitOutput; @@ -488,6 +488,15 @@ class Manager let prefixedPath = path; } + if $resource->getVersion() == null && fetch version, collection->getVersion() { + if version { + if version === true && local { + let version = filemtime($resource->getRealSourcePath()); + } + let prefixedPath = prefixedPath . "?ver=" . version; + } + } + /** * Gets extra HTML attributes in the resource */ @@ -607,6 +616,15 @@ class Manager */ let local = true; + if $resource->getVersion() == null && fetch version, collection->getVersion() { + if version { + if version === true && local { + let version = filemtime($resource->getRealSourcePath()); + } + let prefixedPath = prefixedPath . "?ver=" . version; + } + } + /** * Prepare the parameters for the callback */ @@ -656,6 +674,15 @@ class Manager let prefixedPath = targetUri; } + if fetch version, collection->getVersion() { + if version { + if version === true { + let version = filemtime(completeTargetPath); + } + let prefixedPath = prefixedPath . "?ver=" . version; + } + } + /** * Gets extra HTML attributes in the collection */ diff --git a/phalcon/assets/resource.zep b/phalcon/assets/resource.zep index 898f3d74e1f..47064af7231 100644 --- a/phalcon/assets/resource.zep +++ b/phalcon/assets/resource.zep @@ -61,6 +61,8 @@ class $Resource protected _targetUri { get }; + protected _version { get }; + /** * Phalcon\Assets\Resource constructor * @@ -69,13 +71,15 @@ class $Resource * @param boolean local * @param boolean filter * @param array attributes + * @param int|string|bool version */ - public function __construct(string type, string path, boolean local = true, boolean filter = true, attributes = null) + public function __construct(string type, string path, boolean local = true, boolean filter = true, attributes = null, version = null) { let this->_type = type, this->_path = path, this->_local = local, - this->_filter = filter; + this->_filter = filter, + this->_version = version; if typeof attributes == "array" { let this->_attributes = attributes; } @@ -153,6 +157,17 @@ class $Resource return this; } + /** + * Sets version + * + * @param int|string|bool version + */ + public function setVersion(var version) -> <$Resource> + { + let this->_version = version; + return this; + } + /** * Returns the content of the resource as an string * Optionally a base path where the resource is located can be set @@ -200,12 +215,21 @@ class $Resource */ public function getRealTargetUri() -> string { - var targetUri; + var targetUri, version; let targetUri = this->_targetUri; if empty targetUri { let targetUri = this->_path; } + + let version = this->_version; + if version { + if version === true && this->_local { + let version = filemtime(this->getRealSourcePath()); + } + let targetUri = targetUri . "?ver=" . version; + } + return targetUri; } diff --git a/tests/unit/Assets/ManagerTest.php b/tests/unit/Assets/ManagerTest.php index 327312cc91f..2e2b5d1e2b0 100644 --- a/tests/unit/Assets/ManagerTest.php +++ b/tests/unit/Assets/ManagerTest.php @@ -492,4 +492,70 @@ function () { } ); } + + /** + * Tests assets versioning + * + * @author Wojciech Ślawski + * @since 2017-02-01 + */ + public function testAssetsVersioning() + { + $this->specify( + "Assets versioning doesn't work correctly", + function () { + $assets = new Manager(); + + $assets->addJs('js/script1.js', true, false, null, '1.0.0'); + $assets->addJs('js/script2.js', true, false, null, '2.0.0'); + $assets->addJs('js/script3.js', true, false, null); + + $expected = sprintf( + "%s\n%s\n%s\n", + '', + '', + '' + ); + + $assets->useImplicitOutput(false); + + expect($assets->outputJs())->equals($expected); + } + ); + } + + /** + * Tests assets auto versioning + * + * @author Wojciech Ślawski + * @since 2017-02-01 + */ + public function testAssetsAutoVersioningCollection() + { + $this->specify( + "Assets auto versioning with collection doesn't work correctly", + function () { + $assets = new Manager(); + + $assets->collection('js') + ->addJs('js/script1.js', true, false, null, '1.0.0') + ->addJs('js/script2.js', true, false, null) + ->addJs('js/script3.js', true, false, null, false) + ->setVersion(true); + + $modificationTime = filemtime('js/script2.js'); + + $expected = sprintf( + "%s\n%s\n%s\n", + '', + "", + '' + ); + + $assets->useImplicitOutput(false); + + expect($assets->outputJs())->equals($expected); + } + ); + } } diff --git a/tests/unit/Assets/ResourceTest.php b/tests/unit/Assets/ResourceTest.php index cb4024277ef..4a3a9c45b4a 100644 --- a/tests/unit/Assets/ResourceTest.php +++ b/tests/unit/Assets/ResourceTest.php @@ -82,4 +82,45 @@ function () { } ); } + + /** + * Tests resource versioning + * + * @author Wojciech Ślawski + * @since 2017-02-01 + */ + public function testAssetsVersioning() + { + $this->specify( + "The resource versioning is not correct", + function () { + $resource = new Resource('js', 'js/jquery.js', true, false, null, '1.0.0'); + $actual = $resource->getRealTargetUri(); + $expected = 'js/jquery.js?ver=1.0.0'; + + expect($actual)->equals($expected); + } + ); + } + + /** + * Tests resource auto versioning + * + * @author Wojciech Ślawski + * @since 2017-02-01 + */ + public function testAssetsAutomaticVersioning() + { + $this->specify( + "The resource auto versioning is not correct", + function () { + $resource = new Resource('js', 'js/jquery.js', true, false, null, true); + $actual = $resource->getRealTargetUri(); + $modificationTime = filemtime('js/jquery.js'); + $expected = 'js/jquery.js?ver='.$modificationTime; + + expect($actual)->equals($expected); + } + ); + } }