Skip to content

Commit

Permalink
Merge pull request #1 from PhantPHP/init
Browse files Browse the repository at this point in the history
Init
  • Loading branch information
lennyrouanet authored Apr 8, 2021
2 parents 85ade6d + e7c8f94 commit 0326ad1
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 1 deletion.
78 changes: 77 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,77 @@
# assets-versions
# Assets versions

## Requirments

PHP >= 7.4


## Install

`composer require phant/assets-versions`


## Basic usage

Add this code in your initialization of your application :

```php
use Phant\AssetsVersions\AssetsVersions;

$assetsVersions = new AssetsVersions(
'public/', // path to be processed
[ 'css', 'js' ], // extensions to be processed
[ 'node_modules/' ] // path to be ignored in path to be processed
);
```

And this code when calling your assets :

```
<link rel="stylesheet" href="<?= $assetsVersions->of( 'styles/main.css' ) ?>"/>
<script src="<?= $assetsVersions->of( 'lib/init.js' ) ?>"></script>
```


## Exemple with a cache manager


Exemple with Symfony FilesystemAdapter :

```php
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Phant\AssetsVersions\AssetsVersions;


// cache

$cache = new FilesystemAdapter( 'my-app-cache' );


// assets versions

$callbackLoadCache = function() use ( $cache ): ?array {
return $cache->getItem( $item )->get( 'assets-versions' );
};

$callbackSaveCache = function( ?array $assetVersionCollection ) use ( $cache ) {
$cacheItem = $cache->getItem( 'assets-versions' );
$cacheItem->set( $assetVersionCollection );
$cacheItem->expiresAfter( 30 * 86400 ); // 30 days
$cache->save( $cacheItem );
};

$assetsVersions = new AssetsVersions(
'public/', // path to be processed
[ 'css', 'js' ], // extensions to be processed
[ 'node_modules/' ], // path to be ignored in path to be processed
$callbackLoadCache, //callback load cache
$callbackSaveCache //callback save cache
);
```


You can generate assets versions cache with this method :

```
$assetsVersions->generate();
```
20 changes: 20 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "phant/assets-versions",
"description": "get assets versions easily",
"license": "MIT",
"keywords": ["assets", "versions", "assets versions", "git assets versions", "assets cache auto-revocation"],
"authors": [
{
"name": "Lenny ROUANET",
"email": "[email protected]"
}
],
"require": {
"php": "^7.4"
},
"autoload": {
"psr-4": {
"Phant\\AssetsVersions\\": "src/AssetsVersions/"
}
}
}
169 changes: 169 additions & 0 deletions src/AssetsVersions/AssetsVersions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php
declare(strict_types=1);

namespace Phant\AssetsVersions;

use Phant\AssetsVersions\Exception\{
PathToBeProcessedDoesNotExist,
};
use Phant\AssetsVersions\ValueObject\{
AssetVersion,
AssetVersionCollection,
};

