diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php index 8e6011c09a27f..4cf67858fe287 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php @@ -13,16 +13,20 @@ */ namespace Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery; -use Magento\Framework\App\ObjectManager; +use Magento\Backend\Block\DataProviders\ImageUploadConfig as ImageUploadConfigDataProvider; use Magento\Backend\Block\Media\Uploader; -use Magento\Framework\View\Element\AbstractBlock; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\FileSystemException; -use Magento\Backend\Block\DataProviders\ImageUploadConfig as ImageUploadConfigDataProvider; +use Magento\Framework\Storage\FileNotFoundException; +use Magento\Framework\Storage\StorageProvider; +use Magento\Framework\View\Element\AbstractBlock; use Magento\MediaStorage\Helper\File\Storage\Database; /** * Block for gallery content. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Content extends \Magento\Backend\Block\Widget { @@ -55,11 +59,21 @@ class Content extends \Magento\Backend\Block\Widget * @var Database */ private $fileStorageDatabase; + /** + * @var StorageProvider + */ + private $storageProvider; + + /** + * @var \Magento\Framework\Filesystem\Directory\ReadInterface + */ + private $mediaDirectory; /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder * @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig + * @param StorageProvider $storageProvider * @param array $data * @param ImageUploadConfigDataProvider $imageUploadConfigDataProvider * @param Database $fileStorageDatabase @@ -68,6 +82,7 @@ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Framework\Json\EncoderInterface $jsonEncoder, \Magento\Catalog\Model\Product\Media\Config $mediaConfig, + StorageProvider $storageProvider, array $data = [], ImageUploadConfigDataProvider $imageUploadConfigDataProvider = null, Database $fileStorageDatabase = null @@ -75,10 +90,12 @@ public function __construct( $this->_jsonEncoder = $jsonEncoder; $this->_mediaConfig = $mediaConfig; parent::__construct($context, $data); + $this->mediaDirectory = $this->_filesystem->getDirectoryRead(DirectoryList::MEDIA); $this->imageUploadConfigDataProvider = $imageUploadConfigDataProvider ?: ObjectManager::getInstance()->get(ImageUploadConfigDataProvider::class); $this->fileStorageDatabase = $fileStorageDatabase ?: ObjectManager::getInstance()->get(Database::class); + $this->storageProvider = $storageProvider; } /** @@ -157,10 +174,49 @@ public function getAddImagesButton() ); } + /** + * Sync images to database + * + * @param string $fileName + */ + private function syncImageToDatabase(string $fileName): void + { + if ($this->fileStorageDatabase->checkDbUsage() && + !$this->mediaDirectory->isFile($this->_mediaConfig->getMediaPath($fileName)) + ) { + $this->fileStorageDatabase->saveFileToFilesystem( + $this->_mediaConfig->getMediaPath($fileName) + ); + } + } + + /** + * Returns file metadata as an associative array + * + * @param string $fileName + * @return array + * @throws FileNotFoundException + */ + private function getFileMetadata(string $fileName): array + { + $metadata = []; + try { + $info = $this->storageProvider->get('media') + ->getMetadata($this->_mediaConfig->getMediaPath($fileName)); + $metadata['size'] = $info['size']; + } catch (FileSystemException $e) { + $metadata['url'] = $this->getImageHelper()->getDefaultPlaceholderUrl('small_image'); + $metadata['size'] = 0; + $this->_logger->warning($e); + } + return $metadata; + } + /** * Returns image json * * @return string + * @throws FileNotFoundException */ public function getImagesJson() { @@ -170,24 +226,14 @@ public function getImagesJson() is_array($value['images']) && count($value['images']) ) { - $mediaDir = $this->_filesystem->getDirectoryRead(DirectoryList::MEDIA); $images = $this->sortImagesByPosition($value['images']); foreach ($images as &$image) { $image['url'] = $this->_mediaConfig->getMediaUrl($image['file']); - if ($this->fileStorageDatabase->checkDbUsage() && - !$mediaDir->isFile($this->_mediaConfig->getMediaPath($image['file'])) - ) { - $this->fileStorageDatabase->saveFileToFilesystem( - $this->_mediaConfig->getMediaPath($image['file']) - ); - } - try { - $fileHandler = $mediaDir->stat($this->_mediaConfig->getMediaPath($image['file'])); - $image['size'] = $fileHandler['size']; - } catch (FileSystemException $e) { - $image['url'] = $this->getImageHelper()->getDefaultPlaceholderUrl('small_image'); - $image['size'] = 0; - $this->_logger->warning($e); + $this->syncImageToDatabase($image['file']); + if (isset($image['image_metadata']) && is_array($image['image_metadata'])) { + $image = array_replace_recursive($image, $image['image_metadata']); + } else { + $image = array_replace_recursive($image, $this->getFileMetadata($image['file'])); } } return $this->_jsonEncoder->encode($images); diff --git a/app/code/Magento/Catalog/Block/Product/Gallery.php b/app/code/Magento/Catalog/Block/Product/Gallery.php index 54f848a92e958..2e9dcd1fe6952 100644 --- a/app/code/Magento/Catalog/Block/Product/Gallery.php +++ b/app/code/Magento/Catalog/Block/Product/Gallery.php @@ -11,9 +11,13 @@ */ namespace Magento\Catalog\Block\Product; +use Magento\Framework\Storage\FileNotFoundException; use Magento\Catalog\Model\Product; -use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Catalog\Model\Product\Media\Config; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Data\Collection; +use Magento\Framework\Registry; +use Magento\Framework\Storage\StorageProvider; /** * Product gallery block @@ -26,22 +30,37 @@ class Gallery extends \Magento\Framework\View\Element\Template /** * Core registry * - * @var \Magento\Framework\Registry + * @var Registry */ protected $_coreRegistry = null; + /** + * @var StorageProvider + */ + private $storageProvider; + /** + * @var Config + */ + private $mediaConfig; + /** * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Framework\Registry $registry + * @param Registry $registry * @param array $data + * @param StorageProvider $storageProvider + * @param Config $mediaConfig */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, - \Magento\Framework\Registry $registry, - array $data = [] + Registry $registry, + array $data = [], + StorageProvider $storageProvider = null, + Config $mediaConfig = null ) { $this->_coreRegistry = $registry; parent::__construct($context, $data); + $this->storageProvider = $storageProvider ?? ObjectManager::getInstance()->get(StorageProvider::class); + $this->mediaConfig = $mediaConfig ?? ObjectManager::getInstance()->get(Config::class); } /** @@ -121,16 +140,24 @@ public function getImageFile() */ public function getImageWidth() { - $file = $this->getCurrentImage()->getPath(); - - if ($this->_filesystem->getDirectoryRead(DirectoryList::MEDIA)->isFile($file)) { - $size = getimagesize($file); - if (isset($size[0])) { - if ($size[0] > 600) { + $file = $this->getCurrentImage()->getFile(); + if (!$file) { + return false; + } + $productMediaFile = $this->mediaConfig->getMediaPath($file); + + $mediaStorage = $this->storageProvider->get('media'); + if ($mediaStorage->has($productMediaFile)) { + try { + $meta = $mediaStorage->getMetadata($productMediaFile); + $size = $meta['size']; + if ($size > 600) { return 600; } else { - return (int) $size[0]; + return (int) $size; } + } catch (FileNotFoundException $e) { + return false; } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php index 3e7cc3ee962b9..fda3d0abced7f 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php @@ -8,7 +8,7 @@ use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Storage\StorageProvider; /** * Upload product image action controller @@ -52,9 +52,15 @@ class Upload extends \Magento\Backend\App\Action implements HttpPostActionInterf */ private $productMediaConfig; + /** + * @var StorageProvider + */ + private $storageProvider; + /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory + * @param StorageProvider $storageProvider * @param \Magento\Framework\Image\AdapterFactory $adapterFactory * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\Catalog\Model\Product\Media\Config $productMediaConfig @@ -62,6 +68,7 @@ class Upload extends \Magento\Backend\App\Action implements HttpPostActionInterf public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\Framework\Controller\Result\RawFactory $resultRawFactory, + StorageProvider $storageProvider, \Magento\Framework\Image\AdapterFactory $adapterFactory = null, \Magento\Framework\Filesystem $filesystem = null, \Magento\Catalog\Model\Product\Media\Config $productMediaConfig = null @@ -74,6 +81,7 @@ public function __construct( ->get(\Magento\Framework\Filesystem::class); $this->productMediaConfig = $productMediaConfig ?: ObjectManager::getInstance() ->get(\Magento\Catalog\Model\Product\Media\Config::class); + $this->storageProvider = $storageProvider; } /** @@ -84,6 +92,7 @@ public function __construct( public function execute() { try { + /** @var \Magento\MediaStorage\Model\File\Uploader $uploader */ $uploader = $this->_objectManager->create( \Magento\MediaStorage\Model\File\Uploader::class, ['fileId' => 'image'] @@ -93,11 +102,18 @@ public function execute() $uploader->addValidateCallback('catalog_product_image', $imageAdapter, 'validateUploadFile'); $uploader->setAllowRenameFiles(true); $uploader->setFilesDispersion(true); + $mediaDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); + $baseImagePath = $this->productMediaConfig->getBaseTmpMediaPath(); $result = $uploader->save( - $mediaDirectory->getAbsolutePath($this->productMediaConfig->getBaseTmpMediaPath()) + $mediaDirectory->getAbsolutePath($baseImagePath) ); + $origFile = $this->productMediaConfig->getTmpMediaPath($result['file']); + $storage = $this->storageProvider->get('media'); + $content = $mediaDirectory->readFile($origFile); + $storage->put($origFile, $content); + $this->_eventManager->dispatch( 'catalog_product_gallery_upload_image_after', ['result' => $result, 'action' => $this] diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php index 5b0aa0c496ecd..f220fa0ef0444 100644 --- a/app/code/Magento/Catalog/Helper/Image.php +++ b/app/code/Magento/Catalog/Helper/Image.php @@ -13,7 +13,7 @@ use Magento\Framework\View\Element\Block\ArgumentInterface; /** - * Catalog image helper. + * Catalog image helper * * @api * @SuppressWarnings(PHPMD.TooManyFields) @@ -166,7 +166,8 @@ public function __construct( $this->_assetRepo = $assetRepo; $this->viewConfig = $viewConfig; $this->viewAssetPlaceholderFactory = $placeholderFactory - ?: ObjectManager::getInstance()->get(PlaceholderFactory::class); + ?: ObjectManager::getInstance() + ->get(PlaceholderFactory::class); $this->mediaConfig = $mediaConfig ?: ObjectManager::getInstance()->get(CatalogMediaConfig::class); } @@ -573,6 +574,9 @@ public function save() * Return resized product image information * * @return array + * @deprecated Magento is not responsible for image resizing anymore. This method works with local filesystem only. + * Service that provides resized images should guarantee that the image sizes correspond to requested ones. + * Use `getWidth()` and `getHeight()` instead. */ public function getResizedImageInfo() { diff --git a/app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php b/app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php index f24044fc92c95..0ceeeb596655d 100644 --- a/app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php +++ b/app/code/Magento/Catalog/Model/Config/Source/Web/CatalogMediaUrlFormat.php @@ -24,7 +24,7 @@ public function toOptionArray() 'value' => CatalogMediaConfig::IMAGE_OPTIMIZATION_PARAMETERS, 'label' => __('Image optimization based on query parameters') ], - ['value' => CatalogMediaConfig::HASH, 'label' => __('Unique hash per image variant (Legacy mode)')] + ['value' => CatalogMediaConfig::HASH, 'label' => __('Legacy mode (unique hash per image variant)')] ]; } } diff --git a/app/code/Magento/Catalog/Model/ImageUploader.php b/app/code/Magento/Catalog/Model/ImageUploader.php index 0c3e008fa8bb5..d333ea589b997 100644 --- a/app/code/Magento/Catalog/Model/ImageUploader.php +++ b/app/code/Magento/Catalog/Model/ImageUploader.php @@ -6,6 +6,7 @@ namespace Magento\Catalog\Model; use Magento\Framework\File\Uploader; +use Magento\Framework\Storage\StorageProvider; /** * Catalog image uploader @@ -73,6 +74,11 @@ class ImageUploader */ private $allowedMimeTypes; + /** + * @var StorageProvider + */ + private $storageProvider; + /** * ImageUploader constructor * @@ -84,7 +90,9 @@ class ImageUploader * @param string $baseTmpPath * @param string $basePath * @param string[] $allowedExtensions + * @param StorageProvider $storageProvider * @param string[] $allowedMimeTypes + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\MediaStorage\Helper\File\Storage\Database $coreFileStorageDatabase, @@ -95,6 +103,7 @@ public function __construct( $baseTmpPath, $basePath, $allowedExtensions, + StorageProvider $storageProvider, $allowedMimeTypes = [] ) { $this->coreFileStorageDatabase = $coreFileStorageDatabase; @@ -106,6 +115,7 @@ public function __construct( $this->basePath = $basePath; $this->allowedExtensions = $allowedExtensions; $this->allowedMimeTypes = $allowedMimeTypes; + $this->storageProvider = $storageProvider; } /** @@ -220,6 +230,11 @@ public function moveFileFromTmp($imageName, $returnRelativePath = false) $baseTmpImagePath, $baseImagePath ); + + $storage = $this->storageProvider->get('media'); + $content = $this->mediaDirectory->readFile($baseImagePath); + $storage->put($baseImagePath, $content); + } catch (\Exception $e) { throw new \Magento\Framework\Exception\LocalizedException( __('Something went wrong while saving the file(s).') diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index a9907c1661bd8..c5dce0df14755 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -1540,7 +1540,11 @@ public function getMediaGalleryImages() } $image['url'] = $this->getMediaConfig()->getMediaUrl($image['file']); $image['id'] = $image['value_id']; + + // @deprecated 'path' should not be used + // The file can be absent in local filesystem if remote storage is used $image['path'] = $directory->getAbsolutePath($this->getMediaConfig()->getMediaPath($image['file'])); + $images->addItem(new \Magento\Framework\DataObject($image)); } $this->setData('media_gallery_images', $images); diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php index 225a3a4c44a9b..e56fb8e59d0e9 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php @@ -11,6 +11,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\Operation\ExtensionInterface; +use Magento\Framework\Storage\StorageProvider; use Magento\MediaStorage\Model\File\Uploader as FileUploader; use Magento\Store\Model\StoreManagerInterface; @@ -88,6 +89,10 @@ class CreateHandler implements ExtensionInterface * @var \Magento\Store\Model\StoreManagerInterface */ private $storeManager; + /** + * @var StorageProvider + */ + private $storageProvider; /** * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool @@ -98,6 +103,7 @@ class CreateHandler implements ExtensionInterface * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb * @param \Magento\Store\Model\StoreManagerInterface|null $storeManager + * @param StorageProvider $storageProvider * @throws \Magento\Framework\Exception\FileSystemException */ public function __construct( @@ -108,7 +114,8 @@ public function __construct( \Magento\Catalog\Model\Product\Media\Config $mediaConfig, \Magento\Framework\Filesystem $filesystem, \Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb, - \Magento\Store\Model\StoreManagerInterface $storeManager = null + \Magento\Store\Model\StoreManagerInterface $storeManager = null, + StorageProvider $storageProvider = null ) { $this->metadata = $metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); $this->attributeRepository = $attributeRepository; @@ -118,6 +125,7 @@ public function __construct( $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); $this->fileStorageDb = $fileStorageDb; $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class); + $this->storageProvider = $storageProvider ?: ObjectManager::getInstance()->get(StorageProvider::class); } /** @@ -245,7 +253,6 @@ public function getAttribute() 'media_gallery' ); } - return $this->attribute; } @@ -290,7 +297,8 @@ protected function processNewAndExistingImages($product, array &$images) $data['position'] = isset($image['position']) ? (int)$image['position'] : 0; $data['disabled'] = isset($image['disabled']) ? (int)$image['disabled'] : 0; $data['store_id'] = (int)$product->getStoreId(); - + $stat = $this->mediaDirectory->stat($this->mediaConfig->getMediaPath($image['file'])); + $data['image_metadata']['size'] = $stat['size']; $data[$this->metadata->getLinkField()] = (int)$product->getData($this->metadata->getLinkField()); $this->resourceModel->insertGalleryValueInStore($data); @@ -366,20 +374,20 @@ protected function moveImageFromTmp($file) $file = $this->getFilenameFromTmp($this->getSafeFilename($file)); $destinationFile = $this->getUniqueFileName($file); - if ($this->fileStorageDb->checkDbUsage()) { - $this->fileStorageDb->renameFile( - $this->mediaConfig->getTmpMediaShortUrl($file), - $this->mediaConfig->getMediaShortUrl($destinationFile) - ); + $tmpMediaPath = $this->mediaConfig->getTmpMediaPath($file); + $mediaPath = $this->mediaConfig->getMediaPath($destinationFile); + $this->mediaDirectory->renameFile( + $tmpMediaPath, + $mediaPath + ); + $this->fileStorageDb->renameFile( + $this->mediaConfig->getTmpMediaShortUrl($file), + $this->mediaConfig->getMediaShortUrl($destinationFile) + ); - $this->mediaDirectory->delete($this->mediaConfig->getTmpMediaPath($file)); - $this->mediaDirectory->delete($this->mediaConfig->getMediaPath($destinationFile)); - } else { - $this->mediaDirectory->renameFile( - $this->mediaConfig->getTmpMediaPath($file), - $this->mediaConfig->getMediaPath($destinationFile) - ); - } + $storage = $this->storageProvider->get('media'); + $content = $this->mediaDirectory->readFile($mediaPath); + $storage->put($mediaPath, $content); return str_replace('\\', '/', $destinationFile); } diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php index 7c2a53768fd47..6a0032ca694a5 100644 --- a/app/code/Magento/Catalog/Model/Product/Image.php +++ b/app/code/Magento/Catalog/Model/Product/Image.php @@ -14,7 +14,8 @@ use Magento\Framework\Image as MagentoImage; use Magento\Framework\Serialize\SerializerInterface; use Magento\Catalog\Model\Product\Image\ParamsBuilder; -use Magento\Framework\Filesystem\Driver\File as FilesystemDriver; +use Magento\Framework\Storage\StorageInterface; +use Magento\Framework\Storage\StorageProvider; /** * Image operations @@ -203,9 +204,9 @@ class Image extends \Magento\Framework\Model\AbstractModel private $serializer; /** - * @var FilesystemDriver + * @var StorageInterface */ - private $filesystemDriver; + private $storage; /** * Constructor @@ -222,12 +223,12 @@ class Image extends \Magento\Framework\Model\AbstractModel * @param ImageFactory $viewAssetImageFactory * @param PlaceholderFactory $viewAssetPlaceholderFactory * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param StorageProvider $storageProvider * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data * @param SerializerInterface $serializer * @param ParamsBuilder $paramsBuilder - * @param FilesystemDriver $filesystemDriver * @throws \Magento\Framework\Exception\FileSystemException * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.UnusedLocalVariable) @@ -245,27 +246,30 @@ public function __construct( ImageFactory $viewAssetImageFactory, PlaceholderFactory $viewAssetPlaceholderFactory, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + StorageProvider $storageProvider, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], SerializerInterface $serializer = null, - ParamsBuilder $paramsBuilder = null, - FilesystemDriver $filesystemDriver = null + ParamsBuilder $paramsBuilder = null ) { $this->_storeManager = $storeManager; $this->_catalogProductMediaConfig = $catalogProductMediaConfig; - $this->_coreFileStorageDatabase = $coreFileStorageDatabase; - parent::__construct($context, $registry, $resource, $resourceCollection, $data); + $this->_mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->_coreFileStorageDatabase = $coreFileStorageDatabase; $this->_imageFactory = $imageFactory; + $this->viewAssetImageFactory = $viewAssetImageFactory; + + $this->storage = $storageProvider->get('media'); + + parent::__construct($context, $registry, $resource, $resourceCollection, $data); $this->_assetRepo = $assetRepo; $this->_viewFileSystem = $viewFileSystem; $this->_scopeConfig = $scopeConfig; - $this->viewAssetImageFactory = $viewAssetImageFactory; $this->viewAssetPlaceholderFactory = $viewAssetPlaceholderFactory; $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); $this->paramsBuilder = $paramsBuilder ?: ObjectManager::getInstance()->get(ParamsBuilder::class); - $this->filesystemDriver = $filesystemDriver ?: ObjectManager::getInstance()->get(FilesystemDriver::class); } /** @@ -433,19 +437,20 @@ public function setBaseFile($file) { $this->_isBaseFilePlaceholder = false; - $this->imageAsset = $this->viewAssetImageFactory->create( - [ - 'miscParams' => $this->getMiscParams(), - 'filePath' => $file, - ] - ); - if ($file == 'no_selection' || !$this->_fileExists($this->imageAsset->getSourceFile())) { + if ($file == 'no_selection' || empty($file)) { $this->_isBaseFilePlaceholder = true; $this->imageAsset = $this->viewAssetPlaceholderFactory->create( [ 'type' => $this->getDestinationSubdir(), ] ); + } else { + $this->imageAsset = $this->viewAssetImageFactory->create( + [ + 'miscParams' => $this->getMiscParams(), + 'filePath' => $file, + ] + ); } $this->_baseFile = $this->imageAsset->getSourceFile(); @@ -677,12 +682,7 @@ public function getDestinationSubdir() public function isCached() { $path = $this->imageAsset->getPath(); - try { - $isCached = is_array($this->loadImageInfoFromCache($path)) || $this->filesystemDriver->isExists($path); - } catch (FileSystemException $e) { - $isCached = false; - } - return $isCached; + return is_array($this->loadImageInfoFromCache($path)) || $this->_mediaDirectory->isExist($path); } /** @@ -854,35 +854,20 @@ public function clearCache() { $directory = $this->_catalogProductMediaConfig->getBaseMediaPath() . '/cache'; $this->_mediaDirectory->delete($directory); + $this->storage->deleteDir($directory); $this->_coreFileStorageDatabase->deleteFolder($this->_mediaDirectory->getAbsolutePath($directory)); $this->clearImageInfoFromCache(); } - /** - * First check this file on FS - * - * If it doesn't exist - try to download it from DB - * - * @param string $filename - * @return bool - */ - protected function _fileExists($filename) - { - if ($this->_mediaDirectory->isFile($filename)) { - return true; - } else { - return $this->_coreFileStorageDatabase->saveFileToFilesystem( - $this->_mediaDirectory->getAbsolutePath($filename) - ); - } - } - /** * Return resized product image information * * @return array * @throws NotLoadInfoImageException + * @deprecated Magento is not responsible for image resizing anymore. This method works with local filesystem only. + * Service that provides resized images should guarantee that the image sizes correspond to requested ones. + * Use `getWidth()` and `getHeight()` instead. */ public function getResizedImageInfo() { diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 14daac0147abf..12e3c6b53d701 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -12,6 +12,7 @@ use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; use Magento\Catalog\Model\Product\Gallery\ReadHandler as GalleryReadHandler; +use Magento\Catalog\Model\ResourceModel\Category; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\CatalogUrlRewrite\Model\Storage\DbStorage; @@ -23,7 +24,6 @@ use Magento\Framework\Indexer\DimensionFactory; use Magento\Store\Model\Indexer\WebsiteDimensionProvider; use Magento\Store\Model\Store; -use Magento\Catalog\Model\ResourceModel\Category; /** * Product collection @@ -2337,49 +2337,35 @@ public function addPriceDataFieldFilter($comparisonFormat, $fields) * @SuppressWarnings(PHPMD.NPathComplexity) * @since 101.0.1 * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Zend_Db_Statement_Exception */ public function addMediaGalleryData() { if ($this->getFlag('media_gallery_added')) { return $this; } - if (!$this->getSize()) { return $this; } - - $items = $this->getItems(); - $linkField = $this->getProductEntityMetadata()->getLinkField(); - - $select = $this->getMediaGalleryResource() - ->createBatchBaseSelect( - $this->getStoreId(), - $this->getAttribute('media_gallery')->getAttributeId() - )->reset( - Select::ORDER // we don't care what order is in current scenario - )->where( - 'entity.' . $linkField . ' IN (?)', - array_map( - function ($item) use ($linkField) { - return (int) $item->getOrigData($linkField); - }, - $items - ) - ); - + if (!$this->isLoaded()) { + $this->load(); + } + $records = $this->getMediaGalleryResource()->getMediaRecords( + $this->getStoreId(), + $this->getLoadedIds() + ); $mediaGalleries = []; - foreach ($this->getConnection()->fetchAll($select) as $row) { - $mediaGalleries[$row[$linkField]][] = $row; + foreach ($records as $record) { + $mediaGalleries[$record['entity_id']][] = $record; } - foreach ($items as $item) { + foreach ($this->getItems() as $item) { $this->getGalleryReadHandler() ->addMediaDataToProduct( $item, - $mediaGalleries[$item->getOrigData($linkField)] ?? [] + $mediaGalleries[$item->getId()] ?? [] ); } - $this->setFlag('media_gallery_added', true); return $this; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php index a9741cd8e1ec7..6acda0e574828 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php @@ -6,6 +6,8 @@ namespace Magento\Catalog\Model\ResourceModel\Product; +use Magento\Framework\DB\Select; +use Magento\Framework\DB\Sql\ColumnValueExpression; use Magento\Store\Model\Store; /** @@ -33,9 +35,12 @@ class Gallery extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb protected $metadata; /** + * Gallery constructor. + * * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool * @param string $connectionName + * @throws \Exception */ public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, @@ -45,7 +50,6 @@ public function __construct( $this->metadata = $metadataPool->getMetadata( \Magento\Catalog\Api\Data\ProductInterface::class ); - parent::__construct($context, $connectionName); } @@ -122,19 +126,14 @@ public function loadDataFromTableByValueId( * @param int $attributeId * @return array * @since 101.0.0 + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Zend_Db_Statement_Exception */ - public function loadProductGalleryByAttributeId($product, $attributeId) + public function loadProductGalleryByAttributeId($product, $attributeId = null) { - $select = $this->createBaseLoadSelect( - $product->getData($this->metadata->getLinkField()), - $product->getStoreId(), - $attributeId - ); - - $result = $this->getConnection()->fetchAll($select); - + $result = $this->getMediaRecords($product->getStoreId(), [$product->getId()], true); $this->removeDuplicates($result); - return $result; } @@ -144,6 +143,7 @@ public function loadProductGalleryByAttributeId($product, $attributeId) * @param int $entityId * @param int $storeId * @param int $attributeId + * @deprecated Misleading method, methods relies on autoincrement field instead of entity ID * @return \Magento\Framework\DB\Select * @throws \Magento\Framework\Exception\LocalizedException * @since 101.0.0 @@ -159,6 +159,35 @@ protected function createBaseLoadSelect($entityId, $storeId, $attributeId) return $select; } + /** + * Returns media entries from database + * + * @param int $storeId + * @param array $entityIds + * @param bool $preserveSortOrder + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Zend_Db_Statement_Exception + */ + public function getMediaRecords(int $storeId, array $entityIds, bool $preserveSortOrder = false) : array + { + $output = []; + $select = $this->createBatchBaseSelect($storeId) + ->where('cpe.entity_id IN (?)', $entityIds); + if (!$preserveSortOrder) { + // due to performance consideration it is better to do not use sorting for this query + $select->reset(Select::ORDER); + } + $cursor = $this->getConnection()->query($select); + while ($row = $cursor->fetch()) { + if (!empty($row['image_metadata'])) { + $row['image_metadata'] = $this->getSerializer()->unserialize($row['image_metadata']); + } + $output[] = $row; + } + return $output; + } + /** * Create batch base select * @@ -166,9 +195,10 @@ protected function createBaseLoadSelect($entityId, $storeId, $attributeId) * @param int $attributeId * @return \Magento\Framework\DB\Select * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) Media gallery doesn't support other attributes than media_galley * @since 101.0.1 */ - public function createBatchBaseSelect($storeId, $attributeId) + public function createBatchBaseSelect($storeId, $attributeId = null) { $linkField = $this->metadata->getLinkField(); @@ -191,6 +221,10 @@ public function createBatchBaseSelect($storeId, $attributeId) ['entity' => $this->getTable(self::GALLERY_VALUE_TO_ENTITY_TABLE)], $mainTableAlias . '.value_id = entity.value_id', [$linkField] + )->joinInner( + ['cpe' => $this->getTable('catalog_product_entity')], + sprintf('cpe.%1$s = entity.%1$s', $linkField), + ['entity_id' => 'cpe.entity_id'] )->joinLeft( ['value' => $this->getTable(self::GALLERY_VALUE_TABLE)], implode( @@ -219,16 +253,15 @@ public function createBatchBaseSelect($storeId, $attributeId) 'disabled' => $this->getConnection()->getIfNullSql('`value`.`disabled`', '`default_value`.`disabled`'), 'label_default' => 'default_value.label', 'position_default' => 'default_value.position', - 'disabled_default' => 'default_value.disabled' + 'disabled_default' => 'default_value.disabled', + 'image_metadata' => new ColumnValueExpression( + 'JSON_MERGE_PATCH(default_value.image_metadata, value.image_metadata)' + ) ])->where( - $mainTableAlias . '.attribute_id = ?', - $attributeId - )->where( $mainTableAlias . '.disabled = 0' )->order( $positionCheckSql . ' ' . \Magento\Framework\DB\Select::SQL_ASC ); - return $select; } @@ -357,6 +390,9 @@ public function insertGalleryValueInStore($data) $this->getTable(self::GALLERY_VALUE_TABLE) ); + if (!empty($data['image_metadata'])) { + $data['image_metadata'] = $this->getSerializer()->serialize($data['image_metadata']); + } $this->getConnection()->insert( $this->getTable(self::GALLERY_VALUE_TABLE), $data diff --git a/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php b/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php index 49d150a31750c..ead68c897f95f 100644 --- a/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php +++ b/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php @@ -47,11 +47,10 @@ public function __construct( $this->mediaConfig = $mediaConfig; $this->filesystem = $filesystem; $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); - $this->mediaDirectory->create($this->mediaConfig->getBaseMediaPath()); } /** - * {@inheritdoc} + * @inheritdoc */ public function getPath() { @@ -59,7 +58,7 @@ public function getPath() } /** - * {@inheritdoc} + * @inheritdoc */ public function getBaseUrl() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php deleted file mode 100644 index 23f0aec5b69a2..0000000000000 --- a/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php +++ /dev/null @@ -1,153 +0,0 @@ -eavConfig = $this->getMockBuilder(\Magento\Eav\Model\Config::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->attributeCollection = $this->getMockBuilder( - \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection::class - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->attributeCollectionFactory = $this->getMockBuilder( - \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory::class - ) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $this->attributeCollectionFactory->expects($this->any())->method('create')->will( - $this->returnValue($this->attributeCollection) - ); - - $this->attribute = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->escaperMock = $this->getMockBuilder( - \Magento\Framework\Escaper::class - ) - ->disableOriginalConstructor() - ->setMethods(['escapeHtml']) - ->getMock(); - - $helper = new ObjectManager($this); - $this->model = $helper->getObject( - \Magento\Catalog\Model\Config\CatalogClone\Media\Image::class, - [ - 'eavConfig' => $this->eavConfig, - 'attributeCollectionFactory' => $this->attributeCollectionFactory, - 'escaper' => $this->escaperMock, - ] - ); - } - - /** - * @param string $actualLabel - * @param string $expectedLabel - * @return void - * - * @dataProvider getPrefixesDataProvider - */ - public function testGetPrefixes(string $actualLabel, string $expectedLabel): void - { - $entityTypeId = 3; - /** @var \Magento\Eav\Model\Entity\Type|\PHPUnit_Framework_MockObject_MockObject $entityType */ - $entityType = $this->getMockBuilder(\Magento\Eav\Model\Entity\Type::class) - ->disableOriginalConstructor() - ->getMock(); - $entityType->expects($this->once())->method('getId')->willReturn($entityTypeId); - - /** @var AbstractFrontend|\PHPUnit_Framework_MockObject_MockObject $frontend */ - $frontend = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend::class) - ->setMethods(['getLabel']) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $frontend->expects($this->once())->method('getLabel')->willReturn($actualLabel); - - $this->attributeCollection->expects($this->once())->method('setEntityTypeFilter')->with($entityTypeId); - $this->attributeCollection->expects($this->once())->method('setFrontendInputTypeFilter')->with('media_image'); - - $this->attribute->expects($this->once())->method('getAttributeCode')->willReturn('attributeCode'); - $this->attribute->expects($this->once())->method('getFrontend')->willReturn($frontend); - - $this->attributeCollection->expects($this->any())->method('getIterator') - ->willReturn(new \ArrayIterator([$this->attribute])); - - $this->eavConfig->expects($this->any())->method('getEntityType')->with(Product::ENTITY) - ->willReturn($entityType); - - $this->escaperMock->expects($this->once())->method('escapeHtml')->with($actualLabel) - ->willReturn($expectedLabel); - - $this->assertEquals([['field' => 'attributeCode_', 'label' => $expectedLabel]], $this->model->getPrefixes()); - } - - /** - * @return array - */ - public function getPrefixesDataProvider(): array - { - return [ - [ - 'actual_label' => 'testLabel', - 'expected_label' => 'testLabel', - ], - [ - 'actual_label' => ' '<media-image-attributelabel', - ], - ]; - } -} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php index 0316b2e374d2f..b49decf96452d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php @@ -230,46 +230,6 @@ public function testAddProductCategoriesFilter() $this->collection->addCategoriesFilter([$conditionType => $values]); } - public function testAddMediaGalleryData() - { - $attributeId = 42; - $rowId = 4; - $linkField = 'row_id'; - $mediaGalleriesMock = [[$linkField => $rowId]]; - $itemMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->disableOriginalConstructor() - ->setMethods(['getOrigData']) - ->getMock(); - $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) - ->disableOriginalConstructor() - ->getMock(); - $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) - ->disableOriginalConstructor() - ->getMock(); - $metadataMock = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->collection->addItem($itemMock); - $this->galleryResourceMock->expects($this->once())->method('createBatchBaseSelect')->willReturn($selectMock); - $attributeMock->expects($this->once())->method('getAttributeId')->willReturn($attributeId); - $this->entityMock->expects($this->once())->method('getAttribute')->willReturn($attributeMock); - $itemMock->expects($this->atLeastOnce())->method('getOrigData')->willReturn($rowId); - $selectMock->expects($this->once())->method('reset')->with(Select::ORDER)->willReturnSelf(); - $selectMock->expects($this->once())->method('where')->with('entity.' . $linkField . ' IN (?)', [$rowId]) - ->willReturnSelf(); - $this->metadataPoolMock->expects($this->once())->method('getMetadata')->willReturn($metadataMock); - $metadataMock->expects($this->once())->method('getLinkField')->willReturn($linkField); - - $this->connectionMock->expects($this->once())->method('fetchOne')->with($selectMock)->willReturn(42); - $this->connectionMock->expects($this->once())->method('fetchAll')->with($selectMock)->willReturn( - [['row_id' => $rowId]] - ); - $this->galleryReadHandlerMock->expects($this->once())->method('addMediaDataToProduct') - ->with($itemMock, $mediaGalleriesMock); - - $this->assertSame($this->collection, $this->collection->addMediaGalleryData()); - } - /** * Test addTierPriceDataByGroupId method. * diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php index 47ef3c999125f..43c1abc91a1a9 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Product; +use Magento\Framework\DB\Sql\ColumnValueExpression; + /** * Unit test for product media gallery resource. */ @@ -280,200 +282,4 @@ public function testBindValueToEntityRecordExists() $entityId = 1; $this->resource->bindValueToEntity($valueId, $entityId); } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testLoadGallery() - { - $productId = 5; - $storeId = 1; - $attributeId = 6; - $getTableReturnValue = 'table'; - $quoteInfoReturnValue = - 'main.value_id = value.value_id AND value.store_id = ' . $storeId - . ' AND value.entity_id = entity.entity_id'; - $quoteDefaultInfoReturnValue = - 'main.value_id = default_value.value_id AND default_value.store_id = 0' - . ' AND default_value.entity_id = entity.entity_id'; - - $positionCheckSql = 'testchecksql'; - $resultRow = [ - [ - 'value_id' => '1', - 'file' => '/d/o/download_7.jpg', - 'label' => null, - 'position' => '1', - 'disabled' => '0', - 'label_default' => null, - 'position_default' => '1', - 'disabled_default' => '0', - ], - ]; - - $this->connection->expects($this->once())->method('getCheckSql')->with( - 'value.position IS NULL', - 'default_value.position', - 'value.position' - )->will($this->returnValue($positionCheckSql)); - $this->connection->expects($this->once())->method('select')->will($this->returnValue($this->select)); - $this->select->expects($this->at(0))->method('from')->with( - [ - 'main' => $getTableReturnValue, - ], - [ - 'value_id', - 'file' => 'value', - 'media_type' - ] - )->willReturnSelf(); - $this->select->expects($this->at(1))->method('joinInner')->with( - ['entity' => $getTableReturnValue], - 'main.value_id = entity.value_id', - ['entity_id'] - )->willReturnSelf(); - $this->product->expects($this->at(0))->method('getData') - ->with('entity_id')->willReturn($productId); - $this->product->expects($this->at(1))->method('getStoreId')->will($this->returnValue($storeId)); - $this->connection->expects($this->exactly(2))->method('quoteInto')->withConsecutive( - ['value.store_id = ?'], - ['default_value.store_id = ?'] - )->willReturnOnConsecutiveCalls( - 'value.store_id = ' . $storeId, - 'default_value.store_id = ' . 0 - ); - $this->connection->expects($this->any())->method('getIfNullSql')->will( - $this->returnValueMap([ - [ - '`value`.`label`', - '`default_value`.`label`', - 'IFNULL(`value`.`label`, `default_value`.`label`)' - ], - [ - '`value`.`position`', - '`default_value`.`position`', - 'IFNULL(`value`.`position`, `default_value`.`position`)' - ], - [ - '`value`.`disabled`', - '`default_value`.`disabled`', - 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)' - ] - ]) - ); - $this->select->expects($this->at(2))->method('joinLeft')->with( - ['value' => $getTableReturnValue], - $quoteInfoReturnValue, - [] - )->willReturnSelf(); - $this->select->expects($this->at(3))->method('joinLeft')->with( - ['default_value' => $getTableReturnValue], - $quoteDefaultInfoReturnValue, - [] - )->willReturnSelf(); - $this->select->expects($this->at(4))->method('columns')->with([ - 'label' => 'IFNULL(`value`.`label`, `default_value`.`label`)', - 'position' => 'IFNULL(`value`.`position`, `default_value`.`position`)', - 'disabled' => 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)', - 'label_default' => 'default_value.label', - 'position_default' => 'default_value.position', - 'disabled_default' => 'default_value.disabled' - ])->willReturnSelf(); - $this->select->expects($this->at(5))->method('where')->with( - 'main.attribute_id = ?', - $attributeId - )->willReturnSelf(); - $this->select->expects($this->at(6))->method('where') - ->with('main.disabled = 0')->willReturnSelf(); - $this->select->expects($this->at(8))->method('where') - ->with('entity.entity_id = ?', $productId) - ->willReturnSelf(); - $this->select->expects($this->once())->method('order') - ->with($positionCheckSql . ' ' . \Magento\Framework\DB\Select::SQL_ASC) - ->willReturnSelf(); - $this->connection->expects($this->once())->method('fetchAll') - ->with($this->select) - ->willReturn($resultRow); - - $this->assertEquals($resultRow, $this->resource->loadProductGalleryByAttributeId($this->product, $attributeId)); - } - - public function testInsertGalleryValueInStore() - { - $data = [ - 'value_id' => '8', - 'store_id' => 0, - 'provider' => '', - 'url' => 'https://www.youtube.com/watch?v=abcdfghijk', - 'title' => 'New Title', - 'description' => 'New Description', - 'metadata' => 'New metadata', - ]; - - $this->connection->expects($this->once())->method('describeTable')->willReturn($this->fields); - $this->connection->expects($this->any())->method('prepareColumnValue')->willReturnOnConsecutiveCalls( - '8', - 0, - '', - 'https://www.youtube.com/watch?v=abcdfghijk', - 'New Title', - 'New Description', - 'New metadata' - ); - - $this->resource->insertGalleryValueInStore($data); - } - - public function testDeleteGalleryValueInStore() - { - $valueId = 4; - $entityId = 6; - $storeId = 1; - - $this->connection->expects($this->exactly(3))->method('quoteInto')->withConsecutive( - ['value_id = ?', (int)$valueId], - ['entity_id = ?', (int)$entityId], - ['store_id = ?', (int)$storeId] - )->willReturnOnConsecutiveCalls( - 'value_id = ' . $valueId, - 'entity_id = ' . $entityId, - 'store_id = ' . $storeId - ); - - $this->connection->expects($this->once())->method('delete')->with( - 'table', - 'value_id = 4 AND entity_id = 6 AND store_id = 1' - )->willReturnSelf(); - - $this->resource->deleteGalleryValueInStore($valueId, $entityId, $storeId); - } - - public function testCountImageUses() - { - $results = [ - [ - 'value_id' => '1', - 'attribute_id' => 90, - 'value' => '/d/o/download_7.jpg', - 'media_type' => 'image', - 'disabled' => '0', - ], - ]; - - $this->connection->expects($this->once())->method('select')->will($this->returnValue($this->select)); - $this->select->expects($this->at(0))->method('from')->with( - [ - 'main' => 'table', - ], - '*' - )->willReturnSelf(); - $this->select->expects($this->at(1))->method('where')->with( - 'value = ?', - 1 - )->willReturnSelf(); - $this->connection->expects($this->once())->method('fetchAll') - ->with($this->select) - ->willReturn($results); - $this->assertEquals($this->resource->countImageUses(1), count($results)); - } } diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml index d5b318f671726..9f43c8a69b5e5 100644 --- a/app/code/Magento/Catalog/etc/db_schema.xml +++ b/app/code/Magento/Catalog/etc/db_schema.xml @@ -813,6 +813,7 @@ default="0" comment="Is Disabled"/> + diff --git a/app/code/Magento/Catalog/etc/db_schema_whitelist.json b/app/code/Magento/Catalog/etc/db_schema_whitelist.json index d4bd6927d4345..a9b5dd2084c35 100644 --- a/app/code/Magento/Catalog/etc/db_schema_whitelist.json +++ b/app/code/Magento/Catalog/etc/db_schema_whitelist.json @@ -479,7 +479,8 @@ "label": true, "position": true, "disabled": true, - "record_id": true + "record_id": true, + "image_metadata": true }, "index": { "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_STORE_ID": true, diff --git a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php index ec69baeb92cb9..034f68bdab9d4 100644 --- a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php +++ b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php @@ -314,7 +314,8 @@ protected function prepareVariations() 'canEdit' => 0, 'newProduct' => 0, 'attributes' => $this->getTextAttributes($variationOptions), - 'thumbnail_image' => $this->imageHelper->init($product, 'product_thumbnail_image')->getUrl(), + 'thumbnail_image' => $this->imageHelper->init($product, 'product_thumbnail_image') + ->getUrl(), '__disableTmpl' => true ]; $productIds[] = $product->getId(); diff --git a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php index d592a004e111a..526774a3e6bcd 100644 --- a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php +++ b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php @@ -86,8 +86,7 @@ protected function configure() $this->setName('catalog:images:resize') ->setDescription( 'Creates resized product images ' . - '(Not relevant when image resizing is offloaded from Magento. ' . - 'See https://docs.magento.com/m2/ee/user_guide/configuration/general/web.html#url-options )' + '(Deprecated: see https://docs.magento.com/m2/ee/user_guide/configuration/general/web.html#url-options' ) ->setDefinition($this->getOptionsList()); } diff --git a/app/code/Magento/MediaStorage/Service/ImageResize.php b/app/code/Magento/MediaStorage/Service/ImageResize.php index d061ddbd3dc46..145fcd1b85f4f 100644 --- a/app/code/Magento/MediaStorage/Service/ImageResize.php +++ b/app/code/Magento/MediaStorage/Service/ImageResize.php @@ -19,6 +19,7 @@ use Magento\Framework\Image\Factory as ImageFactory; use Magento\Catalog\Model\Product\Media\ConfigInterface as MediaConfig; use Magento\Framework\App\State; +use Magento\Framework\Storage\StorageProvider; use Magento\Framework\View\ConfigInterface as ViewConfig; use \Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; use Magento\Store\Model\StoreManagerInterface; @@ -99,6 +100,11 @@ class ImageResize */ private $storeManager; + /** + * @var StorageProvider + */ + private $storageProvider; + /** * @param State $appState * @param MediaConfig $imageConfig @@ -112,6 +118,7 @@ class ImageResize * @param Filesystem $filesystem * @param Database $fileStorageDatabase * @param StoreManagerInterface $storeManager + * @param StorageProvider $storageProvider * @throws \Magento\Framework\Exception\FileSystemException * @internal param ProductImage $gallery * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -128,7 +135,8 @@ public function __construct( Collection $themeCollection, Filesystem $filesystem, Database $fileStorageDatabase = null, - StoreManagerInterface $storeManager = null + StoreManagerInterface $storeManager = null, + StorageProvider $storageProvider = null ) { $this->appState = $appState; $this->imageConfig = $imageConfig; @@ -144,6 +152,7 @@ public function __construct( $this->fileStorageDatabase = $fileStorageDatabase ?: ObjectManager::getInstance()->get(Database::class); $this->storeManager = $storeManager ?? ObjectManager::getInstance()->get(StoreManagerInterface::class); + $this->storageProvider = $storageProvider ?? ObjectManager::getInstance()->get(StorageProvider::class); } /** @@ -337,10 +346,15 @@ private function resize(array $imageParams, string $originalImagePath, string $o $image->save($imageAsset->getPath()); + $mediastoragefilename = $this->mediaDirectory->getRelativePath($imageAsset->getPath()); if ($this->fileStorageDatabase->checkDbUsage()) { - $mediastoragefilename = $this->mediaDirectory->getRelativePath($imageAsset->getPath()); $this->fileStorageDatabase->saveFile($mediastoragefilename); } + + $this->storageProvider->get('media')->put( + $mediastoragefilename, + $this->mediaDirectory->readFile($mediastoragefilename) + ); } /** diff --git a/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php b/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php index f0e1efa7806e4..74913b444e63a 100644 --- a/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php +++ b/app/code/Magento/MediaStorage/Test/Unit/Service/ImageResizeTest.php @@ -6,25 +6,27 @@ namespace Magento\MediaStorage\Test\Unit\Service; use Magento\Catalog\Model\Product\Image\ParamsBuilder; -use Magento\Catalog\Model\View\Asset\ImageFactory as AssetImageFactory; +use Magento\Catalog\Model\Product\Media\ConfigInterface as MediaConfig; +use Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; use Magento\Catalog\Model\View\Asset\Image as AssetImage; +use Magento\Catalog\Model\View\Asset\ImageFactory as AssetImageFactory; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\State; +use Magento\Framework\Config\View; use Magento\Framework\DataObject; use Magento\Framework\Filesystem; -use Magento\Framework\Image\Factory as ImageFactory; use Magento\Framework\Image; -use Magento\Catalog\Model\Product\Media\ConfigInterface as MediaConfig; -use Magento\Framework\App\State; +use Magento\Framework\Image\Factory as ImageFactory; +use Magento\Framework\Storage\StorageInterface; use Magento\Framework\View\ConfigInterface as ViewConfig; -use Magento\Framework\Config\View; -use Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; +use Magento\MediaStorage\Helper\File\Storage\Database; +use Magento\MediaStorage\Service\ImageResize; use Magento\Store\Model\StoreManagerInterface; use Magento\Theme\Model\Config\Customization as ThemeCustomizationConfig; use Magento\Theme\Model\ResourceModel\Theme\Collection; -use Magento\MediaStorage\Helper\File\Storage\Database; -use Magento\Framework\App\Filesystem\DirectoryList; /** - * Class ImageResizeTest + * Class ImageResizeTest test for \Magento\MediaStorage\Service\ImageResize * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -32,7 +34,7 @@ class ImageResizeTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\MediaStorage\Service\ImageResize + * @var ImageResize */ protected $service; @@ -120,11 +122,17 @@ class ImageResizeTest extends \PHPUnit\Framework\TestCase * @var string */ private $testfilepath; + /** * @var \PHPUnit\Framework\MockObject\MockObject|StoreManagerInterface */ private $storeManager; + /** + * @var StorageInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storage; + /** * @inheritDoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -149,10 +157,16 @@ protected function setUp() $this->filesystemMock = $this->createMock(Filesystem::class); $this->databaseMock = $this->createMock(Database::class); $this->storeManager = $this->getMockForAbstractClass(StoreManagerInterface::class); + $storageProvider = $this->createMock(\Magento\Framework\Storage\StorageProvider::class); + $this->storage = $this->getMockForAbstractClass(StorageInterface::class); + $storageProvider->expects($this->any()) + ->method('get') + ->with('media') + ->willReturn($this->storage); $this->mediaDirectoryMock = $this->getMockBuilder(Filesystem::class) ->disableOriginalConstructor() - ->setMethods(['getAbsolutePath','isFile','getRelativePath']) + ->setMethods(['getAbsolutePath','isFile','getRelativePath', 'readFile']) ->getMock(); $this->filesystemMock->expects($this->any()) @@ -201,8 +215,7 @@ protected function setUp() $this->viewMock->expects($this->any()) ->method('getMediaEntities') ->willReturn( - ['product_small_image' => - [ + ['product_small_image' => [ 'type' => 'small_image', 'width' => 75, 'height' => 75 @@ -223,7 +236,7 @@ protected function setUp() ->method('getStores') ->willReturn([$store]); - $this->service = new \Magento\MediaStorage\Service\ImageResize( + $this->service = new ImageResize( $this->appStateMock, $this->imageConfigMock, $this->productImageMock, @@ -235,7 +248,8 @@ protected function setUp() $this->themeCollectionMock, $this->filesystemMock, $this->databaseMock, - $this->storeManager + $this->storeManager, + $storageProvider ); } @@ -278,6 +292,14 @@ function () { ->method('saveFile') ->with($this->testfilepath); + $this->mediaDirectoryMock->expects($this->any()) + ->method('readFile') + ->with($this->testfilepath) + ->willReturn('image data'); + $this->storage->expects($this->once()) + ->method('put') + ->with($this->testfilepath, 'image data'); + $generator = $this->service->resizeFromThemes(['test-theme']); while ($generator->valid()) { $generator->next(); @@ -316,6 +338,14 @@ public function testResizeFromImageNameMediaStorageDatabase() ->method('saveFile') ->with($this->testfilepath); + $this->mediaDirectoryMock->expects($this->any()) + ->method('readFile') + ->with($this->testfilepath) + ->willReturn('image data'); + $this->storage->expects($this->once()) + ->method('put') + ->with($this->testfilepath, 'image data'); + $this->service->resizeFromImageName($this->testfilename); } } diff --git a/app/code/Magento/MediaStorage/etc/di.xml b/app/code/Magento/MediaStorage/etc/di.xml index 5cdcbb3b2b9a9..061c3be1bbe4a 100644 --- a/app/code/Magento/MediaStorage/etc/di.xml +++ b/app/code/Magento/MediaStorage/etc/di.xml @@ -31,4 +31,11 @@ Magento\MediaStorage\Service\ImageResizeScheduler\Proxy + + + + pub/media + + + diff --git a/app/code/Magento/Theme/Test/Unit/Block/Adminhtml/Wysiwyg/Files/ContentTest.php b/app/code/Magento/Theme/Test/Unit/Block/Adminhtml/Wysiwyg/Files/ContentTest.php deleted file mode 100644 index 7fe3b25cf97b2..0000000000000 --- a/app/code/Magento/Theme/Test/Unit/Block/Adminhtml/Wysiwyg/Files/ContentTest.php +++ /dev/null @@ -1,220 +0,0 @@ -_helperStorage = $this->createMock(\Magento\Theme\Helper\Storage::class); - $this->_urlBuilder = $this->createMock(\Magento\Backend\Model\Url::class); - $this->_request = $this->createMock(\Magento\Framework\App\RequestInterface::class); - - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $constructArguments = $objectManagerHelper->getConstructArguments( - \Magento\Theme\Block\Adminhtml\Wysiwyg\Files\Content::class, - [ - 'urlBuilder' => $this->_urlBuilder, - 'request' => $this->_request, - 'storageHelper' => $this->_helperStorage - ] - ); - $this->_filesContent = $objectManagerHelper->getObject( - \Magento\Theme\Block\Adminhtml\Wysiwyg\Files\Content::class, - $constructArguments - ); - } - - /** - * @dataProvider requestParamsProvider - * @param array $requestParams - */ - public function testGetNewFolderUrl($requestParams) - { - $expectedUrl = 'some_url'; - - $this->_helperStorage->expects( - $this->once() - )->method( - 'getRequestParams' - )->will( - $this->returnValue($requestParams) - ); - - $this->_urlBuilder->expects( - $this->once() - )->method( - 'getUrl' - )->with( - 'adminhtml/*/newFolder', - $requestParams - )->will( - $this->returnValue($expectedUrl) - ); - - $this->assertEquals($expectedUrl, $this->_filesContent->getNewfolderUrl()); - } - - /** - * @dataProvider requestParamsProvider - * @param array $requestParams - */ - public function testGetDeleteFilesUrl($requestParams) - { - $expectedUrl = 'some_url'; - - $this->_helperStorage->expects( - $this->once() - )->method( - 'getRequestParams' - )->will( - $this->returnValue($requestParams) - ); - - $this->_urlBuilder->expects( - $this->once() - )->method( - 'getUrl' - )->with( - 'adminhtml/*/deleteFiles', - $requestParams - )->will( - $this->returnValue($expectedUrl) - ); - - $this->assertEquals($expectedUrl, $this->_filesContent->getDeleteFilesUrl()); - } - - /** - * @dataProvider requestParamsProvider - * @param array $requestParams - */ - public function testGetOnInsertUrl($requestParams) - { - $expectedUrl = 'some_url'; - - $this->_helperStorage->expects( - $this->once() - )->method( - 'getRequestParams' - )->will( - $this->returnValue($requestParams) - ); - - $this->_urlBuilder->expects( - $this->once() - )->method( - 'getUrl' - )->with( - 'adminhtml/*/onInsert', - $requestParams - )->will( - $this->returnValue($expectedUrl) - ); - - $this->assertEquals($expectedUrl, $this->_filesContent->getOnInsertUrl()); - } - - /** - * Data provider for requestParams - * @return array - */ - public function requestParamsProvider() - { - return [ - [ - 'requestParams' => [ - \Magento\Theme\Helper\Storage::PARAM_THEME_ID => 1, - \Magento\Theme\Helper\Storage::PARAM_CONTENT_TYPE => Storage::TYPE_IMAGE, - \Magento\Theme\Helper\Storage::PARAM_NODE => 'root', - ] - ] - ]; - } - - public function testGetTargetElementId() - { - $expectedRequest = 'some_request'; - - $this->_request->expects( - $this->once() - )->method( - 'getParam' - )->with( - 'target_element_id' - )->will( - $this->returnValue($expectedRequest) - ); - - $this->assertEquals($expectedRequest, $this->_filesContent->getTargetElementId()); - } - - public function testGetContentsUrl() - { - $expectedUrl = 'some_url'; - - $expectedRequest = 'some_request'; - - $requestParams = [ - \Magento\Theme\Helper\Storage::PARAM_THEME_ID => 1, - \Magento\Theme\Helper\Storage::PARAM_CONTENT_TYPE => Storage::TYPE_IMAGE, - \Magento\Theme\Helper\Storage::PARAM_NODE => 'root', - ]; - - $this->_urlBuilder->expects( - $this->once() - )->method( - 'getUrl' - )->with( - 'adminhtml/*/contents', - ['type' => $expectedRequest] + $requestParams - )->will( - $this->returnValue($expectedUrl) - ); - - $this->_request->expects( - $this->once() - )->method( - 'getParam' - )->with( - 'type' - )->will( - $this->returnValue($expectedRequest) - ); - - $this->_helperStorage->expects( - $this->once() - )->method( - 'getRequestParams' - )->will( - $this->returnValue($requestParams) - ); - - $this->assertEquals($expectedUrl, $this->_filesContent->getContentsUrl()); - } -} diff --git a/app/etc/di.xml b/app/etc/di.xml index a11b8fd5a2506..85d05b6be38fa 100644 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -1814,4 +1814,13 @@ configured_block_cache + + + + Magento\Framework\Storage\AdapterFactory\LocalFactory + Magento\Framework\Storage\AdapterFactory\AwsS3Factory + Magento\Framework\Storage\AdapterFactory\AzureFactory + + + diff --git a/composer.json b/composer.json index db34b0a9c2fd0..ac005f9da6a1e 100644 --- a/composer.json +++ b/composer.json @@ -35,11 +35,14 @@ "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1", + "guzzlehttp/guzzle": "^6.3.3", + "league/flysystem": "^1.0", + "league/flysystem-aws-s3-v3": "^1.0", + "league/flysystem-azure-blob-storage": "^0.1.6", "magento/composer": "1.6.x-dev", "magento/magento-composer-installer": ">=0.1.11", "magento/zendframework1": "~1.14.2", "monolog/monolog": "^1.17", - "wikimedia/less.php": "~1.8.0", "paragonie/sodium_compat": "^1.6", "pelago/emogrifier": "^2.0.0", "php-amqplib/php-amqplib": "~2.7.0||~2.10.0", @@ -52,6 +55,7 @@ "tedivm/jshrink": "~1.3.0", "tubalmartin/cssmin": "4.1.1", "webonyx/graphql-php": "^0.13.8", + "wikimedia/less.php": "~1.8.0", "zendframework/zend-captcha": "^2.7.1", "zendframework/zend-code": "~3.3.0", "zendframework/zend-config": "^2.6.0", @@ -79,8 +83,7 @@ "zendframework/zend-text": "^2.6.0", "zendframework/zend-uri": "^2.5.1", "zendframework/zend-validator": "^2.6.0", - "zendframework/zend-view": "~2.11.2", - "guzzlehttp/guzzle": "^6.3.3" + "zendframework/zend-view": "~2.11.2" }, "require-dev": { "allure-framework/allure-phpunit": "~1.2.0", diff --git a/composer.lock b/composer.lock index 144614ba2279d..1236d2b0ae82f 100644 --- a/composer.lock +++ b/composer.lock @@ -1,11 +1,95 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "d9bed7b45c83f9133bdec76acac8b796", + "content-hash": "522d676db5baf5864a824409c54948fc", "packages": [ + { + "name": "aws/aws-sdk-php", + "version": "3.133.24", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "726426e1514be5220d55ecf02eb1f938a3b4a105" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/726426e1514be5220d55ecf02eb1f938a3b4a105", + "reference": "726426e1514be5220d55ecf02eb1f938a3b4a105", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4.1", + "mtdowling/jmespath.php": "^2.5", + "php": ">=5.5" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", + "nette/neon": "^2.3", + "phpunit/phpunit": "^4.8.35|^5.4.3", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Aws\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "time": "2020-02-27T19:13:45+00:00" + }, { "name": "braintree/braintree_php", "version": "3.35.0", @@ -257,16 +341,16 @@ }, { "name": "composer/composer", - "version": "1.9.2", + "version": "1.9.3", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "7a04aa0201ddaa0b3cf64d41022bd8cdcd7fafeb" + "reference": "1291a16ce3f48bfdeca39d64fca4875098af4d7b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/7a04aa0201ddaa0b3cf64d41022bd8cdcd7fafeb", - "reference": "7a04aa0201ddaa0b3cf64d41022bd8cdcd7fafeb", + "url": "https://api.github.com/repos/composer/composer/zipball/1291a16ce3f48bfdeca39d64fca4875098af4d7b", + "reference": "1291a16ce3f48bfdeca39d64fca4875098af4d7b", "shasum": "" }, "require": { @@ -333,7 +417,7 @@ "dependency", "package" ], - "time": "2020-01-14T15:30:32+00:00" + "time": "2020-02-04T11:58:49+00:00" }, { "name": "composer/semver", @@ -398,16 +482,16 @@ }, { "name": "composer/spdx-licenses", - "version": "1.5.2", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5" + "reference": "0c3e51e1880ca149682332770e25977c70cf9dae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7ac1e6aec371357df067f8a688c3d6974df68fa5", - "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/0c3e51e1880ca149682332770e25977c70cf9dae", + "reference": "0c3e51e1880ca149682332770e25977c70cf9dae", "shasum": "" }, "require": { @@ -454,7 +538,7 @@ "spdx", "validator" ], - "time": "2019-07-29T10:31:59+00:00" + "time": "2020-02-14T07:44:31+00:00" }, { "name": "composer/xdebug-handler", @@ -950,6 +1034,178 @@ ], "time": "2019-09-25T14:49:45+00:00" }, + { + "name": "league/flysystem", + "version": "1.0.64", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "d13c43dbd4b791f815215959105a008515d1a2e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/d13c43dbd4b791f815215959105a008515d1a2e0", + "reference": "d13c43dbd4b791f815215959105a008515d1a2e0", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": ">=5.5.9" + }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" + }, + "require-dev": { + "phpspec/phpspec": "^3.4", + "phpunit/phpunit": "^5.7.26" + }, + "suggest": { + "ext-fileinfo": "Required for MimeType", + "ext-ftp": "Allows you to use FTP server storage", + "ext-openssl": "Allows you to use FTPS server storage", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", + "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "time": "2020-02-05T18:14:17+00:00" + }, + { + "name": "league/flysystem-aws-s3-v3", + "version": "1.0.24", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", + "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4382036bde5dc926f9b8b337e5bdb15e5ec7b570", + "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570", + "shasum": "" + }, + "require": { + "aws/aws-sdk-php": "^3.0.0", + "league/flysystem": "^1.0.40", + "php": ">=5.5.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "~1.0.1", + "phpspec/phpspec": "^2.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\AwsS3v3\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Flysystem adapter for the AWS S3 SDK v3.x", + "time": "2020-02-23T13:31:58+00:00" + }, + { + "name": "league/flysystem-azure-blob-storage", + "version": "0.1.6", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-azure-blob-storage.git", + "reference": "97215345f3c42679299ba556a4d16d4847ee7f6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-azure-blob-storage/zipball/97215345f3c42679299ba556a4d16d4847ee7f6d", + "reference": "97215345f3c42679299ba556a4d16d4847ee7f6d", + "shasum": "" + }, + "require": { + "guzzlehttp/psr7": "^1.5", + "league/flysystem": "^1.0", + "microsoft/azure-storage-blob": "^1.1", + "php": ">=5.6" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\AzureBlobStorage\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "time": "2019-06-07T20:42:16+00:00" + }, { "name": "magento/composer", "version": "1.6.x-dev", @@ -1112,6 +1368,94 @@ ], "time": "2019-11-26T15:09:40+00:00" }, + { + "name": "microsoft/azure-storage-blob", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/Azure/azure-storage-blob-php.git", + "reference": "6a333cd28a3742c3e99e79042dc6510f9f917919" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Azure/azure-storage-blob-php/zipball/6a333cd28a3742c3e99e79042dc6510f9f917919", + "reference": "6a333cd28a3742c3e99e79042dc6510f9f917919", + "shasum": "" + }, + "require": { + "microsoft/azure-storage-common": "~1.4", + "php": ">=5.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "MicrosoftAzure\\Storage\\Blob\\": "src/Blob" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Azure Storage PHP Client Library", + "email": "dmsh@microsoft.com" + } + ], + "description": "This project provides a set of PHP client libraries that make it easy to access Microsoft Azure Storage Blob APIs.", + "keywords": [ + "azure", + "blob", + "php", + "sdk", + "storage" + ], + "time": "2020-01-02T07:18:59+00:00" + }, + { + "name": "microsoft/azure-storage-common", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/Azure/azure-storage-common-php.git", + "reference": "be4df800761d0d0fa91a9460c7f42517197d57a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Azure/azure-storage-common-php/zipball/be4df800761d0d0fa91a9460c7f42517197d57a0", + "reference": "be4df800761d0d0fa91a9460c7f42517197d57a0", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "~6.0", + "php": ">=5.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "MicrosoftAzure\\Storage\\Common\\": "src/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Azure Storage PHP Client Library", + "email": "dmsh@microsoft.com" + } + ], + "description": "This project provides a set of common code shared by Azure Storage Blob, Table, Queue and File PHP client libraries.", + "keywords": [ + "azure", + "common", + "php", + "sdk", + "storage" + ], + "time": "2020-01-02T07:15:54+00:00" + }, { "name": "monolog/monolog", "version": "1.25.3", @@ -1190,6 +1534,63 @@ ], "time": "2019-12-20T14:15:16+00:00" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "52168cb9472de06979613d365c7f1ab8798be895" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895", + "reference": "52168cb9472de06979613d365c7f1ab8798be895", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "symfony/polyfill-mbstring": "^1.4" + }, + "require-dev": { + "composer/xdebug-handler": "^1.2", + "phpunit/phpunit": "^4.8.36|^7.5.15" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-4": { + "JmesPath\\": "src/" + }, + "files": [ + "src/JmesPath.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "time": "2019-12-30T18:03:34+00:00" + }, { "name": "paragonie/random_compat", "version": "v9.99.99", @@ -1515,16 +1916,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.23", + "version": "2.0.25", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "c78eb5058d5bb1a183133c36d4ba5b6675dfa099" + "reference": "c18159618ed7cd7ff721ac1a8fec7860a475d2f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c78eb5058d5bb1a183133c36d4ba5b6675dfa099", - "reference": "c78eb5058d5bb1a183133c36d4ba5b6675dfa099", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c18159618ed7cd7ff721ac1a8fec7860a475d2f0", + "reference": "c18159618ed7cd7ff721ac1a8fec7860a475d2f0", "shasum": "" }, "require": { @@ -1603,7 +2004,7 @@ "x.509", "x509" ], - "time": "2019-09-17T03:41:22+00:00" + "time": "2020-02-25T04:16:50+00:00" }, { "name": "psr/container", @@ -1970,16 +2371,16 @@ }, { "name": "seld/phar-utils", - "version": "1.0.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "84715761c35808076b00908a20317a3a8a67d17e" + "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/84715761c35808076b00908a20317a3a8a67d17e", - "reference": "84715761c35808076b00908a20317a3a8a67d17e", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8800503d56b9867d43d9c303b9cbcc26016e82f0", + "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0", "shasum": "" }, "require": { @@ -2008,22 +2409,22 @@ ], "description": "PHAR file format utilities, for when PHP phars you up", "keywords": [ - "phra" + "phar" ], - "time": "2020-01-13T10:41:09+00:00" + "time": "2020-02-14T15:25:33+00:00" }, { "name": "symfony/console", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "e9ee09d087e2c88eaf6e5fc0f5c574f64d100e4f" + "reference": "f512001679f37e6a042b51897ed24a2f05eba656" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e9ee09d087e2c88eaf6e5fc0f5c574f64d100e4f", - "reference": "e9ee09d087e2c88eaf6e5fc0f5c574f64d100e4f", + "url": "https://api.github.com/repos/symfony/console/zipball/f512001679f37e6a042b51897ed24a2f05eba656", + "reference": "f512001679f37e6a042b51897ed24a2f05eba656", "shasum": "" }, "require": { @@ -2086,11 +2487,11 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2020-01-10T21:54:01+00:00" + "time": "2020-01-25T12:44:29+00:00" }, { "name": "symfony/css-selector", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2143,7 +2544,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -2271,7 +2672,7 @@ }, { "name": "symfony/filesystem", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -2321,7 +2722,7 @@ }, { "name": "symfony/finder", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -2370,16 +2771,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.13.1", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3" + "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", - "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", + "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", "shasum": "" }, "require": { @@ -2391,7 +2792,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -2424,20 +2825,20 @@ "polyfill", "portable" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.13.1", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f" + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f", - "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2", + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2", "shasum": "" }, "require": { @@ -2449,7 +2850,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -2483,20 +2884,20 @@ "portable", "shim" ], - "time": "2019-11-27T14:18:11+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.13.1", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "4b0e2222c55a25b4541305a053013d5647d3a25f" + "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/4b0e2222c55a25b4541305a053013d5647d3a25f", - "reference": "4b0e2222c55a25b4541305a053013d5647d3a25f", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/5e66a0fa1070bf46bec4bea7962d285108edd675", + "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675", "shasum": "" }, "require": { @@ -2505,7 +2906,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -2541,11 +2942,11 @@ "portable", "shim" ], - "time": "2019-11-27T16:25:15+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/process", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -5098,16 +5499,16 @@ }, { "name": "allure-framework/allure-php-api", - "version": "1.1.6", + "version": "1.1.7", "source": { "type": "git", "url": "https://github.com/allure-framework/allure-php-commons.git", - "reference": "2c62a70d60eca190253a6b80e9aa4c2ebc2e6e7f" + "reference": "243c9a2259b60c1b7a9d298d4fb3fc72b4f64c18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/allure-framework/allure-php-commons/zipball/2c62a70d60eca190253a6b80e9aa4c2ebc2e6e7f", - "reference": "2c62a70d60eca190253a6b80e9aa4c2ebc2e6e7f", + "url": "https://api.github.com/repos/allure-framework/allure-php-commons/zipball/243c9a2259b60c1b7a9d298d4fb3fc72b4f64c18", + "reference": "243c9a2259b60c1b7a9d298d4fb3fc72b4f64c18", "shasum": "" }, "require": { @@ -5147,7 +5548,7 @@ "php", "report" ], - "time": "2020-01-09T10:26:09+00:00" + "time": "2020-02-05T16:43:19+00:00" }, { "name": "allure-framework/allure-phpunit", @@ -5199,102 +5600,18 @@ ], "time": "2017-11-03T13:08:21+00:00" }, - { - "name": "aws/aws-sdk-php", - "version": "3.133.8", - "source": { - "type": "git", - "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd", - "reference": "c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-pcre": "*", - "ext-simplexml": "*", - "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4.1", - "mtdowling/jmespath.php": "^2.5", - "php": ">=5.5" - }, - "require-dev": { - "andrewsville/php-token-reflection": "^1.4", - "aws/aws-php-sns-message-validator": "~1.0", - "behat/behat": "~3.0", - "doctrine/cache": "~1.4", - "ext-dom": "*", - "ext-openssl": "*", - "ext-pcntl": "*", - "ext-sockets": "*", - "nette/neon": "^2.3", - "phpunit/phpunit": "^4.8.35|^5.4.3", - "psr/cache": "^1.0", - "psr/simple-cache": "^1.0", - "sebastian/comparator": "^1.2.3" - }, - "suggest": { - "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", - "doctrine/cache": "To use the DoctrineCacheAdapter", - "ext-curl": "To send requests using cURL", - "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", - "ext-sockets": "To use client-side monitoring" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Aws\\": "src/" - }, - "files": [ - "src/functions.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Amazon Web Services", - "homepage": "http://aws.amazon.com" - } - ], - "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", - "homepage": "http://aws.amazon.com/sdkforphp", - "keywords": [ - "amazon", - "aws", - "cloud", - "dynamodb", - "ec2", - "glacier", - "s3", - "sdk" - ], - "time": "2020-02-05T19:12:47+00:00" - }, { "name": "behat/gherkin", - "version": "v4.6.0", + "version": "v4.6.1", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07" + "reference": "25bdcaf37898b4a939fa3031d5d753ced97e4759" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/ab0a02ea14893860bca00f225f5621d351a3ad07", - "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/25bdcaf37898b4a939fa3031d5d753ced97e4759", + "reference": "25bdcaf37898b4a939fa3031d5d753ced97e4759", "shasum": "" }, "require": { @@ -5340,7 +5657,7 @@ "gherkin", "parser" ], - "time": "2019-01-16T14:22:17+00:00" + "time": "2020-02-27T11:29:57+00:00" }, { "name": "cache/cache", @@ -5604,80 +5921,43 @@ }, { "name": "consolidation/annotated-command", - "version": "2.12.0", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "512a2e54c98f3af377589de76c43b24652bcb789" + "reference": "33e472d3cceb0f22a527d13ccfa3f76c4d21c178" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/512a2e54c98f3af377589de76c43b24652bcb789", - "reference": "512a2e54c98f3af377589de76c43b24652bcb789", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/33e472d3cceb0f22a527d13ccfa3f76c4d21c178", + "reference": "33e472d3cceb0f22a527d13ccfa3f76c4d21c178", "shasum": "" }, "require": { - "consolidation/output-formatters": "^3.4", - "php": ">=5.4.5", - "psr/log": "^1", - "symfony/console": "^2.8|^3|^4", - "symfony/event-dispatcher": "^2.5|^3|^4", - "symfony/finder": "^2.5|^3|^4" + "consolidation/output-formatters": "^4.1", + "php": ">=7.1.3", + "psr/log": "^1|^2", + "symfony/console": "^4|^5", + "symfony/event-dispatcher": "^4|^5", + "symfony/finder": "^4|^5" }, "require-dev": { "g1a/composer-test-scenarios": "^3", "php-coveralls/php-coveralls": "^1", "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^2.7" + "squizlabs/php_codesniffer": "^3" }, "type": "library", "extra": { "scenarios": { "symfony4": { "require": { - "symfony/console": "^4.0" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony2": { - "require": { - "symfony/console": "^2.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - }, - "scenario-options": { - "create-lockfile": "false" - } - }, - "phpunit4": { - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } + "symfony/console": "^4.0" } } }, "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "4.x-dev" } }, "autoload": { @@ -5696,7 +5976,7 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2019-03-08T16:55:03+00:00" + "time": "2020-02-07T03:35:30+00:00" }, { "name": "consolidation/config", @@ -5781,74 +6061,33 @@ }, { "name": "consolidation/log", - "version": "1.1.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/consolidation/log.git", - "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a" + "reference": "446f804476db4f73957fa4bcb66ab2facf5397ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/b2e887325ee90abc96b0a8b7b474cd9e7c896e3a", - "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a", + "url": "https://api.github.com/repos/consolidation/log/zipball/446f804476db4f73957fa4bcb66ab2facf5397ff", + "reference": "446f804476db4f73957fa4bcb66ab2facf5397ff", "shasum": "" }, "require": { "php": ">=5.4.5", "psr/log": "^1.0", - "symfony/console": "^2.8|^3|^4" + "symfony/console": "^4|^5" }, "require-dev": { "g1a/composer-test-scenarios": "^3", "php-coveralls/php-coveralls": "^1", "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^2" + "squizlabs/php_codesniffer": "^3" }, "type": "library", "extra": { - "scenarios": { - "symfony4": { - "require": { - "symfony/console": "^4.0" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony2": { - "require": { - "symfony/console": "^2.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - } - }, - "phpunit4": { - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - } - } - }, "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "4.x-dev" } }, "autoload": { @@ -5867,34 +6106,35 @@ } ], "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", - "time": "2019-01-01T17:30:51+00:00" + "time": "2020-02-07T01:22:27+00:00" }, { "name": "consolidation/output-formatters", - "version": "3.5.0", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/consolidation/output-formatters.git", - "reference": "99ec998ffb697e0eada5aacf81feebfb13023605" + "reference": "eae721c3a916707c40d4390efbf48d4c799709cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/99ec998ffb697e0eada5aacf81feebfb13023605", - "reference": "99ec998ffb697e0eada5aacf81feebfb13023605", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/eae721c3a916707c40d4390efbf48d4c799709cc", + "reference": "eae721c3a916707c40d4390efbf48d4c799709cc", "shasum": "" }, "require": { "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4.0", - "symfony/console": "^2.8|^3|^4", - "symfony/finder": "^2.5|^3|^4" + "php": ">=7.1.3", + "symfony/console": "^4|^5", + "symfony/finder": "^4|^5" }, "require-dev": { "g1a/composer-test-scenarios": "^3", "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^5.7.27", - "squizlabs/php_codesniffer": "^2.7", - "symfony/var-dumper": "^2.8|^3|^4", + "phpunit/phpunit": "^6", + "squizlabs/php_codesniffer": "^3", + "symfony/var-dumper": "^4", + "symfony/yaml": "^4", "victorjonsson/markdowndocs": "^1.3" }, "suggest": { @@ -5906,50 +6146,11 @@ "symfony4": { "require": { "symfony/console": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^6" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony3": { - "require": { - "symfony/console": "^3.4", - "symfony/finder": "^3.4", - "symfony/var-dumper": "^3.4" - }, - "config": { - "platform": { - "php": "5.6.32" - } - } - }, - "symfony2": { - "require": { - "symfony/console": "^2.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - }, - "scenario-options": { - "create-lockfile": "false" } } }, "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "4.x-dev" } }, "autoload": { @@ -5968,30 +6169,30 @@ } ], "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2019-05-30T23:16:01+00:00" + "time": "2020-02-07T03:22:30+00:00" }, { "name": "consolidation/robo", - "version": "1.4.11", + "version": "1.4.12", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "5fa1d901776a628167a325baa9db95d8edf13a80" + "reference": "eb45606f498b3426b9a98b7c85e300666a968e51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/5fa1d901776a628167a325baa9db95d8edf13a80", - "reference": "5fa1d901776a628167a325baa9db95d8edf13a80", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/eb45606f498b3426b9a98b7c85e300666a968e51", + "reference": "eb45606f498b3426b9a98b7c85e300666a968e51", "shasum": "" }, "require": { - "consolidation/annotated-command": "^2.11.0", - "consolidation/config": "^1.2", - "consolidation/log": "~1", - "consolidation/output-formatters": "^3.1.13", - "consolidation/self-update": "^1", - "grasmash/yaml-expander": "^1.3", - "league/container": "^2.2", + "consolidation/annotated-command": "^2.11.0|^4.1", + "consolidation/config": "^1.2.1", + "consolidation/log": "^1.1.1|^2", + "consolidation/output-formatters": "^3.1.13|^4.1", + "consolidation/self-update": "^1.1.5", + "grasmash/yaml-expander": "^1.4", + "league/container": "^2.4.1", "php": ">=5.5.0", "symfony/console": "^2.8|^3|^4", "symfony/event-dispatcher": "^2.5|^3|^4", @@ -6003,20 +6204,13 @@ "codegyre/robo": "< 1.0" }, "require-dev": { - "codeception/aspect-mock": "^1|^2.1.1", - "codeception/base": "^2.3.7", - "codeception/verify": "^0.3.2", "g1a/composer-test-scenarios": "^3", - "goaop/framework": "~2.1.2", - "goaop/parser-reflection": "^1.1.0", "natxet/cssmin": "3.0.4", - "nikic/php-parser": "^3.1.5", - "patchwork/jsqueeze": "~2", + "patchwork/jsqueeze": "^2", "pear/archive_tar": "^1.4.4", "php-coveralls/php-coveralls": "^1", - "phpunit/php-code-coverage": "~2|~4", - "sebastian/comparator": "^1.2.4", - "squizlabs/php_codesniffer": "^2.8" + "phpunit/phpunit": "^5.7.27", + "squizlabs/php_codesniffer": "^3" }, "suggest": { "henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch", @@ -6044,8 +6238,11 @@ "require": { "symfony/console": "^2.8" }, + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, "remove": [ - "goaop/framework" + "php-coveralls/php-coveralls" ], "config": { "platform": { @@ -6058,7 +6255,7 @@ } }, "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -6077,7 +6274,7 @@ } ], "description": "Modern task runner", - "time": "2019-10-29T15:50:02+00:00" + "time": "2020-02-18T17:31:26+00:00" }, { "name": "consolidation/self-update", @@ -7042,16 +7239,16 @@ }, { "name": "jms/serializer", - "version": "1.14.0", + "version": "1.14.1", "source": { "type": "git", "url": "https://github.com/schmittjoh/serializer.git", - "reference": "ee96d57024af9a7716d56fcbe3aa94b3d030f3ca" + "reference": "ba908d278fff27ec01fb4349f372634ffcd697c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/ee96d57024af9a7716d56fcbe3aa94b3d030f3ca", - "reference": "ee96d57024af9a7716d56fcbe3aa94b3d030f3ca", + "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/ba908d278fff27ec01fb4349f372634ffcd697c0", + "reference": "ba908d278fff27ec01fb4349f372634ffcd697c0", "shasum": "" }, "require": { @@ -7104,13 +7301,13 @@ "MIT" ], "authors": [ - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - }, { "name": "Johannes M. Schmitt", "email": "schmittjoh@gmail.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" } ], "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", @@ -7122,7 +7319,7 @@ "serialization", "xml" ], - "time": "2019-04-17T08:12:16+00:00" + "time": "2020-02-22T20:59:37+00:00" }, { "name": "league/container", @@ -7189,90 +7386,6 @@ ], "time": "2017-05-10T09:20:27+00:00" }, - { - "name": "league/flysystem", - "version": "1.0.63", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/flysystem.git", - "reference": "8132daec326565036bc8e8d1876f77ec183a7bd6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/8132daec326565036bc8e8d1876f77ec183a7bd6", - "reference": "8132daec326565036bc8e8d1876f77ec183a7bd6", - "shasum": "" - }, - "require": { - "ext-fileinfo": "*", - "php": ">=5.5.9" - }, - "conflict": { - "league/flysystem-sftp": "<1.0.6" - }, - "require-dev": { - "phpspec/phpspec": "^3.4", - "phpunit/phpunit": "^5.7.10" - }, - "suggest": { - "ext-fileinfo": "Required for MimeType", - "ext-ftp": "Allows you to use FTP server storage", - "ext-openssl": "Allows you to use FTPS server storage", - "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", - "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", - "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", - "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", - "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", - "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", - "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", - "league/flysystem-webdav": "Allows you to use WebDAV storage", - "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", - "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", - "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Flysystem\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frenky.net" - } - ], - "description": "Filesystem abstraction: Many filesystems, one API.", - "keywords": [ - "Cloud Files", - "WebDAV", - "abstraction", - "aws", - "cloud", - "copy.com", - "dropbox", - "file systems", - "files", - "filesystem", - "filesystems", - "ftp", - "rackspace", - "remote", - "s3", - "sftp", - "storage" - ], - "time": "2020-01-04T16:30:31+00:00" - }, { "name": "lusitanian/oauth", "version": "v0.8.11", @@ -7510,63 +7623,6 @@ "homepage": "http://vfs.bovigo.org/", "time": "2019-10-30T15:31:00+00:00" }, - { - "name": "mtdowling/jmespath.php", - "version": "2.5.0", - "source": { - "type": "git", - "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "52168cb9472de06979613d365c7f1ab8798be895" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895", - "reference": "52168cb9472de06979613d365c7f1ab8798be895", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "symfony/polyfill-mbstring": "^1.4" - }, - "require-dev": { - "composer/xdebug-handler": "^1.2", - "phpunit/phpunit": "^4.8.36|^7.5.15" - }, - "bin": [ - "bin/jp.php" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.5-dev" - } - }, - "autoload": { - "psr-4": { - "JmesPath\\": "src/" - }, - "files": [ - "src/JmesPath.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Declaratively specify how to extract elements from a JSON document", - "keywords": [ - "json", - "jsonpath" - ], - "time": "2019-12-30T18:03:34+00:00" - }, { "name": "mustache/mustache", "version": "v2.13.0", @@ -7856,16 +7912,16 @@ }, { "name": "php-webdriver/webdriver", - "version": "1.8.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/php-webdriver/php-webdriver.git", - "reference": "3e33ee3b8a688d719c55acdd7c6788e3006e1d3e" + "reference": "262ea0d209c292e0330be1041424887bbbffef04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/3e33ee3b8a688d719c55acdd7c6788e3006e1d3e", - "reference": "3e33ee3b8a688d719c55acdd7c6788e3006e1d3e", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/262ea0d209c292e0330be1041424887bbbffef04", + "reference": "262ea0d209c292e0330be1041424887bbbffef04", "shasum": "" }, "require": { @@ -7897,12 +7953,12 @@ } }, "autoload": { - "files": [ - "lib/Exception/TimeoutException.php" - ], "psr-4": { "Facebook\\WebDriver\\": "lib/" - } + }, + "files": [ + "lib/Exception/TimeoutException.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7917,7 +7973,7 @@ "selenium", "webdriver" ], - "time": "2020-02-10T15:04:25+00:00" + "time": "2020-02-17T08:14:38+00:00" }, { "name": "phpcollection/phpcollection", @@ -8079,41 +8135,38 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.4", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c" + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c", - "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", "shasum": "" }, "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", - "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", - "webmozart/assert": "^1.0" + "ext-filter": "^7.1", + "php": "^7.2", + "phpdocumentor/reflection-common": "^2.0", + "phpdocumentor/type-resolver": "^1.0", + "webmozart/assert": "^1" }, "require-dev": { - "doctrine/instantiator": "^1.0.5", - "mockery/mockery": "^1.0", - "phpdocumentor/type-resolver": "0.4.*", - "phpunit/phpunit": "^6.4" + "doctrine/instantiator": "^1", + "mockery/mockery": "^1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -8124,10 +8177,14 @@ { "name": "Mike van Riel", "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2019-12-28T18:55:12+00:00" + "time": "2020-02-22T12:28:44+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -8364,16 +8421,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.7", + "version": "0.12.11", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "07fa7958027fd98c567099bbcda5d6a0f2ec5197" + "reference": "ca5f2b7cf81c6d8fba74f9576970399c5817e03b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/07fa7958027fd98c567099bbcda5d6a0f2ec5197", - "reference": "07fa7958027fd98c567099bbcda5d6a0f2ec5197", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ca5f2b7cf81c6d8fba74f9576970399c5817e03b", + "reference": "ca5f2b7cf81c6d8fba74f9576970399c5817e03b", "shasum": "" }, "require": { @@ -8399,7 +8456,7 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", - "time": "2020-01-20T21:59:06+00:00" + "time": "2020-02-16T14:00:29+00:00" }, { "name": "phpunit/php-code-coverage", @@ -9593,7 +9650,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -9652,7 +9709,7 @@ }, { "name": "symfony/config", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", @@ -9716,16 +9773,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "6faf589e1f6af78692aed3ab6b3c336c58d5d83c" + "reference": "ec60a7d12f5e8ab0f99456adce724717d9c1784a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/6faf589e1f6af78692aed3ab6b3c336c58d5d83c", - "reference": "6faf589e1f6af78692aed3ab6b3c336c58d5d83c", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ec60a7d12f5e8ab0f99456adce724717d9c1784a", + "reference": "ec60a7d12f5e8ab0f99456adce724717d9c1784a", "shasum": "" }, "require": { @@ -9785,11 +9842,11 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2020-01-21T07:39:36+00:00" + "time": "2020-01-31T09:49:27+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -9850,16 +9907,16 @@ }, { "name": "symfony/http-foundation", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "c33998709f3fe9b8e27e0277535b07fbf6fde37a" + "reference": "491a20dfa87e0b3990170593bc2de0bb34d828a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c33998709f3fe9b8e27e0277535b07fbf6fde37a", - "reference": "c33998709f3fe9b8e27e0277535b07fbf6fde37a", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/491a20dfa87e0b3990170593bc2de0bb34d828a5", + "reference": "491a20dfa87e0b3990170593bc2de0bb34d828a5", "shasum": "" }, "require": { @@ -9901,11 +9958,11 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "time": "2020-01-31T09:11:17+00:00" }, { "name": "symfony/mime", - "version": "v5.0.3", + "version": "v5.0.4", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", @@ -9967,7 +10024,7 @@ }, { "name": "symfony/options-resolver", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", @@ -10021,22 +10078,22 @@ }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.13.1", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "6f9c239e61e1b0c9229a28ff89a812dc449c3d46" + "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6f9c239e61e1b0c9229a28ff89a812dc449c3d46", - "reference": "6f9c239e61e1b0c9229a28ff89a812dc449c3d46", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6842f1a39cf7d580655688069a03dd7cd83d244a", + "reference": "6842f1a39cf7d580655688069a03dd7cd83d244a", "shasum": "" }, "require": { "php": ">=5.3.3", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php72": "^1.9" + "symfony/polyfill-php72": "^1.10" }, "suggest": { "ext-intl": "For best performance" @@ -10044,7 +10101,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -10079,20 +10136,20 @@ "portable", "shim" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2020-01-17T12:01:36+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.13.1", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "af23c7bb26a73b850840823662dda371484926c4" + "reference": "419c4940024c30ccc033650373a1fe13890d3255" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/af23c7bb26a73b850840823662dda371484926c4", - "reference": "af23c7bb26a73b850840823662dda371484926c4", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/419c4940024c30ccc033650373a1fe13890d3255", + "reference": "419c4940024c30ccc033650373a1fe13890d3255", "shasum": "" }, "require": { @@ -10102,7 +10159,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -10138,20 +10195,20 @@ "portable", "shim" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.13.1", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "66fea50f6cb37a35eea048d75a7d99a45b586038" + "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/66fea50f6cb37a35eea048d75a7d99a45b586038", - "reference": "66fea50f6cb37a35eea048d75a7d99a45b586038", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", + "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", "shasum": "" }, "require": { @@ -10160,7 +10217,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -10193,11 +10250,11 @@ "portable", "shim" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/stopwatch", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -10247,7 +10304,7 @@ }, { "name": "symfony/yaml", - "version": "v4.4.3", + "version": "v4.4.4", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", @@ -10437,16 +10494,16 @@ }, { "name": "webmozart/assert", - "version": "1.6.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "573381c0a64f155a0d9a23f4b0c797194805b925" + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925", - "reference": "573381c0a64f155a0d9a23f4b0c797194805b925", + "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", "shasum": "" }, "require": { @@ -10481,7 +10538,7 @@ "check", "validate" ], - "time": "2019-11-24T13:36:37+00:00" + "time": "2020-02-14T12:15:55+00:00" }, { "name": "weew/helpers-array", diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ImageUploaderTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ImageUploaderTest.php deleted file mode 100644 index 569cf2357675c..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ImageUploaderTest.php +++ /dev/null @@ -1,165 +0,0 @@ -objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var \Magento\Framework\Filesystem $filesystem */ - $this->filesystem = $this->objectManager->get(\Magento\Framework\Filesystem::class); - $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); - /** @var $uploader \Magento\MediaStorage\Model\File\Uploader */ - $this->imageUploader = $this->objectManager->create( - \Magento\Catalog\Model\ImageUploader::class, - [ - 'baseTmpPath' => 'catalog/tmp/category', - 'basePath' => 'catalog/category', - 'allowedExtensions' => ['jpg', 'jpeg', 'gif', 'png'], - 'allowedMimeTypes' => ['image/jpg', 'image/jpeg', 'image/gif', 'image/png'] - ] - ); - } - - /** - * @return void - */ - public function testSaveFileToTmpDir(): void - { - $fileName = 'magento_small_image.jpg'; - $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP); - $fixtureDir = realpath(__DIR__ . '/../_files'); - $filePath = $tmpDirectory->getAbsolutePath($fileName); - copy($fixtureDir . DIRECTORY_SEPARATOR . $fileName, $filePath); - - $_FILES['image'] = [ - 'name' => $fileName, - 'type' => 'image/jpeg', - 'tmp_name' => $filePath, - 'error' => 0, - 'size' => 12500, - ]; - - $this->imageUploader->saveFileToTmpDir('image'); - $filePath = $this->imageUploader->getBaseTmpPath() . DIRECTORY_SEPARATOR. $fileName; - $this->assertTrue(is_file($this->mediaDirectory->getAbsolutePath($filePath))); - } - - /** - * Test that method rename files when move it with the same name into base directory. - * - * @return void - * @magentoDataFixture Magento/Catalog/_files/catalog_category_image.php - * @magentoDataFixture Magento/Catalog/_files/catalog_tmp_category_image.php - */ - public function testMoveFileFromTmp(): void - { - $expectedFilePath = $this->imageUploader->getBasePath() . DIRECTORY_SEPARATOR . 'magento_small_image_1.jpg'; - - $this->assertFileNotExists($this->mediaDirectory->getAbsolutePath($expectedFilePath)); - - $this->imageUploader->moveFileFromTmp('magento_small_image.jpg'); - - $this->assertFileExists($this->mediaDirectory->getAbsolutePath($expectedFilePath)); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage File validation failed. - * @return void - */ - public function testSaveFileToTmpDirWithWrongExtension(): void - { - $fileName = 'text.txt'; - $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP); - $filePath = $tmpDirectory->getAbsolutePath($fileName); - $file = fopen($filePath, "wb"); - fwrite($file, 'just a text'); - - $_FILES['image'] = [ - 'name' => $fileName, - 'type' => 'text/plain', - 'tmp_name' => $filePath, - 'error' => 0, - 'size' => 12500, - ]; - - $this->imageUploader->saveFileToTmpDir('image'); - $filePath = $this->imageUploader->getBaseTmpPath() . DIRECTORY_SEPARATOR. $fileName; - $this->assertFalse(is_file($this->mediaDirectory->getAbsolutePath($filePath))); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage File validation failed. - * @return void - */ - public function testSaveFileToTmpDirWithWrongFile(): void - { - $fileName = 'file.gif'; - $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP); - $filePath = $tmpDirectory->getAbsolutePath($fileName); - $file = fopen($filePath, "wb"); - fwrite($file, 'just a text'); - - $_FILES['image'] = [ - 'name' => $fileName, - 'type' => 'image/gif', - 'tmp_name' => $filePath, - 'error' => 0, - 'size' => 12500, - ]; - - $this->imageUploader->saveFileToTmpDir('image'); - $filePath = $this->imageUploader->getBaseTmpPath() . DIRECTORY_SEPARATOR. $fileName; - $this->assertFalse(is_file($this->mediaDirectory->getAbsolutePath($filePath))); - } - - /** - * @inheritdoc - */ - public static function tearDownAfterClass() - { - parent::tearDownAfterClass(); - $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\Filesystem::class - ); - /** @var \Magento\Framework\Filesystem\Directory\WriteInterface $mediaDirectory */ - $mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); - $mediaDirectory->delete('tmp'); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php index 89b91ab57e51a..f348372f2029a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php @@ -338,11 +338,7 @@ private function getProductInstance(?int $storeId = null): ProductInterface { /** @var ProductInterface $product */ $product = $this->productFactory->create(); - $product->setData( - $this->productLinkField, - $this->getProduct()->getData($this->productLinkField) - ); - + $product->setId($this->getProduct()->getId()); if ($storeId) { $product->setStoreId($storeId); } diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php index 93e7833038a42..12ed71b708b88 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php @@ -58,6 +58,8 @@ public function testRenderRestrictMode(): void foreach ($header as $item) { $contentSecurityPolicyContent[] = $item->getFieldValue(); } + } else { + $contentSecurityPolicyContent = [$header->getFieldValue()]; } $this->assertEquals(['default-src https://magento.com \'self\';'], $contentSecurityPolicyContent); } @@ -84,6 +86,8 @@ public function testRenderRestrictWithReportingMode(): void foreach ($header as $item) { $contentSecurityPolicyContent[] = $item->getFieldValue(); } + } else { + $contentSecurityPolicyContent = [$header->getFieldValue()]; } $this->assertEquals( ['default-src https://magento.com \'self\'; report-uri /csp-reports/; report-to report-endpoint;'], diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt index f54defbd57604..6a7c814f50524 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt @@ -14,3 +14,5 @@ dev/tests/api-functional/testsuite/Magento/Customer/Api/AddressRepositoryTest.ph dev/tests/api-functional/testsuite/Magento/Framework/Model/Entity/HydratorTest.php dev/tests/api-functional/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php dev/tests/api-functional/testsuite/Magento/Integration/Model/CustomerTokenServiceTest.php +lib/internal/Magento/Framework/Storage/AdapterFactory/AwsS3Factory.php +lib/internal/Magento/Framework/Storage/AdapterFactory/AzureFactory.php diff --git a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php index dc3e63fcc7df8..2adff8162e5a3 100644 --- a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php +++ b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php @@ -19,6 +19,11 @@ class Response extends \Zend\Http\PhpEnvironment\Response implements \Magento\Fr */ protected $isRedirect = false; + /** + * @var bool + */ + private $headersSent; + /** * @inheritdoc */ @@ -28,6 +33,10 @@ public function getHeader($name) $headers = $this->getHeaders(); if ($headers->has($name)) { $header = $headers->get($name); + // zend-http >= 2.10.11 can return \ArrayIterator instead of a single Header + if ($header instanceof \ArrayIterator) { + $header = $header->current(); + } } return $header; } diff --git a/lib/internal/Magento/Framework/Storage/AdapterFactory/AdapterFactoryInterface.php b/lib/internal/Magento/Framework/Storage/AdapterFactory/AdapterFactoryInterface.php new file mode 100644 index 0000000000000..56794bbd29fcf --- /dev/null +++ b/lib/internal/Magento/Framework/Storage/AdapterFactory/AdapterFactoryInterface.php @@ -0,0 +1,25 @@ + + + + default/location + + + +``` + +`default/location` is a default path in local filesystem, relative to Magento root. + +Now, in a PHP class that uses the declared storage, use the same `\Magento\Framework\Storage\StorageProvider` to get it: + +```php +/** + * @var \Magento\Framework\Storage\StorageProvider + */ +private $storageProvider; + +public function doSomething() +{ + $storage = $this->storageProvider->get('storage-name') + $storage->put('path.txt', $content); +} +``` + +## Configuring Storage + +A storage can be configured in `env.php`: + +```php +'storage' => [ + 'storage-name' => [ + 'adapter' => 'aws_s3', + 'options' => [ + 'client' => [ + 'credentials' => [ + 'key' => '', + 'secret' => '' + ], + 'region' => '', + 'version' => 'latest', + ], + 'bucket' => '', + ], + ], + 'media' => [ + // this is default configuration, so it doesn't need to be configured explicitly like so + 'adapter' => 'local', + 'options' => [ + 'root' => 'pub/media' + ] + ] +] +``` + +Different providers have different `options` available for configuration. +Under the hood, Magento Storage relies on [Flysystem](https://github.com/thephpleague/flysystem) library, so`options` might reflect options required by a corresponding storage adapter implemented for Flysystem. + +## Storage Providers + +By default, Magento Storage provides support for the following storage providers: + +* Local filesystem (based on `\League\Flysystem\Adapter\Local`) + * Adapter name: `local` + * Options: + ```php + [ + 'root' => 'path/relative/to/magento/root' + ] + ``` +* AWS S3 V3 (based on `\League\Flysystem\AwsS3v3\AwsS3Adapter`) + * Adapter name: `aws_s3` + * Options: + ```php + [ + 'client' => [ + 'credentials' => [ + 'key' => '', + 'secret' => '' + ], + 'region' => '', + 'version' => 'latest', + ], + 'bucket' => '', + 'prefix' => '', + ] + ``` +* Azure Blob storage (based on `\League\Flysystem\AzureBlobStorage\AzureBlobStorageAdapter`) + * Adapter name: `ms_azure` + * Options: + ```php + [ + 'connection_string' => '', + 'container_name' => '', + 'prefix' => '', + ] + ``` + +Additional adapters can be added by: +1. Creating an adapter factory implementing `\Magento\Framework\Storage\AdapterFactory\AdapterFactoryInterface` +2. Registering the factory in `Magento\Framework\Storage\StorageProvider` via `di.xml`: + ```xml + + + + My\Storage\AdapterFactory + + + + ``` + +The factory is registered as a "string" (name of the class). +That's because in most cases only a few adapters will be really created for a single application, and we don't want to create unnecessary factory instances. diff --git a/lib/internal/Magento/Framework/Storage/RootViolationException.php b/lib/internal/Magento/Framework/Storage/RootViolationException.php new file mode 100644 index 0000000000000..3ea41bfb57073 --- /dev/null +++ b/lib/internal/Magento/Framework/Storage/RootViolationException.php @@ -0,0 +1,17 @@ +filesystem = $filesystem; + } + + /** + * @inheritDoc + */ + public function put($path, $contents, array $config = []): bool + { + return $this->filesystem->put($path, $contents, $config); + } + + /** + * @inheritDoc + */ + public function deleteDir($dirname): bool + { + try { + $result = $this->filesystem->deleteDir($dirname); + } catch (\League\Flysystem\RootViolationException $exception) { + throw new \Magento\Framework\Storage\RootViolationException($exception->getMessage()); + } + return $result; + } + + /** + * @inheritDoc + */ + public function getMetadata($path): ?array + { + try { + $metadata = $this->filesystem->getMetadata($path); + } catch (\League\Flysystem\FileNotFoundException $exception) { + throw new \Magento\Framework\Storage\FileNotFoundException( + $exception->getMessage() + ); + } + if ($metadata === false) { + $metadata = null; + } + return $metadata; + } + + /** + * @inheritDoc + */ + public function has($path): bool + { + return $this->filesystem->has($path); + } +} diff --git a/lib/internal/Magento/Framework/Storage/StorageAdapterProvider.php b/lib/internal/Magento/Framework/Storage/StorageAdapterProvider.php new file mode 100644 index 0000000000000..bcd5fe806c0c3 --- /dev/null +++ b/lib/internal/Magento/Framework/Storage/StorageAdapterProvider.php @@ -0,0 +1,63 @@ +objectManager = $objectManager; + $this->config = $config; + } + + /** + * Create storage adapter based on its name with provided options + * + * @param string $adapterName + * @param array $options + * @return AdapterInterface|null + */ + public function create(string $adapterName, array $options) :? AdapterInterface + { + if (!isset($this->config[$adapterName])) { + throw new InvalidStorageConfigurationException( + "Configured adapter '$adapterName' is not supported" + ); + } + $adapterFactoryClass = $this->config[$adapterName]; + $adapterFactory = $this->objectManager->get($adapterFactoryClass); + if (!$adapterFactory instanceof AdapterFactoryInterface) { + throw new InvalidStorageConfigurationException( + "Configured storage adapter factory '$adapterFactory' must implement " . + "'\Magento\Framework\Storage\AdapterFactory\AdapterFactoryInterface'" + ); + } + return $adapterFactory->create($options); + } +} diff --git a/lib/internal/Magento/Framework/Storage/StorageInterface.php b/lib/internal/Magento/Framework/Storage/StorageInterface.php new file mode 100644 index 0000000000000..4cf965b2287cd --- /dev/null +++ b/lib/internal/Magento/Framework/Storage/StorageInterface.php @@ -0,0 +1,57 @@ +get(''), + * where $storageProvider is an instance of \Magento\Framework\Storage\StorageProvider + */ +interface StorageInterface +{ + /** + * Create a file or update if exists. + * + * @param string $path The path to the file. + * @param string $contents The file contents. + * @param array $config An optional configuration array. + * + * @return bool True on success, false on failure. + */ + public function put($path, $contents, array $config = []): bool; + + /** + * Delete a directory. + * + * @param string $dirname + * + * @throws RootViolationException Thrown if $dirname is empty. + * + * @return bool True on success, false on failure. + */ + public function deleteDir($dirname): bool; + + /** + * Get a file's metadata. + * + * @param string $path The path to the file. + * + * @throws FileNotFoundException + * + * @return array|false The file metadata or false on failure. + */ + public function getMetadata($path): ?array; + + /** + * Check whether a file exists. + * + * @param string $path + * + * @return bool + */ + public function has($path): bool; +} diff --git a/lib/internal/Magento/Framework/Storage/StorageProvider.php b/lib/internal/Magento/Framework/Storage/StorageProvider.php new file mode 100644 index 0000000000000..bad0813f33002 --- /dev/null +++ b/lib/internal/Magento/Framework/Storage/StorageProvider.php @@ -0,0 +1,102 @@ + $localPath) { + $this->storageConfig[$storageName] = [ + 'adapter' => LocalFactory::ADAPTER_NAME, + 'options' => [ + 'root' => BP . '/' . $localPath, + ], + ]; + $envStorageConfig = $envConfig->get('storage/' . $storageName); + if ($envStorageConfig) { + $this->storageConfig[$storageName] = array_replace( + $this->storageConfig[$storageName], + $envStorageConfig + ); + } + } + $this->filesystemFactory = $filesystemFactory; + $this->storageFactory = $storageFactory; + $this->adapterProvider = $adapterProvider; + } + + /** + * Get storage by its name + * + * @param string $storageName + * @return StorageInterface + */ + public function get(string $storageName): StorageInterface + { + if (!isset($this->storage[$storageName])) { + if (isset($this->storageConfig[$storageName])) { + $config = $this->storageConfig[$storageName]; + if (empty($config['adapter']) || empty($config['options'])) { + throw new InvalidStorageConfigurationException( + "Incorrect configuration for storage '$storageName': required field " . + "'adapter' and/or 'options' is not defined" + ); + } + $adapter = $this->adapterProvider->create($config['adapter'], $config['options']); + $filesystem = $this->filesystemFactory->create(['adapter' => $adapter]); + $this->storage[$storageName] = $this->storageFactory->create(['filesystem' => $filesystem]); + } else { + throw new UnsupportedStorageException("No storage with name '$storageName' is declared"); + } + } + return $this->storage[$storageName]; + } +} diff --git a/lib/internal/Magento/Framework/Storage/UnsupportedStorageException.php b/lib/internal/Magento/Framework/Storage/UnsupportedStorageException.php new file mode 100644 index 0000000000000..8267fea85319e --- /dev/null +++ b/lib/internal/Magento/Framework/Storage/UnsupportedStorageException.php @@ -0,0 +1,13 @@ +componentRegistrar = $this->createMock(\Magento\Framework\Component\ComponentRegistrar::class); - } - - /** - * @param array $context - * @param string $path - * @param array $pathValues - * @dataProvider dataProviderContextByPath - */ - public function testGetContextByPath($context, $path, $pathValues) - { - $this->componentRegistrar->expects($this->any()) - ->method('getPaths') - ->willReturnMap($pathValues); - $this->context = new Context($this->componentRegistrar); - $this->assertEquals($context, $this->context->getContextByPath($path)); - } - - /** - * @return array - */ - public function dataProviderContextByPath() - { - return [ - [ - [Context::CONTEXT_TYPE_MODULE, 'Magento_Module'], - '/app/code/Magento/Module/Block/Test.php', - [ - [Context::CONTEXT_TYPE_MODULE, ['Magento_Module' => '/app/code/Magento/Module']], - [Context::CONTEXT_TYPE_THEME, []], - ] - ], - [ - [Context::CONTEXT_TYPE_THEME, 'frontend/Some/theme'], - '/app/design/area/theme/test.phtml', - [ - [Context::CONTEXT_TYPE_MODULE, []], - [Context::CONTEXT_TYPE_THEME, ['frontend/Some/theme' => '/app/design/area/theme']], - ] - ], - [ - [Context::CONTEXT_TYPE_LIB, 'lib/web/module/test.phtml'], - '/lib/web/module/test.phtml', - [ - [Context::CONTEXT_TYPE_MODULE, []], - [Context::CONTEXT_TYPE_THEME, []], - ] - ], - ]; - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Invalid path given: "invalid_path". - */ - public function testGetContextByPathWithInvalidPath() - { - $this->componentRegistrar->expects($this->any()) - ->method('getPaths') - ->willReturnMap([ - [ComponentRegistrar::MODULE, ['/path/to/module']], - [ComponentRegistrar::THEME, ['/path/to/theme']] - ]); - $this->context = new Context($this->componentRegistrar); - $this->context->getContextByPath('invalid_path'); - } - - /** - * @param string $path - * @param array $context - * @param array $registrar - * @dataProvider dataProviderPathToLocaleDirectoryByContext - */ - public function testBuildPathToLocaleDirectoryByContext($path, $context, $registrar) - { - $paths = []; - foreach ($registrar as $module) { - $paths[$module[1]] = $module[2]; - } - $this->componentRegistrar->expects($this->any()) - ->method('getPath') - ->willReturnMap($registrar); - $this->context = new Context($this->componentRegistrar); - $this->assertEquals($path, $this->context->buildPathToLocaleDirectoryByContext($context[0], $context[1])); - } - - /** - * @return array - */ - public function dataProviderPathToLocaleDirectoryByContext() - { - return [ - [ - BP . '/app/code/Magento/Module/i18n/', - [Context::CONTEXT_TYPE_MODULE, 'Magento_Module'], - [[ComponentRegistrar::MODULE, 'Magento_Module', BP . '/app/code/Magento/Module']] - ], - [ - BP . '/app/design/frontend/Magento/luma/i18n/', - [Context::CONTEXT_TYPE_THEME, 'frontend/Magento/luma'], - [[ComponentRegistrar::THEME, 'frontend/Magento/luma', BP . '/app/design/frontend/Magento/luma']] - ], - - [ - null, - [Context::CONTEXT_TYPE_MODULE, 'Unregistered_Module'], - [[ComponentRegistrar::MODULE, 'Unregistered_Module', null]] - ], - [ - null, - [Context::CONTEXT_TYPE_THEME, 'frontend/Magento/unregistered'], - [[ComponentRegistrar::THEME, 'frontend/Magento/unregistered', null]] - ], - [BP . '/lib/web/i18n/', [Context::CONTEXT_TYPE_LIB, 'lib/web/module/test.phtml'], []], - ]; - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Invalid context given: "invalid_type". - */ - public function testBuildPathToLocaleDirectoryByContextWithInvalidType() - { - $this->componentRegistrar->expects($this->never()) - ->method('getPath'); - $this->context = new Context($this->componentRegistrar); - $this->context->buildPathToLocaleDirectoryByContext('invalid_type', 'Magento_Module'); - } -}