Skip to content

Commit

Permalink
ImageStorage: Add native support for image storage of post main image.
Browse files Browse the repository at this point in the history
  • Loading branch information
janbarasek committed Nov 4, 2020
1 parent e3e8cc2 commit e3c7a88
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 4 deletions.
11 changes: 10 additions & 1 deletion src/Feed.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ final class Feed
{
private Cache $cache;

private ImageStorage $imageStorage;

private string $expirationTime;


public function __construct(IStorage $storage, string $expirationTime)
public function __construct(IStorage $storage, ImageStorage $imageStorage, string $expirationTime)
{
$this->cache = new Cache($storage, 'wordpress-post-feed');
$this->imageStorage = $imageStorage;
$this->expirationTime = $expirationTime;
}

Expand All @@ -41,6 +44,7 @@ public function load(string $url): array
$this->hydrateValueToString($node, 'link'),
$this->hydrateValueToDateTime($node, 'pubDate')
))
->setImageStorage($this->imageStorage)
->setCreator($this->hydrateValueToString($node, 'creator'))
->setCategories($this->hydrateValue($node, 'category'))
->setMainImageUrl($description['mainImageUrl'] ?? null);
Expand Down Expand Up @@ -91,6 +95,11 @@ private function hydrateDescription(string $description): array
if (preg_match('/(.*)<img\s[^>]*?src="([^"]+)"[^>]*?>(.*)/', $description, $parser)) {
$description = trim($parser[1] . ' ' . $parser[3]);
$mainImageUrl = trim($parser[2]);
try {
$this->imageStorage->save($mainImageUrl);
} catch (\InvalidArgumentException $e) {
trigger_error($e->getMessage());
}
}

