* @copyright Since 2007 PrestaShop SA and Contributors * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) */ declare(strict_types=1); namespace PrestaShop\PrestaShop\Core\Module\SourceHandler; use PrestaShop\PrestaShop\Core\Module\Exception\ModuleErrorException; use Symfony\Contracts\Translation\TranslatorInterface; use ZipArchive; class ZipSourceHandler implements SourceHandlerInterface { private const AUTHORIZED_MIME = [ 'application/zip', 'application/x-gzip', 'application/gzip', 'application/x-gtar', 'application/x-tgz', ]; private const MODULE_REGEX = '/^(.*)\/\1\.php$/i'; // module_name/module_name.php /** @var string */ protected $modulePath; /** @var TranslatorInterface */ protected $translator; public function __construct(string $modulePath, TranslatorInterface $translator) { $this->modulePath = rtrim($modulePath, '/') . '/'; $this->translator = $translator; } public function canHandle($source): bool { return is_file($source) && in_array(mime_content_type($source), self::AUTHORIZED_MIME); } public function getModuleName($source): string { $zip = new ZipArchive(); if ($zip->open($source) === true) { for ($i = 0; $i < $zip->numFiles; ++$i) { if (preg_match(self::MODULE_REGEX, $zip->getNameIndex($i), $matches)) { $zip->close(); return $matches[1]; } } $zip->close(); } throw new ModuleErrorException( $this->translator->trans( 'This file does not seem to be a valid module zip', [], 'Admin.Modules.Notification' ) ); } public function handle(string $source): void { $zip = new ZipArchive(); if ($zip->open($source) !== true || !$zip->extractTo($this->modulePath) || !$zip->close()) { throw new ModuleErrorException( $this->translator->trans( 'Cannot extract module in %path%. %error%', [ '%path%' => $this->modulePath, '%error%' => @$zip->getStatusString() ?: '', // Since php 8.0 getStatusString cannot return false nor a warning ], 'Admin.Modules.Notification' ) ); } } }