From dce01df3f672b07717aac02f4148baba38a36243 Mon Sep 17 00:00:00 2001 From: Maksim Khodyrev Date: Wed, 18 Jan 2017 17:30:10 +0400 Subject: [PATCH] allow set target root CDN directory #103 --- readme.md | 7 ++ src/Vinelab/Cdn/CdnFacade.php | 4 +- src/Vinelab/Cdn/CdnHelper.php | 31 ++++++ .../Cdn/Contracts/CdnHelperInterface.php | 2 + src/Vinelab/Cdn/Providers/AwsS3Provider.php | 6 +- src/config/cdn.php | 12 ++ tests/Vinelab/Cdn/CdnFacadeTest.php | 4 + tests/Vinelab/Cdn/CdnHelperTest.php | 105 ++++++++++++++++++ tests/Vinelab/Cdn/CdnTest.php | 4 + .../Cdn/Providers/AwsS3ProviderTest.php | 4 + 10 files changed, 174 insertions(+), 5 deletions(-) create mode 100755 tests/Vinelab/Cdn/CdnHelperTest.php diff --git a/readme.md b/readme.md index 716c7cd..5b8d28a 100755 --- a/readme.md +++ b/readme.md @@ -139,6 +139,13 @@ Set the CDN URL: 'url' => 'https://s3.amazonaws.com', ``` +##### CDN Base Directory +Set CDN base dir path, if null, upload assets into bucket root. +```php +'cdn_base_dir' => null, +``` +Example: 'myapp1/assets', then you can access assets by url like https://my-bucket.name/myapp1/assets/file.jpg + ##### HTTP Set the HTTP parameters: diff --git a/src/Vinelab/Cdn/CdnFacade.php b/src/Vinelab/Cdn/CdnFacade.php index c7a010f..6b83d8a 100755 --- a/src/Vinelab/Cdn/CdnFacade.php +++ b/src/Vinelab/Cdn/CdnFacade.php @@ -150,7 +150,9 @@ private function generateUrl($path, $prepend = '') // remove slashes from begging and ending of the path // and append directories if needed - $clean_path = $prepend.$this->helper->cleanPath($path); + $clean_path = $this->helper->getCdnFilePath( + $prepend.$this->helper->cleanPath($path) + ); // call the provider specific url generator return $this->provider->urlGenerator($clean_path); diff --git a/src/Vinelab/Cdn/CdnHelper.php b/src/Vinelab/Cdn/CdnHelper.php index d021054..434507d 100644 --- a/src/Vinelab/Cdn/CdnHelper.php +++ b/src/Vinelab/Cdn/CdnHelper.php @@ -112,4 +112,35 @@ public function cleanPath($path) { return rtrim(ltrim($path, '/'), '/'); } + + /** + * Make path to local file copy on CDN, using cdn_base_dir + * @param string $localPath + * @return string + */ + public function getCdnFilePath($localPath) + { + $baseDir = null; + $cfg = $this->getConfigurations(); + $localPath = $this->normalizePath($localPath); + + if (isset($cfg['cdn_base_dir'])) { + $baseDir = $cfg['cdn_base_dir']; + } + + if (null === $baseDir) { + return $localPath; + } + + return $this->cleanPath($baseDir).'/'.$this->cleanPath($localPath); + } + + /** + * @param string $path + * @return string + */ + private function normalizePath($path) + { + return str_replace('\\', '/', $path); + } } diff --git a/src/Vinelab/Cdn/Contracts/CdnHelperInterface.php b/src/Vinelab/Cdn/Contracts/CdnHelperInterface.php index cd4a0e6..3db9d16 100644 --- a/src/Vinelab/Cdn/Contracts/CdnHelperInterface.php +++ b/src/Vinelab/Cdn/Contracts/CdnHelperInterface.php @@ -18,4 +18,6 @@ public function parseUrl($url); public function startsWith($haystack, $needle); public function cleanPath($path); + + public function getCdnFilePath($localPath); } diff --git a/src/Vinelab/Cdn/Providers/AwsS3Provider.php b/src/Vinelab/Cdn/Providers/AwsS3Provider.php index 8c45720..92db573 100755 --- a/src/Vinelab/Cdn/Providers/AwsS3Provider.php +++ b/src/Vinelab/Cdn/Providers/AwsS3Provider.php @@ -204,7 +204,7 @@ public function upload($assets) // the bucket name 'Bucket' => $this->getBucket(), // the path of the file on the server (CDN) - 'Key' => str_replace('\\', '/', $file->getPathName()), + 'Key' => $this->cdn_helper->getCdnFilePath($file->getPathName()), // the path of the path locally 'Body' => fopen($file->getRealPath(), 'r'), // the permission of the file @@ -214,8 +214,6 @@ public function upload($assets) 'Metadata' => $this->default['providers']['aws']['s3']['metadata'], 'Expires' => $this->default['providers']['aws']['s3']['expires'], ]); -// var_dump(get_class($command));exit(); - $this->s3_client->execute($command); } catch (S3Exception $e) { @@ -395,7 +393,7 @@ private function getFilesAlreadyOnBucket($assets) } $assets->transform(function($item, $key) use(&$filesOnAWS) { - $fileOnAWS = $filesOnAWS->get(str_replace('\\', '/', $item->getPathName())); + $fileOnAWS = $filesOnAWS->get($this->cdn_helper->getCdnFilePath($item->getPathName())); //select to upload files that are different in size AND last modified time. if(!($item->getMTime() === $fileOnAWS['LastModified']) && !($item->getSize() === $fileOnAWS['Size'])) { diff --git a/src/config/cdn.php b/src/config/cdn.php index db5e0f1..b19fd4f 100755 --- a/src/config/cdn.php +++ b/src/config/cdn.php @@ -40,6 +40,18 @@ */ 'url' => 'https://s3.amazonaws.com', + /* + |-------------------------------------------------------------------------- + | CDN BASE DIRECTORY + |-------------------------------------------------------------------------- + | + | Set CDN base dir path, if null, upload into bucket root + | Example: 'myapp1/assets', then you can access assets + | by url like https://my-bucket.name/myapp1/assets/file.jpg + | + */ + 'cdn_base_dir' => null, + /* |-------------------------------------------------------------------------- | Threshold diff --git a/tests/Vinelab/Cdn/CdnFacadeTest.php b/tests/Vinelab/Cdn/CdnFacadeTest.php index 18f276a..45ac1dd 100755 --- a/tests/Vinelab/Cdn/CdnFacadeTest.php +++ b/tests/Vinelab/Cdn/CdnFacadeTest.php @@ -13,6 +13,8 @@ */ class CdnFacadeTest extends TestCase { + const CDN_BASE_DIR = 'base'; + public function setUp() { parent::setUp(); @@ -21,6 +23,7 @@ public function setUp() 'bypass' => false, 'default' => 'AwsS3', 'url' => 'https://s3.amazonaws.com', + 'cdn_base_dir' => self::CDN_BASE_DIR, 'threshold' => 10, 'providers' => [ 'aws' => [ @@ -66,6 +69,7 @@ public function setUp() $this->helper->shouldReceive('getConfigurations')->once()->andReturn($configuration_file); $this->helper->shouldReceive('cleanPath')->andReturn($this->asset_path); $this->helper->shouldReceive('startsWith')->andReturn(true); + $this->helper->shouldReceive('getCdnFilePath')->andReturn(self::CDN_BASE_DIR.'/'.$this->asset_path); $this->validator = new \Vinelab\Cdn\Validators\CdnFacadeValidator(); diff --git a/tests/Vinelab/Cdn/CdnHelperTest.php b/tests/Vinelab/Cdn/CdnHelperTest.php new file mode 100755 index 0000000..53eb4f5 --- /dev/null +++ b/tests/Vinelab/Cdn/CdnHelperTest.php @@ -0,0 +1,105 @@ + + */ +class CdnHelperTest extends \PHPUnit_Framework_TestCase +{ + const CDN_BASE_DIR = 'base'; + + /** + * @dataProvider dpGetCdnFilePath + * @param string $localPath + * @param string $cdnPath + */ + public function testGetCdnFilePath($localPath, $cdnPath) + { + $helper = new CdnHelper( + new Repository([ + 'cdn' => $this->getCdnConfig() + ]) + ); + + $this->assertEquals( + $cdnPath, + $helper->getCdnFilePath($localPath) + ); + } + + /** + * @return array + */ + public function dpGetCdnFilePath() + { + return [ + [ + 'public/image.png', + self::CDN_BASE_DIR.'/public/image.png', + ], + [ + '/public/image.png', + self::CDN_BASE_DIR.'/public/image.png', + ], + [ + 'image.png', + self::CDN_BASE_DIR.'/image.png', + ] + ]; + } + + /** + * @return array + */ + private function getCdnConfig() + { + return [ + 'bypass' => false, + 'default' => 'AwsS3', + 'url' => 'https://s3.amazonaws.com', + 'cdn_base_dir' => self::CDN_BASE_DIR, + 'threshold' => 10, + 'providers' => [ + 'aws' => [ + 's3' => [ + 'region' => 'us-standard', + 'version' => 'latest', + 'http' => null, + 'buckets' => [ + 'my-bucket-name' => '*', + ], + 'acl' => 'public-read', + 'cloudfront' => [ + 'use' => false, + 'cdn_url' => '', + ], + 'metadata' => [], + + 'expires' => gmdate('D, d M Y H:i:s T', strtotime('+5 years')), + + 'cache-control' => 'max-age=2628000', + ], + ], + ], + 'include' => [ + 'directories' => [__DIR__], + 'extensions' => [], + 'patterns' => [], + ], + 'exclude' => [ + 'directories' => [], + 'files' => [], + 'extensions' => [], + 'patterns' => [], + 'hidden' => true, + ], + ]; + } +} diff --git a/tests/Vinelab/Cdn/CdnTest.php b/tests/Vinelab/Cdn/CdnTest.php index 390f424..eca4cc1 100755 --- a/tests/Vinelab/Cdn/CdnTest.php +++ b/tests/Vinelab/Cdn/CdnTest.php @@ -14,6 +14,8 @@ */ class CdnTest extends TestCase { + const CDN_BASE_DIR = 'base'; + public function setUp() { parent::setUp(); @@ -81,6 +83,7 @@ public function testPushCommand() 'bypass' => false, 'default' => 'AwsS3', 'url' => 'https://s3.amazonaws.com', + 'cdn_base_dir' => self::CDN_BASE_DIR, 'threshold' => 10, 'providers' => [ 'aws' => [ @@ -144,6 +147,7 @@ public function testPushCommand() $m_validator->shouldReceive('validate'); $m_helper = M::mock('Vinelab\Cdn\CdnHelper'); + $m_helper->shouldReceive('getCdnFilePath'); $m_spl_file = M::mock('Symfony\Component\Finder\SplFileInfo'); $m_spl_file->shouldReceive('getPathname') diff --git a/tests/Vinelab/Cdn/Providers/AwsS3ProviderTest.php b/tests/Vinelab/Cdn/Providers/AwsS3ProviderTest.php index 6fb3389..0a81ecc 100755 --- a/tests/Vinelab/Cdn/Providers/AwsS3ProviderTest.php +++ b/tests/Vinelab/Cdn/Providers/AwsS3ProviderTest.php @@ -14,6 +14,8 @@ */ class AwsS3ProviderTest extends TestCase { + const CDN_BASE_DIR = 'base'; + public function setUp() { parent::setUp(); @@ -33,6 +35,7 @@ public function setUp() $this->m_helper = M::mock('Vinelab\Cdn\CdnHelper'); $this->m_helper->shouldReceive('parseUrl') ->andReturn($this->pased_url); + $this->m_helper->shouldReceive('getCdnFilePath'); $this->m_spl_file = M::mock('Symfony\Component\Finder\SplFileInfo'); $this->m_spl_file->shouldReceive('getPathname')->andReturn('vinelab/cdn/tests/Vinelab/Cdn/AwsS3ProviderTest.php'); @@ -103,6 +106,7 @@ public function testUploadingAssets() $configurations = [ 'default' => 'AwsS3', 'url' => 'https://s3.amazonaws.com', + 'cdn_base_dir' => self::CDN_BASE_DIR, 'threshold' => 10, 'providers' => [ 'aws' => [