return [
Expand Down
98 changes: 98 additions & 0 deletions src/ImageStorage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

namespace Baraja\WordPressPostFeed;


use Nette\Utils\FileSystem;
use Nette\Utils\Strings;
use Nette\Utils\Validators;

final class ImageStorage
{
private string $storagePath;

private string $relativeStoragePath;


public function __construct(string $storagePath, string $relativeStoragePath)
{
$this->storagePath = $storagePath;
$this->relativeStoragePath = $relativeStoragePath;
}


public function save(string $url): void
{
if (Validators::isUrl($url) === false) {
throw new \InvalidArgumentException('Given input is not valid absolute URL, because "' . $url . '" given.');
}
if (\is_file($storagePath = $this->getInternalPath($url)) === false) {
FileSystem::copy($url, $storagePath);
}
}


public function getInternalPath(string $url): string
{
return $this->storagePath . '/' . $this->getRelativeInternalUrl($url);
}


public function getAbsoluteInternalUrl(string $url): string
{
return $this->getBaseUrl() . '/' . $this->relativeStoragePath . '/' . $this->getRelativeInternalUrl($url);
}


public function getRelativeInternalUrl(string $url): string
{
$originalFileName = (string) preg_replace_callback(
'/^.*\/([^\/]+)\.([^.]+)$/',
fn(array $match): string => substr(Strings::webalize($match[1]), 0, 64) . '.' . strtolower($match[2]),
$url
);
$relativeName = substr(md5($url), 0, 7) . '-' . $originalFileName;

return $this->resolvePrefixDir($url) . '/' . $relativeName;
}


private function resolvePrefixDir(string $url): string
{
if ($url === '') {
throw new \InvalidArgumentException('URL can not be empty string.');
}
if (preg_match('/wp-content.+(\d{4})\/(\d{2})/', $url, $urlParser)) {
return $urlParser[1] . '-' . $urlParser[2];
}

return substr(md5($url), 0, 7);
}


private function getBaseUrl(): string
{
static $return;
if ($return !== null) {
return $return;
}
if (!isset($_SERVER['REQUEST_URI'], $_SERVER['HTTP_HOST'])) {
throw new \RuntimeException('Can not resolve current URL in CLI mode.');
}

$currentUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http')
. '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];

if (preg_match('/^(https?:\/\/.+)\/www\//', $currentUrl, $localUrlParser)) {
$return = $localUrlParser[0];
} elseif (preg_match('/^(https?:\/\/[^\/]+)/', $currentUrl, $publicUrlParser)) {
$return = $publicUrlParser[1];
} else {
throw new \RuntimeException('Can not parse relative URL from "' . $currentUrl . '".');
}

return ($return = rtrim($return, '/'));
}
}
37 changes: 34 additions & 3 deletions src/Post.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

class Post
{
private ?ImageStorage $imageStorage = null;

private string $title;

private string $description;
Expand All @@ -23,9 +25,6 @@ class Post
private ?string $mainImageUrl = null;


/**
* @param string[] $categories
*/
public function __construct(string $title, string $description, string $link, \DateTimeImmutable $date)
{
$this->title = $title;
Expand All @@ -35,6 +34,14 @@ public function __construct(string $title, string $description, string $link, \D
}


public function setImageStorage(?ImageStorage $imageStorage): self
{
$this->imageStorage = $imageStorage;

return $this;
}


public function getTitle(): string
{
return $this->title;
Expand Down Expand Up @@ -93,6 +100,30 @@ public function setCategories(array $categories): self
}


public function getAbsoluteInternalUrl(): ?string
{
if ($this->imageStorage === null) {
throw new \RuntimeException('Image storage does not set. Did you create instance this entity from Feed service?');
}

return $this->mainImageUrl !== null
? $this->imageStorage->getAbsoluteInternalUrl($this->mainImageUrl)
: null;
}


public function getRelativeInternalUrl(): ?string
{
if ($this->imageStorage === null) {
throw new \RuntimeException('Image storage does not set. Did you create instance this entity from Feed service?');
}

return $this->mainImageUrl !== null
? $this->imageStorage->getRelativeInternalUrl($this->mainImageUrl)
: null;
}


public function getMainImageUrl(): ?string
{
return $this->mainImageUrl;
Expand Down
37 changes: 37 additions & 0 deletions src/WordpressPostFeedExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
use Nette\DI\CompilerExtension;
use Nette\Schema\Expect;
use Nette\Schema\Schema;
use Nette\Utils\FileSystem;

final class WordpressPostFeedExtension extends CompilerExtension
{
public function getConfigSchema(): Schema
{
return Expect::structure([
'expirationTime' => Expect::string('2 hours'),
'imageStoragePath' => Expect::string(),
'imageRelativeStoragePath' => Expect::string(),
])->castTo('array');
}

Expand All @@ -28,5 +31,39 @@ public function beforeCompile(): void
$builder->addDefinition($this->prefix('feed'))
->setFactory(Feed::class)
->setArgument('expirationTime', $config['expirationTime'] ?? '2 hours');

$imageStoragePaths = $this->resolveImageStoragePath($config, $builder->parameters);
$builder->addDefinition($this->prefix('imageStorage'))
->setFactory(ImageStorage::class)
->setArgument('storagePath', $imageStoragePaths['storagePath'] ?? '')
->setArgument('relativeStoragePath', $imageStoragePaths['relativeStoragePath'] ?? '');
}


/**
* @param mixed[] $config
* @param mixed[] $parameters
* @return string[]
*/
private function resolveImageStoragePath(array $config, array $parameters): array
{
if (isset($config['imageStoragePath'])) {
$storagePath = (string) $config['imageStoragePath'];
if (isset($config['imageRelativeStoragePath']) === false) {
throw new \RuntimeException('Configuration option "imageRelativeStoragePath" is required when option "imageStoragePath" is declared.');
}
$relativeStoragePath = (string) $config['imageRelativeStoragePath'];
} elseif (isset($parameters['wwwDir'])) {
$relativeStoragePath = 'wordpress-post-feed';
$storagePath = $parameters['wwwDir'] . '/' . $relativeStoragePath;
} else {
throw new \RuntimeException('Configuration parameter "wwwDir" does not exist. Did you install Nette correctly?');
}
FileSystem::createDir($storagePath);

return [
'storagePath' => $storagePath,
'relativeStoragePath' => $relativeStoragePath,
];
}
}

0 comments on commit e3c7a88

Please sign in to comment.