final class AssetsVersions
{
private string $pathToBeProcessed;
private array $extensionsToBeProcessed;
private ?array $pathsToBeIgnored;
protected $callbackLoadCache;
protected $callbackSaveCache;

private AssetVersionCollection $assetVersionCollection;

public function __construct( string $pathToBeProcessed, array $extensionsToBeProcessed = [ 'css', 'js' ], ?array $pathsToBeIgnored = null, ?callable $callbackLoadCache = null, ?callable $callbackSaveCache = null )
{
if ( !is_dir( $pathToBeProcessed ) ) {
throw new PathToBeProcessedDoesNotExist;
}

$this->pathToBeProcessed = $pathToBeProcessed;
$this->extensionsToBeProcessed = array_map( 'strtolower', $extensionsToBeProcessed );
$this->pathsToBeIgnored = $pathsToBeIgnored;
$this->callbackLoadCache = $callbackLoadCache;
$this->callbackSaveCache = $callbackSaveCache;

$this->assetVersionCollection = new AssetVersionCollection();

$this->loadFromCache();
}

public function of( string $assetPath ): string
{
$assetVersion = $this->assetVersionCollection->findFromAssetPath( $assetPath );

if ($assetVersion) {
return (string) $assetVersion;
}

$assetVersion = $this->getFromPath( $assetPath );

if ($assetVersion) {
$this->assetVersionCollection->add( $assetVersion );
$this->saveToCache();
return (string) $assetVersion;
}

return $assetPath;
}

public function generate()
{
$assetPathList = $this->getAssetPathList();

foreach ( $assetPathList as $assetPath ) {
$assetVersion = $this->getFromPath( $assetPath );
$this->assetVersionCollection->add( $assetVersion );
}

$this->saveToCache();
}

private function loadFromCache()
{
if ( is_callable( $this->callbackLoadCache ) ) {
$assetVersionCollection = ( $this->callbackLoadCache )();

if ( !is_a( $assetVersionCollection, get_class( $this->assetVersionCollection ) ) ) {
return;
}

$this->assetVersionCollection = $assetVersionCollection;
}
}

private function saveToCache()
{
if ( is_callable( $this->callbackSaveCache ) ) {
( $this->callbackSaveCache )( $this->assetVersionCollection );
}
}

private function getFromPath( string $assetPath ): ?AssetVersion
{
$assetVersion = $this->getAssetVersionFromGitRevisions( $assetPath );

if ( $assetVersion ) {
return $assetVersion;
}

$assetVersion = $this->getAssetVersionFromFileUpdateTime( $assetPath );

if ( $assetVersion ) {
return $assetVersion;
}

return null;
}

private function getAssetVersionFromGitRevisions( string $assetPath ): ?AssetVersion
{
$command = sprintf( 'git log --oneline %s | wc -l', escapeshellarg( $this->pathToBeProcessed . $assetPath ) );
$revision = exec( $command );

if ( !$revision ) {
return null;
}

return new AssetVersion( $assetPath, AssetVersion::TYPE_VERSION, (int) $revision );
}

private function getAssetVersionFromFileUpdateTime( string $assetPath ): ?AssetVersion
{
$modificationTime = filemtime( $this->pathToBeProcessed . $assetPath );

if ( !$modificationTime ) {
return null;
}

return new AssetVersion( $assetPath, AssetVersion::TYPE_MODIFICATION_TIME, (int) $modificationTime );
}

private function getAssetPathList( string $subpath = '' ): array
{
$assetPathList = [];

$dh = opendir( $this->pathToBeProcessed . $subpath );

while ( $entry = readdir( $dh ) ) {

if ( $entry == '.' || $entry == '..' ) {
continue;
}

$entryPath = $subpath . $entry;

if ( is_dir( $entryPath )) {
$entryPath.= '/';

if (in_array( $entryPath, $this->pathsToBeIgnored ) ) {
continue;
}

$assetPathList = array_merge( $assetPathList, $this->getAssetPathList( $entryPath ) );

continue;
}

if ( !empty( $this->extensionsToBeProcessed ) && !in_array( strtolower( pathinfo( $entryPath, PATHINFO_EXTENSION ) ), $this->extensionsToBeProcessed ) ) {
continue;
}

$assetPathList[] = $entryPath;
}

closedir($dh);

return $assetPathList;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
declare(strict_types=1);

namespace Phant\AssetsVersions\Exception;

class PathToBeProcessedDoesNotExist extends \Exception
{
}
28 changes: 28 additions & 0 deletions src/AssetsVersions/ValueObject/AssetVersion.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);

namespace Phant\AssetsVersions\ValueObject;

final class AssetVersion
{
const TYPE_VERSION = 'ver';
const TYPE_MODIFICATION_TIME = 'lmod';

public string $assetPath;
public string $type;
public int $value;

public function __construct( string $assetPath, string $type, int $value )
{
$this->assetPath = $assetPath;
$this->type = $type;
$this->value = $value;
}

public function __toString()
{
return $this->assetPath . '?' . http_build_query([
$this->type => $this->value
]);
}
}
29 changes: 29 additions & 0 deletions src/AssetsVersions/ValueObject/AssetVersionCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);

namespace Phant\AssetsVersions\ValueObject;

final class AssetVersionCollection
{
private array $collection;

public function __construct()
{
$this->collection = [];
}

public function add( AssetVersion $assetVersion )
{
$this->collection[ $assetVersion->assetPath ] = $assetVersion;
}

public function findFromAssetPath( string $assetPath ): ?AssetVersion
{
return $this->collection[ $assetPath ] ?? null;
}

public function isEmpty(): bool
{
return empty( $this->collection );
}
}

0 comments on commit 0326ad1

Please sign in to comment.