├── .gitignore ├── tests ├── resources │ ├── teal.jpg │ ├── green.jpg │ ├── purple.jpg │ ├── green_large.jpg │ ├── twig │ │ ├── index.twig │ │ ├── _partials │ │ │ └── main.twig │ │ ├── detail.twig │ │ └── overview.twig │ ├── data │ │ └── entries.yaml │ └── config │ │ └── site.yaml ├── Stitcher │ ├── Template │ │ ├── TemplateRendererFactoryTest.php │ │ └── TwigRendererTest.php │ ├── Variable │ │ ├── JsonVariableTest.php │ │ ├── MarkdownVariableTest.php │ │ ├── YamlVariableTest.php │ │ ├── ImageVariableTest.php │ │ ├── VariableFactoryTest.php │ │ └── VariableParserTest.php │ ├── Page │ │ ├── Adapter │ │ │ ├── AdapterFactoryTest.php │ │ │ ├── CollectionAdapterTest.php │ │ │ ├── FilterAdapterTest.php │ │ │ └── PaginationAdapterTest.php │ │ ├── PageRendererTest.php │ │ ├── PageTest.php │ │ ├── PageFactoryTest.php │ │ └── PageParserTest.php │ └── Integration │ │ ├── BasicMetaTest.php │ │ ├── PaginatedMetaTest.php │ │ └── CollectionMetaTest.php ├── Pageon │ ├── Html │ │ ├── SiteMapTest.php │ │ ├── Meta │ │ │ ├── Item │ │ │ │ ├── CharsetMetaTest.php │ │ │ │ ├── LinkMetaTest.php │ │ │ │ ├── HttpEquivMetaTest.php │ │ │ │ ├── NameMetaTest.php │ │ │ │ ├── ItemPropMetaTest.php │ │ │ │ └── PropertyMetaTest.php │ │ │ ├── Social │ │ │ │ ├── GooglePlusMetaTest.php │ │ │ │ ├── TwitterMetaTest.php │ │ │ │ └── OpenGraphMetaTest.php │ │ │ └── MetaTest.php │ │ └── Image │ │ │ ├── ImageTest.php │ │ │ └── ImageFactoryTest.php │ ├── Stitcher │ │ └── Command │ │ │ └── ParseTest.php │ └── Integration │ │ └── FullSiteParseTest.php ├── StitcherTest.php ├── CreateStitcherFiles.php └── CreateStitcherObjects.php ├── src ├── Pageon │ ├── Stitcher │ │ ├── Console.php │ │ └── Command │ │ │ └── Parse.php │ └── Html │ │ ├── Meta │ │ ├── MetaItem.php │ │ ├── SocialMeta.php │ │ ├── Item │ │ │ ├── CharsetMeta.php │ │ │ ├── LinkMeta.php │ │ │ ├── NameMeta.php │ │ │ ├── ItemPropMeta.php │ │ │ ├── PropertyMeta.php │ │ │ └── HttpEquivMeta.php │ │ ├── Social │ │ │ ├── GooglePlusMeta.php │ │ │ ├── OpenGraphMeta.php │ │ │ └── TwitterMeta.php │ │ └── Meta.php │ │ ├── Image │ │ ├── Scaler.php │ │ ├── FixedWidthScaler.php │ │ ├── Image.php │ │ └── ImageFactory.php │ │ └── SiteMap.php └── Stitcher │ ├── Command.php │ ├── Validatory.php │ ├── Adapter.php │ ├── Renderer.php │ ├── Parseable.php │ ├── Variable │ ├── DefaultVariable.php │ ├── JsonVariable.php │ ├── AbstractVariable.php │ ├── MarkdownVariable.php │ ├── VariableParser.php │ ├── ImageVariable.php │ ├── YamlVariable.php │ └── VariableFactory.php │ ├── DynamicFactory.php │ ├── Page │ ├── PageRenderer.php │ ├── PageFactory.php │ ├── Page.php │ ├── PageParser.php │ └── Adapter │ │ ├── FilterAdapter.php │ │ ├── AdapterFactory.php │ │ ├── CollectionAdapter.php │ │ └── PaginationAdapter.php │ ├── Exception │ └── InvalidConfiguration.php │ ├── Renderer │ ├── TwigRenderer.php │ └── RendererFactory.php │ └── File.php ├── .scrutinizer.yml ├── phpunit.xml ├── VariableParsers.md └── composer.json /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | .DS_Store 3 | .idea 4 | /tests/.cache 5 | /tests/public 6 | /vendor 7 | -------------------------------------------------------------------------------- /tests/resources/teal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pageon/lib-static/master/tests/resources/teal.jpg -------------------------------------------------------------------------------- /tests/resources/green.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pageon/lib-static/master/tests/resources/green.jpg -------------------------------------------------------------------------------- /tests/resources/purple.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pageon/lib-static/master/tests/resources/purple.jpg -------------------------------------------------------------------------------- /src/Pageon/Stitcher/Console.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% if _meta is defined %} 4 | {{ _meta.render()|raw }} 5 | {% endif %} 6 | 7 | 8 | {% block content %}{% endblock %} 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Pageon/Html/Image/Scaler.php: -------------------------------------------------------------------------------- 1 | 6 |

{{ entry.title }}

7 | 8 | {% if entry.image is defined %} 9 | {{ entry.image.alt }} 12 | {% endif %} 13 | 14 | 15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /src/Stitcher/Variable/DefaultVariable.php: -------------------------------------------------------------------------------- 1 | parsed = $this->unparsed; 15 | 16 | return $this; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Stitcher/Variable/JsonVariable.php: -------------------------------------------------------------------------------- 1 | parsed = json_decode(File::read($this->unparsed), true); 17 | 18 | return $this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | build: 2 | environment: 3 | php: 4 | version: 7.1 5 | tests: 6 | override: 7 | - 8 | command: 'vendor/bin/phpunit --coverage-clover=coverage.xml' 9 | coverage: 10 | file: 'coverage.xml' 11 | format: 'clover' 12 | checks: 13 | php: 14 | code_rating: true 15 | duplication: true 16 | 17 | filter: 18 | excluded_paths: 19 | - "tests/" 20 | -------------------------------------------------------------------------------- /tests/Stitcher/Template/TemplateRendererFactoryTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(TwigRenderer::class, $factory->create('twig')); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | tests 10 | 11 | 12 | 13 | 14 | ./src 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/Stitcher/Variable/JsonVariableTest.php: -------------------------------------------------------------------------------- 1 | 'test', 16 | ])); 17 | 18 | $variable = JsonVariable::make($path)->parse(); 19 | 20 | $this->assertTrue(is_array($variable->parsed())); 21 | $this->assertArrayHasKey('test', $variable->parsed()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Stitcher/DynamicFactory.php: -------------------------------------------------------------------------------- 1 | rules[$class] = $callback; 12 | 13 | return $this; 14 | } 15 | 16 | public function removeRule(string $class): DynamicFactory 17 | { 18 | if (isset($this->rules[$class])) { 19 | unset($this->rules[$class]); 20 | } 21 | 22 | return $this; 23 | } 24 | 25 | protected function getRules(): array 26 | { 27 | return $this->rules; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Pageon/Html/SiteMapTest.php: -------------------------------------------------------------------------------- 1 | addPath('/blog'); 16 | $siteMap->addPath('/guide'); 17 | 18 | $xml = $siteMap->render(); 19 | 20 | $this->assertContains('stitcher.io/blog', $xml); 21 | $this->assertContains('stitcher.io/guide', $xml); 22 | $this->assertContains('', $xml); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Stitcher/Template/TwigRendererTest.php: -------------------------------------------------------------------------------- 1 | createAllTemplates(); 18 | 19 | $html = $renderer->renderTemplate('index.twig', [ 20 | 'variable' => 'hello world' 21 | ]); 22 | 23 | $this->assertContains('hello world', $html); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Stitcher/Page/PageRenderer.php: -------------------------------------------------------------------------------- 1 | renderer = $renderer; 14 | } 15 | 16 | public static function make(Renderer $renderer): PageRenderer 17 | { 18 | return new self($renderer); 19 | } 20 | 21 | public function render(Page $page): string 22 | { 23 | $variables = $page->variables(); 24 | $variables['_meta'] = $page->meta(); 25 | 26 | return $this->renderer->renderTemplate($page->template(), $variables); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/resources/config/site.yaml: -------------------------------------------------------------------------------- 1 | /entries/{id}: 2 | template: detail.twig 3 | variables: 4 | entry: data/entries.yaml 5 | config: 6 | collection: 7 | variable: entry 8 | parameter: id 9 | 10 | /entries-paginated: 11 | template: overview.twig 12 | variables: 13 | entries: data/entries.yaml 14 | config: 15 | pagination: 16 | variable: entries 17 | perPage: 2 18 | 19 | /entries: 20 | template: overview.twig 21 | variables: 22 | entries: data/entries.yaml 23 | 24 | /: 25 | template: index.twig 26 | variables: 27 | title: Hello World 28 | description: Description 29 | -------------------------------------------------------------------------------- /tests/Pageon/Html/Meta/Item/CharsetMetaTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($meta); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function it_can_be_rendered() { 23 | $meta = CharsetMeta::create('UTF-16'); 24 | $tag = $meta->render(); 25 | 26 | $this->assertContains('', $tag); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Pageon/Html/Meta/Item/LinkMetaTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($meta); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function it_can_be_rendered() { 23 | $meta = LinkMeta::create('next', '/?page=3'); 24 | $tag = $meta->render(); 25 | 26 | $this->assertContains('', $tag); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Pageon/Html/Meta/Item/HttpEquivMetaTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($meta); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function it_can_be_rendered() { 23 | $meta = HttpEquivMeta::create('Expires', '3000'); 24 | $tag = $meta->render(); 25 | 26 | $this->assertContains('', $tag); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /VariableParsers.md: -------------------------------------------------------------------------------- 1 | ```yaml 2 | // entries.yaml 3 | 4 | a: 5 | title: A 6 | image: 7 | src: image.jpg 8 | alt: test 9 | body: body.md 10 | 11 | // site.yaml 12 | 13 | /: 14 | variables: 15 | entries: entries.yaml 16 | ``` 17 | 18 | - foreach `site.yaml` as `pageId` => `pageConfig` 19 | - foreach `pageConfig['variables']` as `variableName` => `variableConfig` 20 | - `variableName` = parse(`variableConfig`) 21 | 22 | - foreach `variableConfig` as `propertyName` => `propertyConfig` 23 | - if factory(`propertyConfig`) > `variableName` = factory(`propertyConfig`)->parse 24 | - elseif is_array(`propertyConfig`) > foreach `propertyConfig` as `childPropertyName` => `childProperyConfig` 25 | - else `variableName` = `propertyConfig` 26 | -------------------------------------------------------------------------------- /src/Stitcher/Exception/InvalidConfiguration.php: -------------------------------------------------------------------------------- 1 | exists($dataDir)) { 31 | $fs->remove($dataDir); 32 | } 33 | 34 | File::base(null); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Stitcher/Variable/MarkdownVariableTest.php: -------------------------------------------------------------------------------- 1 | getMarkdown()); 14 | 15 | $variable = MarkdownVariable::make($path, new \Parsedown())->parse(); 16 | 17 | $this->assertTrue(is_string($variable->parsed())); 18 | $this->assertContains('

', $variable->parsed()); 19 | } 20 | 21 | private function getMarkdown() : string 22 | { 23 | return <<unparsed = $unparsed; 17 | } 18 | 19 | /** 20 | * @return mixed 21 | */ 22 | public function unparsed() 23 | { 24 | return $this->unparsed; 25 | } 26 | 27 | /** 28 | * @return mixed 29 | */ 30 | public function parsed() 31 | { 32 | if (! $this->parsed) { 33 | $this->parse(); 34 | } 35 | 36 | return $this->parsed; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/resources/twig/overview.twig: -------------------------------------------------------------------------------- 1 | {% extends '_partials/main.twig' %} 2 | 3 | {% block content %} 4 | 5 |
6 | {% for entry in entries %} 7 |

{{ entry.title }}

8 | {% endfor %} 9 |
10 | 11 |
12 | {% if _pagination is defined %} 13 | 14 | {% if _pagination.previous != null %} 15 | 16 | {{ _pagination.previous.index }} 17 | 18 | {% endif %} 19 | 20 | {% if _pagination.next != null %} 21 | 22 | {{ _pagination.next.index }} 23 | 24 | {% endif %} 25 | 26 | {% endif %} 27 |
28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /tests/Stitcher/Page/Adapter/AdapterFactoryTest.php: -------------------------------------------------------------------------------- 1 | createVariableParser()); 16 | 17 | $this->assertInstanceOf(CollectionAdapter::class, $factory->create('collection', ['variable' => 'test', 'parameter' => 'id'])); 18 | $this->assertInstanceOf(FilterAdapter::class, $factory->create('filter', ['entries' => ['name' => 'A']])); 19 | $this->assertInstanceOf(PaginationAdapter::class, $factory->create('pagination', ['variable' => 'entries'])); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Pageon/Html/Meta/Item/CharsetMeta.php: -------------------------------------------------------------------------------- 1 | charset}\">"; 28 | } 29 | 30 | /** 31 | * CharsetMeta constructor. 32 | * 33 | * @param string $charset 34 | */ 35 | public function __construct(string $charset) { 36 | $this->charset = $charset; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Stitcher/Variable/YamlVariableTest.php: -------------------------------------------------------------------------------- 1 | createVariableParser())->parse(); 28 | 29 | $this->assertTrue(is_array($variable->parsed())); 30 | $this->assertTrue(isset($variable->parsed()['root']['entry'])); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Pageon/Html/Image/ImageTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(Image::class, $image); 16 | } 17 | 18 | /** @test */ 19 | public function it_can_be_made_with_sizes() 20 | { 21 | $image = Image::make('resources/green.jpg', '100vw'); 22 | 23 | $this->assertEquals('100vw', $image->sizes()); 24 | } 25 | 26 | /** @test */ 27 | public function it_can_be_made_with_alt() 28 | { 29 | $image = Image::make('resources/green.jpg', null, 'alt'); 30 | 31 | $this->assertEquals('alt', $image->alt()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Pageon/Html/Meta/Social/GooglePlusMeta.php: -------------------------------------------------------------------------------- 1 | meta = $meta; 14 | } 15 | 16 | public function title(string $title) : SocialMeta { 17 | $this->meta->itemprop('name', $title); 18 | 19 | return $this; 20 | } 21 | 22 | public function description(string $description) : SocialMeta { 23 | $this->meta->itemprop('description', $description); 24 | 25 | return $this; 26 | } 27 | 28 | public function image(string $image) : SocialMeta { 29 | $this->meta->itemprop('image', $image); 30 | 31 | return $this; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Stitcher/Renderer/TwigRenderer.php: -------------------------------------------------------------------------------- 1 | exists($templateDirectory)) { 15 | $fs->mkdir($templateDirectory); 16 | } 17 | 18 | $loader = new \Twig_Loader_Filesystem($templateDirectory); 19 | 20 | parent::__construct($loader); 21 | } 22 | 23 | public static function make(string $templateDirectory): TwigRenderer 24 | { 25 | return new self($templateDirectory); 26 | } 27 | 28 | public function renderTemplate(string $path, array $variables): string 29 | { 30 | return $this->render($path, $variables); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Pageon/Html/Meta/Social/OpenGraphMeta.php: -------------------------------------------------------------------------------- 1 | meta = $meta; 14 | 15 | $this->meta->property('og:type', $type); 16 | } 17 | 18 | public function title(string $title) : SocialMeta { 19 | $this->meta->property('og:title', $title); 20 | 21 | return $this; 22 | } 23 | 24 | public function description(string $description) : SocialMeta { 25 | $this->meta->property('og:description', $description); 26 | 27 | return $this; 28 | } 29 | 30 | public function image(string $image) : SocialMeta { 31 | $this->meta->property('og:image', $image); 32 | 33 | return $this; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Pageon/Html/Image/FixedWidthScaler.php: -------------------------------------------------------------------------------- 1 | fixedWidths = $fixedWidths; 14 | } 15 | 16 | public static function make(array $fixedWidths): FixedWidthScaler 17 | { 18 | return new self($fixedWidths); 19 | } 20 | 21 | public function getVariations(ScaleableImage $scaleableImage): array 22 | { 23 | $width = $scaleableImage->getWidth(); 24 | $height = $scaleableImage->getHeight(); 25 | $ratio = $width / $height; 26 | $variations = []; 27 | 28 | foreach ($this->fixedWidths as $fixedWidth) { 29 | $variations[$fixedWidth] = round($fixedWidth * $ratio); 30 | } 31 | 32 | return $variations; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Pageon/Html/Meta/Social/TwitterMeta.php: -------------------------------------------------------------------------------- 1 | meta = $meta; 14 | 15 | $this->meta->name('twitter:card', $type); 16 | } 17 | 18 | public function title(string $title) : SocialMeta { 19 | $this->meta->name('twitter:title', $title); 20 | 21 | return $this; 22 | } 23 | 24 | public function description(string $description) : SocialMeta { 25 | $this->meta->name('twitter:description', substr($description, 0, 199)); 26 | 27 | return $this; 28 | } 29 | 30 | public function image(string $image) : SocialMeta { 31 | $this->meta->name('twitter:image', $image); 32 | 33 | return $this; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Stitcher/Variable/MarkdownVariable.php: -------------------------------------------------------------------------------- 1 | parser = $parser; 18 | } 19 | 20 | public static function make(string $value, Parsedown $parser): MarkdownVariable 21 | { 22 | return new self($value, $parser); 23 | } 24 | 25 | public function parse(): AbstractVariable 26 | { 27 | $contents = File::read($this->unparsed); 28 | 29 | if (! $contents) { 30 | throw InvalidConfiguration::fileNotFound($this->unparsed); 31 | } 32 | 33 | $this->parsed = $this->parser->parse($contents); 34 | 35 | return $this; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Stitcher/Variable/VariableParser.php: -------------------------------------------------------------------------------- 1 | factory = $factory; 12 | $this->factory->setVariableParser($this); 13 | } 14 | 15 | public static function make(VariableFactory $factory): VariableParser 16 | { 17 | return new self($factory); 18 | } 19 | 20 | public function parse($unparsedValue) 21 | { 22 | $variable = $this->factory->create($unparsedValue); 23 | 24 | if ($variable) { 25 | $parsedValue = $variable->parsed(); 26 | } else { 27 | $parsedValue = $unparsedValue; 28 | } 29 | 30 | return $parsedValue; 31 | } 32 | 33 | public function getVariable($unparsedValue): AbstractVariable 34 | { 35 | return $this->factory->create($unparsedValue); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Stitcher/Variable/ImageVariable.php: -------------------------------------------------------------------------------- 1 | imageFactory = $imageFactory; 17 | $this->alt = $alt; 18 | } 19 | 20 | public static function make(string $value, ImageFactory $imageFactory, ?string $alt = '') : ImageVariable 21 | { 22 | return new self($value, $imageFactory, $alt); 23 | } 24 | 25 | public function parse() : AbstractVariable 26 | { 27 | $image = $this->imageFactory->create($this->unparsed); 28 | 29 | $this->parsed = [ 30 | 'src' => $image->src(), 31 | 'srcset' => $image->srcset(), 32 | 'alt' => $this->alt, 33 | ]; 34 | 35 | return $this; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Stitcher/Page/PageRendererTest.php: -------------------------------------------------------------------------------- 1 | createAllTemplates(); 19 | 20 | $variableParser = $this->createVariableParser(); 21 | $parser = $this->createPageParser($variableParser); 22 | $result = $parser->parse([ 23 | 'id' => '/', 24 | 'template' => 'index.twig', 25 | 'variables' => [ 26 | 'variable' => 'Hello world', 27 | ], 28 | ]); 29 | 30 | $renderer = $this->createPageRenderer(); 31 | $html = $renderer->render($result->first()); 32 | 33 | $this->assertContains('Hello world', $html); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Pageon/Html/Meta/Item/LinkMeta.php: -------------------------------------------------------------------------------- 1 | rel}\" href=\"{$this->href}\">"; 34 | } 35 | 36 | /** 37 | * LinkMeta constructor. 38 | * 39 | * @param string $rel 40 | * @param string $href 41 | */ 42 | public function __construct(string $rel, string $href) { 43 | $this->rel = $rel; 44 | $this->href = $href; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/Pageon/Html/Meta/Item/NameMetaTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($meta); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function it_can_be_rendered() { 23 | $meta = NameMeta::create('title', 'Hello World'); 24 | $tag = $meta->render(); 25 | 26 | $this->assertContains('', $tag); 27 | } 28 | 29 | /** 30 | * @test 31 | */ 32 | public function it_escapes_special_characters() { 33 | $meta = NameMeta::create('title', '""'); 34 | $tag = $meta->render(); 35 | 36 | $this->assertContains('"', $tag); 37 | $this->assertContains('>', $tag); 38 | $this->assertContains('<', $tag); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Pageon/Html/Meta/Item/NameMeta.php: -------------------------------------------------------------------------------- 1 | name}\" content=\"{$this->content}\">"; 34 | } 35 | 36 | /** 37 | * NamedMeta constructor. 38 | * 39 | * @param string $name 40 | * @param string $content 41 | */ 42 | public function __construct(string $name, string $content) { 43 | $this->name = $name; 44 | $this->content = htmlentities($content); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/Pageon/Html/Meta/Item/ItemPropMeta.php: -------------------------------------------------------------------------------- 1 | name}\" content=\"{$this->content}\">"; 34 | } 35 | 36 | /** 37 | * NamedMeta constructor. 38 | * 39 | * @param string $name 40 | * @param string $content 41 | */ 42 | public function __construct(string $name, string $content) { 43 | $this->name = $name; 44 | $this->content = htmlentities($content); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/Stitcher/Page/PageFactory.php: -------------------------------------------------------------------------------- 1 | variableParser = $variableParser; 15 | } 16 | 17 | public static function make(VariableParser $variableParser): PageFactory 18 | { 19 | return new self($variableParser); 20 | } 21 | 22 | public function create($value): Page 23 | { 24 | $id = $value['id'] ?? null; 25 | $template = $value['template'] ?? null; 26 | $variables = $value['variables'] ?? []; 27 | 28 | if (! $id || ! $template) { 29 | throw InvalidConfiguration::pageIdAndTemplateRequired(); 30 | } 31 | 32 | foreach ($variables as $key => $variable) { 33 | $variables[$key] = $this->variableParser->parse($variable); 34 | } 35 | 36 | $page = Page::make($id, $template, $variables); 37 | 38 | return $page; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Pageon/Html/Meta/Item/PropertyMeta.php: -------------------------------------------------------------------------------- 1 | name}\" content=\"{$this->content}\">"; 34 | } 35 | 36 | /** 37 | * NamedMeta constructor. 38 | * 39 | * @param string $name 40 | * @param string $content 41 | */ 42 | public function __construct(string $name, string $content) { 43 | $this->name = $name; 44 | $this->content = htmlentities($content); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /tests/Pageon/Html/Meta/Item/ItemPropMetaTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($meta); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function it_can_be_rendered() { 23 | $meta = ItemPropMeta::create('title', 'Hello World'); 24 | $tag = $meta->render(); 25 | 26 | $this->assertContains('', $tag); 27 | } 28 | 29 | /** 30 | * @test 31 | */ 32 | public function it_escapes_special_characters() { 33 | $meta = ItemPropMeta::create('title', '""'); 34 | $tag = $meta->render(); 35 | 36 | $this->assertContains('"', $tag); 37 | $this->assertContains('>', $tag); 38 | $this->assertContains('<', $tag); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Pageon/Html/Meta/Item/PropertyMetaTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($meta); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function it_can_be_rendered() { 23 | $meta = PropertyMeta::create('title', 'Hello World'); 24 | $tag = $meta->render(); 25 | 26 | $this->assertContains('', $tag); 27 | } 28 | 29 | /** 30 | * @test 31 | */ 32 | public function it_escapes_special_characters() { 33 | $meta = PropertyMeta::create('title', '""'); 34 | $tag = $meta->render(); 35 | 36 | $this->assertContains('"', $tag); 37 | $this->assertContains('>', $tag); 38 | $this->assertContains('<', $tag); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Pageon/Html/Meta/Item/HttpEquivMeta.php: -------------------------------------------------------------------------------- 1 | httpEquiv}\" content=\"{$this->content}\">"; 34 | } 35 | 36 | /** 37 | * HttpEquivMeta constructor. 38 | * 39 | * @param string $httpEquiv 40 | * @param string $content 41 | */ 42 | public function __construct(string $httpEquiv, string $content) { 43 | $this->httpEquiv = $httpEquiv; 44 | $this->content = $content; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Stitcher/File.php: -------------------------------------------------------------------------------- 1 | dumpFile(self::path($path), $content); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Pageon/Stitcher/Command/ParseTest.php: -------------------------------------------------------------------------------- 1 | createAllTemplates(); 20 | $this->createConfigurationFile(); 21 | 22 | $command = Parse::make( 23 | File::path('public'), 24 | File::path('site.yaml'), 25 | $this->createPageParser(), 26 | $this->createPageRenderer() 27 | ); 28 | 29 | $command->execute(); 30 | 31 | $this->assertFileExists(File::path('public/index.html')); 32 | $this->assertFileExists(File::path('public/test.html')); 33 | } 34 | 35 | private function createConfigurationFile() 36 | { 37 | File::write('site.yaml', <<src = "/{$src}"; 15 | $this->sizes = $sizes; 16 | $this->alt = $alt; 17 | } 18 | 19 | public static function make(string $src, ?string $sizes = null, ?string $alt = null): Image 20 | { 21 | return new self($src, $sizes, $alt); 22 | } 23 | 24 | public function src(): string 25 | { 26 | return $this->src; 27 | } 28 | 29 | public function srcset(): string 30 | { 31 | return implode(', ', $this->srcset); 32 | } 33 | 34 | public function sizes(): ?string 35 | { 36 | return $this->sizes; 37 | } 38 | 39 | public function alt(): ?string 40 | { 41 | return $this->alt; 42 | } 43 | 44 | public function addSrcset(string $src, int $width): Image 45 | { 46 | $src = ltrim($src, '/'); 47 | 48 | $this->srcset[] = "/{$src} {$width}w"; 49 | 50 | return $this; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Pageon/Html/SiteMap.php: -------------------------------------------------------------------------------- 1 | hostname = trim((string) $hostname, '/'); 13 | } 14 | 15 | public static function make(string $hostname): SiteMap 16 | { 17 | return new self($hostname); 18 | } 19 | 20 | public function addPath(string $path): SiteMap 21 | { 22 | $path = trim($path, '/'); 23 | 24 | $this->urls[] = "{$this->hostname}/{$path}"; 25 | 26 | return $this; 27 | } 28 | 29 | public function render(): string 30 | { 31 | $xml = '' . "\n"; 32 | $xml .= '' . "\n"; 33 | $dateModified = date('c'); 34 | 35 | foreach ($this->urls as $url) { 36 | $xml .= "\t\n"; 37 | $xml .= "\t\t{$url}\n"; 38 | $xml .= "\t\t{$dateModified}\n"; 39 | $xml .= "\t\n"; 40 | } 41 | 42 | $xml .= ''; 43 | 44 | return $xml; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Stitcher/Renderer/RendererFactory.php: -------------------------------------------------------------------------------- 1 | templateDirectory = $templateDirectory; 15 | 16 | $this->setTwigRule(); 17 | } 18 | 19 | public static function make(string $templateDirectory): RendererFactory 20 | { 21 | return new self($templateDirectory); 22 | } 23 | 24 | public function create($value): ?Renderer 25 | { 26 | foreach ($this->getRules() as $rule) { 27 | $templateRenderer = $rule($value); 28 | 29 | if ($templateRenderer) { 30 | return $templateRenderer; 31 | } 32 | } 33 | 34 | return null; 35 | } 36 | 37 | private function setTwigRule(): void 38 | { 39 | $this->setRule(TwigRenderer::class, function ($value) { 40 | if ($value === 'twig') { 41 | return TwigRenderer::make($this->templateDirectory); 42 | } 43 | 44 | return null; 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Stitcher/Integration/BasicMetaTest.php: -------------------------------------------------------------------------------- 1 | createPageParser(); 17 | 18 | /** @var \Stitcher\Page\Page $page */ 19 | $page = $pageParser->parse($this->createConfiguration())->first(); 20 | $meta = $page->meta()->render(); 21 | 22 | $this->assertContains('', $meta); 23 | $this->assertContains('', $meta); 24 | $this->assertContains('', $meta); 25 | $this->assertContains('', $meta); 26 | $this->assertContains('', $meta); 27 | $this->assertContains('', $meta); 28 | } 29 | 30 | private function createConfiguration(): array 31 | { 32 | return Yaml::parse(<<meta = new Meta(); 17 | } 18 | 19 | private function createSocialMeta() : SocialMeta { 20 | return new GooglePlusMeta($this->meta); 21 | } 22 | 23 | /** @test */ 24 | public function it_can_render_the_title() { 25 | $social = $this->createSocialMeta(); 26 | 27 | $social->title('hello'); 28 | 29 | $this->assertContains('', $this->meta->render()); 30 | } 31 | 32 | /** @test */ 33 | public function it_can_render_the_description() { 34 | $social = $this->createSocialMeta(); 35 | 36 | $social->description('hello'); 37 | 38 | $this->assertContains('', $this->meta->render()); 39 | } 40 | 41 | /** @test */ 42 | public function it_can_render_the_image() { 43 | $social = $this->createSocialMeta(); 44 | 45 | $social->image('hello'); 46 | 47 | $this->assertContains('', $this->meta->render()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/Stitcher/Page/PageTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(Page::class, $page); 15 | } 16 | 17 | /** @test */ 18 | public function it_sets_default_meta_from_variables() 19 | { 20 | $page = Page::make('/home', 'index.twig', [ 21 | 'title' => 'title', 22 | 'description' => 'description' 23 | ]); 24 | 25 | $meta = $page->meta()->render(); 26 | $this->assertContains('', $meta); 27 | $this->assertContains('', $meta); 28 | } 29 | 30 | /** @test */ 31 | public function it_sets_default_meta_from_meta_variables() 32 | { 33 | $page = Page::make('/home', 'index.twig', [ 34 | 'title' => 'title', 35 | 'description' => 'description', 36 | 'meta' => [ 37 | 'title' => 'title2', 38 | 'description' => 'description2', 39 | ], 40 | ]); 41 | 42 | $meta = $page->meta()->render(); 43 | $this->assertContains('', $meta); 44 | $this->assertContains('', $meta); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/Stitcher/Page/Adapter/CollectionAdapterTest.php: -------------------------------------------------------------------------------- 1 | '/{id}', 26 | 'template' => 'index.twig', 27 | 'variables' => [ 28 | 'entry' => 'entries.yaml', 29 | ], 30 | 'config' => [ 31 | 'collection' => [ 32 | 'variable' => 'entry', 33 | 'parameter' => 'id', 34 | ], 35 | ], 36 | ]; 37 | 38 | $adapter = CollectionAdapter::make($pageConfiguration['config']['collection'], $this->createVariableParser()); 39 | $result = $adapter->transform($pageConfiguration); 40 | 41 | $this->assertTrue(is_array($result)); 42 | $this->assertArrayHasKey('/a', $result); 43 | $this->assertEquals('A', $result['/a']['variables']['entry']['name']); 44 | $this->assertArrayHasKey('/b', $result); 45 | $this->assertEquals('B', $result['/b']['variables']['entry']['name']); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Stitcher/Variable/ImageVariableTest.php: -------------------------------------------------------------------------------- 1 | createImageFactory() 19 | )->parse(); 20 | 21 | $parsed = $variable->parsed(); 22 | $this->assertArrayHasKey('src', $parsed, '`src` not found in parsed image.'); 23 | $this->assertArrayHasKey('srcset', $parsed, '`srcset not found in parsed image.`'); 24 | $this->assertEquals('/resources/green.jpg', $parsed['src'], '`src` does not match expected value in parsed image.'); 25 | } 26 | 27 | /** @test */ 28 | public function it_can_be_parsed_with_alt() 29 | { 30 | $variable = ImageVariable::make('/resources/green.jpg', $this->createImageFactory(), 'test')->parse(); 31 | 32 | $parsed = $variable->parsed(); 33 | $this->assertArrayHasKey('alt', $parsed, '`alt not found in parsed image.`'); 34 | $this->assertEquals('test', $parsed['alt'], '`alt` does not match expected value in parsed image.'); 35 | $this->assertEquals('/resources/green.jpg', $parsed['src'], '`src` does not match expected value in parsed image.'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Stitcher/Integration/PaginatedMetaTest.php: -------------------------------------------------------------------------------- 1 | createPageParser(); 17 | 18 | $pages = $pageParser->parse($this->createConfiguration()); 19 | 20 | $metaPage1 = $pages->get('test/page-1')->meta()->render(); 21 | $metaPage2 = $pages->get('test/page-2')->meta()->render(); 22 | $metaPage3 = $pages->get('test/page-3')->meta()->render(); 23 | 24 | $this->assertContains('next', $metaPage1); 25 | $this->assertNotContains('prev', $metaPage1); 26 | 27 | $this->assertContains('next', $metaPage2); 28 | $this->assertContains('prev', $metaPage2); 29 | 30 | $this->assertNotContains('next', $metaPage3); 31 | $this->assertContains('prev', $metaPage3); 32 | } 33 | 34 | private function createConfiguration(): array 35 | { 36 | return Yaml::parse(<<assertNotNull($meta); 17 | } 18 | 19 | public function test_social_meta_title() { 20 | $meta = Meta::create(); 21 | $meta->title('test'); 22 | $html = $meta->render(); 23 | 24 | $this->assertContains('assertContains('assertContains('image('test'); 42 | $html = $meta->render(); 43 | 44 | $this->assertContains('assertContains('', $this->meta->render()); 31 | $this->assertContains('', $this->meta->render()); 32 | } 33 | 34 | /** @test */ 35 | public function it_can_render_the_description() { 36 | $social = $this->createSocialMeta(); 37 | 38 | $social->description('hello'); 39 | 40 | $this->assertContains('', $this->meta->render()); 41 | } 42 | 43 | /** @test */ 44 | public function it_can_render_the_image() { 45 | $social = $this->createSocialMeta(); 46 | 47 | $social->image('hello'); 48 | 49 | $this->assertContains('', $this->meta->render()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/Pageon/Html/Meta/Social/OpenGraphMetaTest.php: -------------------------------------------------------------------------------- 1 | meta = new Meta(); 18 | } 19 | 20 | private function createSocialMeta() : SocialMeta { 21 | return new OpenGraphMeta($this->meta); 22 | } 23 | 24 | /** @test */ 25 | public function it_can_render_the_title() { 26 | $social = $this->createSocialMeta(); 27 | 28 | $social->title('hello'); 29 | 30 | $this->assertContains('', $this->meta->render()); 31 | $this->assertContains('', $this->meta->render()); 32 | } 33 | 34 | /** @test */ 35 | public function it_can_render_the_description() { 36 | $social = $this->createSocialMeta(); 37 | 38 | $social->description('hello'); 39 | 40 | $this->assertContains('', $this->meta->render()); 41 | } 42 | 43 | /** @test */ 44 | public function it_can_render_the_image() { 45 | $social = $this->createSocialMeta(); 46 | 47 | $social->image('hello'); 48 | 49 | $this->assertContains('', $this->meta->render()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/Stitcher/Page/Adapter/FilterAdapterTest.php: -------------------------------------------------------------------------------- 1 | '/', 32 | 'template' => 'index.twig', 33 | 'variables' => [ 34 | 'entries' => 'entries.yaml', 35 | ], 36 | 'config' => [ 37 | 'filter' => [ 38 | 'entries' => [ 39 | 'category' => 'blog', 40 | 'name' => 'A', 41 | ], 42 | ], 43 | ], 44 | ]; 45 | 46 | $adapter = FilterAdapter::make($pageConfiguration['config']['filter'], $this->createVariableParser()); 47 | $result = $adapter->transform($pageConfiguration); 48 | $entries = $result['variables']['entries']; 49 | 50 | $this->assertArrayHasKey('a', $entries); 51 | $this->assertArrayNotHasKey('b', $entries); 52 | $this->assertArrayNotHasKey('c', $entries); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/Pageon/Html/Image/ImageFactoryTest.php: -------------------------------------------------------------------------------- 1 | create('resources/green_large.jpg'); 22 | 23 | $this->assertNotNull(File::read('public/resources/green_large.jpg')); 24 | $this->assertNotNull(File::read('public/resources/green_large-500x500.jpg')); 25 | $this->assertNotNull(File::read('public/resources/green_large-300x300.jpg')); 26 | } 27 | 28 | /** @test */ 29 | public function it_adds_the_srcset() 30 | { 31 | $public = File::path('public'); 32 | 33 | $factory = ImageFactory::make(__DIR__ . '/../../../', $public, FixedWidthScaler::make([ 34 | 300, 500, 35 | ])); 36 | 37 | $image = $factory->create('resources/green_large.jpg'); 38 | $srcset = $image->srcset(); 39 | 40 | $this->assertContains('/resources/green_large.jpg 2500w', $srcset); 41 | $this->assertContains('/resources/green_large-500x500.jpg 500w', $srcset); 42 | $this->assertContains('/resources/green_large-300x300.jpg 300w', $srcset); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Stitcher/Variable/YamlVariable.php: -------------------------------------------------------------------------------- 1 | parser = $parser; 18 | $this->variableParser = $variableParser; 19 | } 20 | 21 | public static function make(string $value, Yaml $parser, VariableParser $variableParser): YamlVariable 22 | { 23 | return new self($value, $parser, $variableParser); 24 | } 25 | 26 | public function parse(): AbstractVariable 27 | { 28 | $this->parsed = $this->parser->parse(File::read($this->unparsed)); 29 | 30 | $this->parsed = $this->parseRecursive($this->parsed); 31 | 32 | return $this; 33 | } 34 | 35 | private function parseRecursive($unparsedValue) 36 | { 37 | $unparsedValue = $this->variableParser->getVariable($unparsedValue); 38 | 39 | if ($unparsedValue instanceof DefaultVariable) { 40 | $parsedValue = $unparsedValue->parsed(); 41 | 42 | if (is_array($parsedValue)) { 43 | foreach ($parsedValue as &$property) { 44 | $property = $this->parseRecursive($property); 45 | } 46 | } 47 | } else { 48 | $parsedValue = $unparsedValue->parsed(); 49 | } 50 | 51 | return $parsedValue; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Stitcher/Page/PageFactoryTest.php: -------------------------------------------------------------------------------- 1 | create([ 22 | 'id' => '/', 23 | 'template' => 'index.twig', 24 | ]); 25 | 26 | $this->assertInstanceOf(Page::class, $page); 27 | } 28 | 29 | /** @test */ 30 | public function it_throws_an_exception_when_id_is_missing() 31 | { 32 | $this->expectException(InvalidConfiguration::class); 33 | 34 | $factory = new PageFactory( 35 | VariableParser::make( 36 | VariableFactory::make() 37 | ) 38 | ); 39 | 40 | $factory->create([ 41 | 'template' => 'index.twig', 42 | ]); 43 | } 44 | 45 | /** @test */ 46 | public function it_throws_an_exception_when_template_is_missing() 47 | { 48 | $this->expectException(InvalidConfiguration::class); 49 | 50 | $factory = new PageFactory( 51 | VariableParser::make( 52 | VariableFactory::make() 53 | ) 54 | ); 55 | 56 | $factory->create([ 57 | 'id' => '/', 58 | ]); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/Stitcher/Integration/CollectionMetaTest.php: -------------------------------------------------------------------------------- 1 | createPageParser(); 17 | 18 | $pages = $pageParser->parse($this->createConfiguration()); 19 | 20 | $metaPage1 = $pages['/a']->meta()->render(); 21 | $metaPage2 = $pages['/b']->meta()->render(); 22 | 23 | $this->assertContains('', $metaPage1); 24 | $this->assertContains('', $metaPage1); 25 | 26 | $this->assertContains('', $metaPage2); 27 | $this->assertContains('', $metaPage2); 28 | } 29 | 30 | private function createConfiguration(): array 31 | { 32 | return Yaml::parse(<<id = $id; 17 | $this->template = $template; 18 | $this->variables = $variables; 19 | $this->setMeta(); 20 | } 21 | 22 | public static function make(string $id, string $template, array $variables = []): Page 23 | { 24 | return new self($id, $template, $variables); 25 | } 26 | 27 | public function id(): string 28 | { 29 | return $this->id; 30 | } 31 | 32 | public function template(): string 33 | { 34 | return $this->template; 35 | } 36 | 37 | public function variables(): array 38 | { 39 | return $this->variables; 40 | } 41 | 42 | public function variable(string $name) 43 | { 44 | return $this->variables[$name] ?? null; 45 | } 46 | 47 | public function meta() 48 | { 49 | return $this->meta; 50 | } 51 | 52 | private function setMeta() 53 | { 54 | $this->meta = Meta::create() 55 | ->title($this->variables['meta']['title'] ?? $this->variables['title'] ?? null) 56 | ->description($this->variables['meta']['description'] ?? $this->variables['description'] ?? null) 57 | ->link('next', $this->variables['_pagination']['next']['url'] ?? null) 58 | ->link('prev', $this->variables['_pagination']['previous']['url'] ?? null) 59 | ; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/CreateStitcherFiles.php: -------------------------------------------------------------------------------- 1 | copy(__DIR__ . '/resources/twig/index.twig', File::path('template/index.twig')); 15 | } 16 | 17 | protected function createAllTemplates(): void 18 | { 19 | $fs = new Filesystem(); 20 | 21 | $fs->copy(__DIR__ . '/resources/twig/_partials/main.twig', File::path('template/_partials/main.twig')); 22 | $fs->copy(__DIR__ . '/resources/twig/overview.twig', File::path('template/overview.twig')); 23 | $fs->copy(__DIR__ . '/resources/twig/detail.twig', File::path('template/detail.twig')); 24 | $this->createIndexTemplate(); 25 | } 26 | 27 | protected function createDataFile() 28 | { 29 | $fs = new Filesystem(); 30 | 31 | $fs->copy(__DIR__ . '/resources/data/entries.yaml', File::path('data/entries.yaml')); 32 | } 33 | 34 | protected function createImageFiles() 35 | { 36 | $fs = new Filesystem(); 37 | 38 | $fs->copy(__DIR__ . '/resources/green_large.jpg', File::path('images/green_large.jpg')); 39 | $fs->copy(__DIR__ . '/resources/green.jpg', File::path('images/green.jpg')); 40 | } 41 | 42 | protected function createSiteConfiguration(string $configurationPath = null): void 43 | { 44 | $fs = new Filesystem(); 45 | $configurationPath = $configurationPath ?? File::path('config/site.yaml'); 46 | 47 | $fs->copy(__DIR__ . '/resources/config/site.yaml', $configurationPath); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/Stitcher/Variable/VariableFactoryTest.php: -------------------------------------------------------------------------------- 1 | setYamlParser(new Yaml()) 23 | ->setMarkdownParser(new \Parsedown()) 24 | ->setImageParser($this->createImageFactory()) 25 | ->setVariableParser($this->createVariableParser()); 26 | 27 | $this->assertInstanceOf(ImageVariable::class, $factory->create('image.jpg')); 28 | $this->assertInstanceOf(JsonVariable::class, $factory->create('test.json')); 29 | $this->assertInstanceOf(YamlVariable::class, $factory->create('test.yaml')); 30 | $this->assertInstanceOf(YamlVariable::class, $factory->create('test.yml')); 31 | $this->assertInstanceOf(MarkdownVariable::class, $factory->create('test.md')); 32 | $this->assertInstanceOf(ImageVariable::class, $factory->create('image.jpeg')); 33 | $this->assertInstanceOf(ImageVariable::class, $factory->create('image.png')); 34 | $this->assertInstanceOf(ImageVariable::class, $factory->create('image.gif')); 35 | $this->assertInstanceOf(ImageVariable::class, $factory->create([ 36 | 'src' => 'image.jpeg', 37 | ])); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Stitcher/Variable/VariableParserTest.php: -------------------------------------------------------------------------------- 1 | createVariableParser(); 24 | $parsed = $variableParser->parse($path); 25 | 26 | $this->assertTrue(is_array($parsed)); 27 | $this->assertTrue(isset($parsed['entry']['title'])); 28 | } 29 | 30 | /** @test */ 31 | public function it_can_be_parsed_recursively() 32 | { 33 | $path = File::path('YamlVariableTest_test_recursive_parent.yaml'); 34 | $this->createRecursiveFiles($path); 35 | 36 | $variableParser = $this->createVariableParser(); 37 | $parsed = $variableParser->parse($path); 38 | 39 | $this->assertTrue(isset($parsed['entry']['child']['title'])); 40 | } 41 | 42 | private function createRecursiveFiles(string $path) 43 | { 44 | $parentPath = File::path($path); 45 | File::write($parentPath, <<pageFactory = $pageFactory; 16 | $this->adapterFactory = $adapterFactory; 17 | } 18 | 19 | public static function make(PageFactory $factory, AdapterFactory $adapterFactory): PageParser 20 | { 21 | return new self($factory, $adapterFactory); 22 | } 23 | 24 | public function parse($inputConfiguration): Collection 25 | { 26 | $result = []; 27 | $adaptedInputConfiguration = $this->parseAdapterConfiguration($inputConfiguration); 28 | 29 | foreach ($adaptedInputConfiguration as $adaptedPageConfiguration) { 30 | $page = $this->parsePage($adaptedPageConfiguration); 31 | 32 | $result[$page->id()] = $page; 33 | } 34 | 35 | return collect($result); 36 | } 37 | 38 | private function parseAdapterConfiguration(array $pageConfiguration): array 39 | { 40 | $result = [$pageConfiguration]; 41 | $adapterConfigurations = $pageConfiguration['config'] ?? $pageConfiguration['adapters'] ?? []; 42 | 43 | foreach ($adapterConfigurations as $adapterType => $adapterConfiguration) { 44 | $adapter = $this->adapterFactory->create($adapterType, $adapterConfiguration); 45 | $transformedPages = []; 46 | 47 | foreach ($result as $pageToTransform) { 48 | $transformedPages = array_merge($transformedPages, $adapter->transform($pageToTransform)); 49 | } 50 | 51 | $result = $transformedPages; 52 | } 53 | 54 | return $result; 55 | } 56 | 57 | private function parsePage($inputConfiguration): Page 58 | { 59 | return $this->pageFactory->create($inputConfiguration); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Stitcher/Page/Adapter/FilterAdapter.php: -------------------------------------------------------------------------------- 1 | isValid($adapterConfiguration)) { 18 | throw InvalidConfiguration::invalidAdapterConfiguration('filter', '`field`: `filter`'); 19 | } 20 | 21 | $this->filters = $adapterConfiguration; 22 | $this->variableParser = $variableParser; 23 | } 24 | 25 | public static function make(array $adapterConfiguration, VariableParser $variableParser): FilterAdapter 26 | { 27 | return new self($adapterConfiguration, $variableParser); 28 | } 29 | 30 | public function transform(array $pageConfiguration): array 31 | { 32 | foreach ($this->filters as $variableName => $filterConfiguration) { 33 | $variable = $pageConfiguration['variables'][$variableName] ?? null; 34 | $entries = $this->variableParser->parse($variable)['entries'] ?? []; 35 | $filteredEntries = $this->filterEntries($filterConfiguration, $entries); 36 | 37 | $pageConfiguration['variables'][$variableName] = $filteredEntries; 38 | } 39 | 40 | unset($pageConfiguration['config']['filter']); 41 | 42 | return $pageConfiguration; 43 | } 44 | 45 | public function isValid($subject): bool 46 | { 47 | return is_array($subject); 48 | } 49 | 50 | private function filterEntries($filterConfiguration, $entries): array 51 | { 52 | foreach ($filterConfiguration as $filterField => $filterValue) { 53 | foreach ($entries as $entryId => $entry) { 54 | $value = $entry[$filterField] ?? null; 55 | 56 | if ($value !== $filterValue) { 57 | unset($entries[$entryId]); 58 | } 59 | } 60 | } 61 | 62 | return $entries; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Stitcher/Page/Adapter/AdapterFactory.php: -------------------------------------------------------------------------------- 1 | setCollectionRule(); 16 | $this->setFilterRule(); 17 | $this->setPaginationRule(); 18 | 19 | $this->variableParser = $variableParser; 20 | } 21 | 22 | public static function make(VariableParser $variableParser): AdapterFactory 23 | { 24 | return new self($variableParser); 25 | } 26 | 27 | public function create($adapterType, $adapterConfiguration): ?Adapter 28 | { 29 | foreach ($this->getRules() as $rule) { 30 | $adapter = $rule($adapterType, $adapterConfiguration); 31 | 32 | if ($adapter) { 33 | return $adapter; 34 | } 35 | } 36 | 37 | return null; 38 | } 39 | 40 | private function setCollectionRule(): void 41 | { 42 | $this->setRule(CollectionAdapter::class, function (string $adapterType, array $adapterConfiguration) { 43 | if ($adapterType === 'collection') { 44 | return CollectionAdapter::make($adapterConfiguration, $this->variableParser); 45 | } 46 | 47 | return null; 48 | }); 49 | } 50 | 51 | private function setFilterRule(): void 52 | { 53 | $this->setRule(FilterAdapter::class, function (string $adapterType, array $adapterConfiguration) { 54 | if ($adapterType === 'filter') { 55 | return FilterAdapter::make($adapterConfiguration, $this->variableParser); 56 | } 57 | 58 | return null; 59 | }); 60 | } 61 | 62 | private function setPaginationRule(): void 63 | { 64 | $this->setRule(PaginationAdapter::class, function (string $adapterType, array $adapterConfiguration) { 65 | if ($adapterType === 'pagination') { 66 | return PaginationAdapter::make($adapterConfiguration, $this->variableParser); 67 | } 68 | 69 | return null; 70 | }); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Pageon/Stitcher/Command/Parse.php: -------------------------------------------------------------------------------- 1 | outputDirectory = rtrim($outputDirectory, '/'); 27 | $this->configurationFile = $configurationFile; 28 | $this->pageParser = $pageParser; 29 | $this->pageRenderer = $pageRenderer; 30 | } 31 | 32 | public static function make( 33 | string $outputDirectory, 34 | string $configurationFile, 35 | PageParser $pageParser, 36 | PageRenderer $pageRenderer 37 | ): Parse { 38 | return new self($outputDirectory, $configurationFile, $pageParser, $pageRenderer); 39 | } 40 | 41 | public function execute(): void 42 | { 43 | $parsedConfiguration = Yaml::parse(File::read($this->configurationFile)); 44 | 45 | $pages = $this->parsePageConfiguration($parsedConfiguration); 46 | 47 | $this->renderPages($pages); 48 | } 49 | 50 | protected function parsePageConfiguration($config): array 51 | { 52 | $pages = []; 53 | 54 | foreach ($config as $pageId => $pageConfiguration) { 55 | $pageConfiguration['id'] = $pageConfiguration['id'] ?? $pageId; 56 | 57 | $pages += $this->pageParser->parse($pageConfiguration)->toArray(); 58 | } 59 | 60 | return $pages; 61 | } 62 | 63 | protected function renderPages($pages): void 64 | { 65 | $fs = new Filesystem(); 66 | 67 | /** 68 | * @var string $pageId 69 | * @var Page $page 70 | */ 71 | foreach ($pages as $pageId => $page) { 72 | $fileName = $pageId === '/' ? 'index' : $pageId; 73 | $renderedPage = $this->pageRenderer->render($page); 74 | 75 | $fs->dumpFile("{$this->outputDirectory}/{$fileName}.html", $renderedPage); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tests/CreateStitcherObjects.php: -------------------------------------------------------------------------------- 1 | createVariableParser(File::path()); 30 | 31 | return PageParser::make( 32 | PageFactory::make($variableParser), 33 | AdapterFactory::make($variableParser) 34 | ); 35 | } 36 | 37 | protected function createVariableParser(string $sourceDirectory = null) : VariableParser 38 | { 39 | return VariableParser::make( 40 | VariableFactory::make() 41 | ->setMarkdownParser(new Parsedown()) 42 | ->setYamlParser(new Yaml()) 43 | ->setImageParser($this->createImageFactory($sourceDirectory)) 44 | ); 45 | } 46 | 47 | protected function createPageFactory(VariableParser $variableParser) : PageFactory 48 | { 49 | return PageFactory::make($variableParser); 50 | } 51 | 52 | protected function createAdapterFactory(VariableParser $variableParser) : AdapterFactory 53 | { 54 | return AdapterFactory::make($variableParser); 55 | } 56 | 57 | protected function createImageFactory($sourceDirectory = null): ImageFactory 58 | { 59 | $sourceDirectory = $sourceDirectory ?? __DIR__ . '/'; 60 | $publicPath = File::path('public'); 61 | 62 | return ImageFactory::make($sourceDirectory, $publicPath, FixedWidthScaler::make([ 63 | 300, 500, 64 | ])); 65 | } 66 | 67 | protected function createVariableFactory(VariableParser $variableParser = null) : VariableFactory 68 | { 69 | $variableParser = $variableParser ?? $this->createVariableParser(); 70 | 71 | $factory = VariableFactory::make() 72 | ->setVariableParser($variableParser) 73 | ->setMarkdownParser(new Parsedown()) 74 | ->setYamlParser(new Yaml()) 75 | ->setImageParser($this->createImageFactory()); 76 | 77 | return $factory; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Pageon/Html/Image/ImageFactory.php: -------------------------------------------------------------------------------- 1 | sourceDirectory = rtrim($sourceDirectory, '/'); 22 | $this->publicDirectory = rtrim($publicDirectory, '/'); 23 | $this->scaler = $scaler; 24 | $this->imageManager = new ImageManager([ 25 | 'driver' => 'gd', 26 | ]); 27 | } 28 | 29 | public static function make( 30 | string $sourceDirectory, 31 | string $publicDirectory, 32 | Scaler $scaler 33 | ): ImageFactory 34 | { 35 | return new self($sourceDirectory, $publicDirectory, $scaler); 36 | } 37 | 38 | public function create($src): Image 39 | { 40 | $srcPath = ltrim($src, '/'); 41 | $image = Image::make($srcPath); 42 | 43 | $this->copySourceImageToDestination($srcPath); 44 | $scaleableImage = $this->imageManager->make("{$this->publicDirectory}/{$srcPath}"); 45 | 46 | $variations = $this->scaler->getVariations($scaleableImage); 47 | $image->addSrcset($image->src(), $scaleableImage->getWidth()); 48 | 49 | foreach ($variations as $width => $height) { 50 | $this->createScaledImage($image, $width, $height, $scaleableImage); 51 | } 52 | 53 | return $image; 54 | } 55 | 56 | private function createScaledImage( 57 | Image $image, 58 | int $width, 59 | int $height, 60 | ScaleableImage $scaleableImage 61 | ) { 62 | $scaleableImageClone = clone $scaleableImage; 63 | $scaledFileName = $this->createScaledFileName($image, $width, $height); 64 | 65 | $scaleableImageClone 66 | ->resize($width, $height) 67 | ->save("{$this->publicDirectory}/{$scaledFileName}"); 68 | 69 | $image->addSrcset($scaledFileName, $width); 70 | } 71 | 72 | private function createScaledFileName(Image $image, int $width, int $height): string 73 | { 74 | $srcPath = ltrim($image->src(), '/'); 75 | $extension = pathinfo($srcPath, PATHINFO_EXTENSION); 76 | 77 | return str_replace(".{$extension}", "-{$width}x{$height}.{$extension}", $srcPath); 78 | } 79 | 80 | private function copySourceImageToDestination(string $srcPath): void 81 | { 82 | $fs = new Filesystem(); 83 | $fs->copy("{$this->sourceDirectory}/{$srcPath}", "{$this->publicDirectory}/{$srcPath}"); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/Stitcher/Page/PageParserTest.php: -------------------------------------------------------------------------------- 1 | createVariableParser(); 17 | $parser = PageParser::make($this->createPageFactory($variableParser), $this->createAdapterFactory($variableParser)); 18 | 19 | $page = $parser->parse([ 20 | 'id' => '/', 21 | 'template' => 'index.twig', 22 | ])->first(); 23 | 24 | $this->assertInstanceOf(Page::class, $page); 25 | } 26 | 27 | /** @test */ 28 | public function it_can_parse_variables() 29 | { 30 | $markdownPath = File::path('test.md'); 31 | File::write($markdownPath, <<createVariableParser(); 37 | $parser = PageParser::make($this->createPageFactory($variableParser), $this->createAdapterFactory($variableParser)); 38 | $page = $parser->parse([ 39 | 'id' => '/', 40 | 'template' => 'index.twig', 41 | 'variables' => [ 42 | 'title' => 'Test', 43 | 'body' => 'test.md', 44 | ], 45 | ])->first(); 46 | 47 | $this->assertEquals('Test', $page->variable('title')); 48 | $this->assertEquals('

Hello world

', $page->variable('body')); 49 | } 50 | 51 | /** @test */ 52 | public function it_can_parse_a_collection_of_pages() 53 | { 54 | File::write('entries.yaml', <<createVariableParser(); 63 | $parser = PageParser::make( 64 | $this->createPageFactory($variableParser), 65 | $this->createAdapterFactory($variableParser) 66 | ); 67 | 68 | $result = $parser->parse([ 69 | 'id' => '/{id}', 70 | 'template' => 'index.twig', 71 | 'variables' => [ 72 | 'entry' => 'entries.yaml', 73 | ], 74 | 'config' => [ 75 | 'collection' => [ 76 | 'variable' => 'entry', 77 | 'parameter' => 'id', 78 | ], 79 | ], 80 | ]); 81 | 82 | $this->assertArrayHasKey('/a', $result); 83 | $this->assertArrayHasKey('/b', $result); 84 | 85 | $pageA = $result['/a']; 86 | $this->assertEquals('A', $pageA->variable('entry')['name']); 87 | 88 | $pageB = $result['/b']; 89 | $this->assertEquals('B', $pageB->variable('entry')['name']); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/Stitcher/Page/Adapter/PaginationAdapterTest.php: -------------------------------------------------------------------------------- 1 | createPageConfiguration(); 17 | 18 | $adapter = PaginationAdapter::make($pageConfiguration['config']['pagination'], $this->createVariableParser()); 19 | $result = $adapter->transform($pageConfiguration); 20 | 21 | $this->assertCount(3, $result); 22 | 23 | $this->assertCount(1, $result['/page-1']['variables']['entries']); 24 | $this->assertEquals('A', $result['/page-1']['variables']['entries']['a']['name']); 25 | 26 | $this->assertCount(1, $result['/page-2']['variables']['entries']); 27 | $this->assertEquals('B', $result['/page-2']['variables']['entries']['b']['name']); 28 | 29 | $this->assertCount(1, $result['/page-3']['variables']['entries']); 30 | $this->assertEquals('C', $result['/page-3']['variables']['entries']['c']['name']); 31 | } 32 | 33 | /** @test */ 34 | public function it_sets_the_pagination_variable() 35 | { 36 | $pageConfiguration = $this->createPageConfiguration(); 37 | 38 | $adapter = PaginationAdapter::make($pageConfiguration['config']['pagination'], $this->createVariableParser()); 39 | $result = $adapter->transform($pageConfiguration); 40 | 41 | $page1 = $result['/page-1']; 42 | $this->assertTrue(isset($page1['variables']['_pagination'])); 43 | $this->assertEquals(2, $page1['variables']['_pagination']['next']['index']); 44 | $this->assertEquals('/page-2', $page1['variables']['_pagination']['next']['url']); 45 | $this->assertNull($page1['variables']['_pagination']['previous']); 46 | 47 | $page2 = $result['/page-2']; 48 | $this->assertTrue(isset($page2['variables']['_pagination'])); 49 | $this->assertEquals(1, $page2['variables']['_pagination']['previous']['index']); 50 | $this->assertEquals('/page-1', $page2['variables']['_pagination']['previous']['url']); 51 | $this->assertEquals(3, $page2['variables']['_pagination']['next']['index']); 52 | $this->assertEquals('/page-3', $page2['variables']['_pagination']['next']['url']); 53 | 54 | $page3 = $result['/page-3']; 55 | $this->assertTrue(isset($page3['variables']['_pagination'])); 56 | $this->assertEquals(2, $page3['variables']['_pagination']['previous']['index']); 57 | $this->assertEquals('/page-2', $page3['variables']['_pagination']['previous']['url']); 58 | $this->assertNull($page3['variables']['_pagination']['next']); 59 | } 60 | 61 | private function createPageConfiguration(): array 62 | { 63 | File::write('entries.yaml', << '/', 79 | 'template' => 'index.twig', 80 | 'variables' => [ 81 | 'entries' => 'entries.yaml', 82 | ], 83 | 'config' => [ 84 | 'pagination' => [ 85 | 'variable' => 'entries', 86 | 'perPage' => 1, 87 | ], 88 | ], 89 | ]; 90 | 91 | return $pageConfiguration; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tests/Pageon/Integration/FullSiteParseTest.php: -------------------------------------------------------------------------------- 1 | createAllTemplates(); 22 | $this->createSiteConfiguration($configurationFile); 23 | $this->createDataFile(); 24 | $this->createImageFiles(); 25 | 26 | $command = Parse::make( 27 | File::path('public'), 28 | $configurationFile, 29 | $this->createPageParser(), 30 | $this->createPageRenderer() 31 | ); 32 | 33 | $command->execute(); 34 | 35 | $this->assertIndexPageParsed(); 36 | $this->assertOverviewPageParsed(); 37 | $this->assertOverviewPaginatedPageParsed(); 38 | $this->assertDetailPageParsed(); 39 | $this->assertImageParsed(); 40 | } 41 | 42 | private function assertIndexPageParsed(): void 43 | { 44 | $html = File::read('public/index.html'); 45 | 46 | $this->assertNotNull($html); 47 | $this->assertContains('', $html); 48 | } 49 | 50 | private function assertOverviewPageParsed(): void 51 | { 52 | $html = File::read('public/entries.html'); 53 | 54 | $this->assertNotNull($html); 55 | $this->assertContains('

A

', $html); 56 | $this->assertContains('

B

', $html); 57 | $this->assertContains('

C

', $html); 58 | } 59 | 60 | private function assertOverviewPaginatedPageParsed(): void 61 | { 62 | $page1 = File::read('public/entries-paginated/page-1.html'); 63 | $this->assertNotNull($page1); 64 | $this->assertContains('

A

', $page1); 65 | $this->assertContains('

B

', $page1); 66 | $this->assertNotContains('

C

', $page1); 67 | $this->assertNotContains('assertContains('assertContains('assertNotNull($page2); 73 | $this->assertContains('

C

', $page2); 74 | $this->assertContains('
assertContains('assertContains('assertContains('assertNotNull($page3); 81 | } 82 | 83 | private function assertDetailPageParsed(): void 84 | { 85 | $detail = File::read('public/entries/a.html'); 86 | $this->assertNotNull($detail); 87 | $this->assertContains('

A

', $detail); 88 | } 89 | 90 | private function assertImageParsed() 91 | { 92 | $detail = File::read('public/entries/a.html'); 93 | 94 | $this->assertContains('assertContains('srcset="/images/green.jpg 250w', $detail); 96 | $this->assertContains('alt="test"', $detail); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Stitcher/Page/Adapter/CollectionAdapter.php: -------------------------------------------------------------------------------- 1 | isValid($adapterConfiguration)) { 19 | throw InvalidConfiguration::invalidAdapterConfiguration('collection', '`variable` and `parameter`'); 20 | } 21 | 22 | $this->variable = $adapterConfiguration['variable']; 23 | $this->parameter = $adapterConfiguration['parameter']; 24 | $this->variableParser = $variableParser; 25 | } 26 | 27 | public static function make(array $adapterConfiguration, VariableParser $variableParser) 28 | { 29 | return new self($adapterConfiguration, $variableParser); 30 | } 31 | 32 | public function transform(array $pageConfiguration): array 33 | { 34 | $entries = $this->getEntries($pageConfiguration); 35 | $collectionPageConfiguration = []; 36 | 37 | foreach ($entries as $entryId => $entry) { 38 | $entryConfiguration = $this->createEntryConfiguration($pageConfiguration, $entryId, $entry); 39 | 40 | $collectionPageConfiguration[$entryConfiguration['id']] = $entryConfiguration; 41 | } 42 | 43 | return $collectionPageConfiguration; 44 | } 45 | 46 | public function isValid($subject): bool 47 | { 48 | return is_array($subject) && isset($subject['variable']) && isset($subject['parameter']); 49 | } 50 | 51 | protected function getEntries($pageConfiguration): array 52 | { 53 | $variable = $pageConfiguration['variables'][$this->variable] ?? null; 54 | 55 | $entries = $this->variableParser->parse($variable) 56 | ?? $variable; 57 | 58 | return $entries; 59 | } 60 | 61 | protected function createEntryConfiguration(array $pageConfiguration, $entryId, $entry): array 62 | { 63 | $entryConfiguration = $pageConfiguration; 64 | $parsedEntryId = str_replace('{' . $this->parameter . '}', $entryId, $pageConfiguration['id']); 65 | $entryConfiguration['id'] = $parsedEntryId; 66 | $entryConfiguration['variables'][$this->variable] = $entry; 67 | $entryConfiguration['variables']['meta'] = array_merge( 68 | $entryConfiguration['variables']['meta'] ?? [], 69 | $this->createMetaVariable($entryConfiguration) 70 | ); 71 | 72 | unset($entryConfiguration['config']['collection']); 73 | 74 | return $entryConfiguration; 75 | } 76 | 77 | protected function createMetaVariable(array $entryConfiguration): array 78 | { 79 | $meta = []; 80 | 81 | if ($title = $this->getTitleMeta($entryConfiguration)) { 82 | $meta['title'] = $title; 83 | } 84 | 85 | if ($description = $this->getDescriptionMeta($entryConfiguration)) { 86 | $meta['description'] = $description; 87 | } 88 | 89 | return $meta; 90 | } 91 | 92 | protected function getTitleMeta(array $entryConfiguration): ?string 93 | { 94 | $title = $entryConfiguration['variables'][$this->variable]['meta']['title'] 95 | ?? $entryConfiguration['variables'][$this->variable]['title'] 96 | ?? null; 97 | 98 | return $title; 99 | } 100 | 101 | protected function getDescriptionMeta(array $entryConfiguration): ?string 102 | { 103 | $description = $entryConfiguration['variables'][$this->variable]['meta']['description'] 104 | ?? $entryConfiguration['variables'][$this->variable]['description'] 105 | ?? null; 106 | 107 | return $description; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Stitcher/Variable/VariableFactory.php: -------------------------------------------------------------------------------- 1 | setJsonRule(); 20 | $this->setYamlRule(); 21 | $this->setMarkdownRule(); 22 | $this->setImageRule(); 23 | } 24 | 25 | public static function make(): VariableFactory 26 | { 27 | return new self(); 28 | } 29 | 30 | public function setYamlParser(Yaml $yamlParser): VariableFactory 31 | { 32 | $this->yamlParser = $yamlParser; 33 | 34 | return $this; 35 | } 36 | 37 | public function setMarkdownParser(Parsedown $markdownParser): VariableFactory 38 | { 39 | $this->markdownParser = $markdownParser; 40 | 41 | return $this; 42 | } 43 | 44 | public function setImageParser(ImageFactory $imageParser): VariableFactory 45 | { 46 | $this->imageParser = $imageParser; 47 | 48 | return $this; 49 | } 50 | 51 | public function setVariableParser(VariableParser $variableParser): VariableFactory 52 | { 53 | $this->variableParser = $variableParser; 54 | 55 | return $this; 56 | } 57 | 58 | public function create($value): AbstractVariable 59 | { 60 | foreach ($this->getRules() as $rule) { 61 | try { 62 | $variable = $rule($value); 63 | } catch (\TypeError $e) { 64 | continue; 65 | } 66 | 67 | if ($variable instanceof AbstractVariable) { 68 | return $variable; 69 | } 70 | } 71 | 72 | return DefaultVariable::make($value); 73 | } 74 | 75 | private function setJsonRule(): DynamicFactory 76 | { 77 | return $this->setRule(JsonVariable::class, function (string $value) { 78 | if (is_string($value) && pathinfo($value, PATHINFO_EXTENSION) === 'json') { 79 | return JsonVariable::make($value); 80 | } 81 | 82 | return null; 83 | }); 84 | } 85 | 86 | private function setYamlRule(): void 87 | { 88 | $this->setRule(YamlVariable::class, function (string $value) { 89 | if (! $this->yamlParser) { 90 | return null; 91 | } 92 | 93 | $extension = pathinfo($value, PATHINFO_EXTENSION); 94 | 95 | if (in_array($extension, ['yaml', 'yml'])) { 96 | return YamlVariable::make($value, $this->yamlParser, $this->variableParser); 97 | } 98 | 99 | return null; 100 | }); 101 | } 102 | 103 | private function setMarkdownRule(): void 104 | { 105 | $this->setRule(MarkdownVariable::class, function (string $value) { 106 | if ($this->markdownParser && pathinfo($value, PATHINFO_EXTENSION) === 'md') { 107 | return MarkdownVariable::make($value, $this->markdownParser); 108 | } 109 | 110 | return null; 111 | }); 112 | } 113 | 114 | private function setImageRule(): void 115 | { 116 | $this->setRule(ImageVariable::class, function ($value) { 117 | if (! $this->imageParser) { 118 | return null; 119 | } 120 | 121 | $srcPath = is_array($value) ? $value['src'] ?? null : $value; 122 | 123 | $extension = pathinfo($srcPath, PATHINFO_EXTENSION); 124 | 125 | // TODO: Let ImageVariable take a config array, and let it parse that array itself. 126 | 127 | if (in_array($extension, ['jpeg', 'jpg', 'png', 'gif'])) { 128 | return ImageVariable::make($srcPath, $this->imageParser, $value['alt'] ?? null); 129 | } 130 | 131 | return null; 132 | }); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/Pageon/Html/Meta/Meta.php: -------------------------------------------------------------------------------- 1 | charset($charset); 24 | $this->name('viewport', 'width=device-width, initial-scale=1'); 25 | 26 | $this->socialMeta = [ 27 | new GooglePlusMeta($this), 28 | new TwitterMeta($this), 29 | new OpenGraphMeta($this), 30 | ]; 31 | } 32 | 33 | public static function create(string $charset = 'UTF-8') : Meta { 34 | return new self($charset); 35 | } 36 | 37 | public function render() : string { 38 | $html = ''; 39 | 40 | /** 41 | * @var string $type 42 | * @var MetaItem[] $metaItems 43 | */ 44 | foreach ($this->meta as $type => $metaItems) { 45 | foreach ($metaItems as $metaItem) { 46 | $html .= $metaItem->render() . "\n"; 47 | } 48 | } 49 | 50 | return $html; 51 | } 52 | 53 | public function charset(string $charset) : Meta { 54 | $item = CharsetMeta::create($charset); 55 | $this->meta['charset'][] = $item; 56 | 57 | return $this; 58 | } 59 | 60 | public function name(string $name, ?string $content) : Meta { 61 | if (!$content) { 62 | return $this; 63 | } 64 | 65 | $item = NameMeta::create($name, $content); 66 | $this->meta['name'][$name] = $item; 67 | 68 | return $this; 69 | } 70 | 71 | public function itemprop(string $name, ?string $content) : Meta { 72 | if (!$content) { 73 | return $this; 74 | } 75 | 76 | $item = ItemPropMeta::create($name, $content); 77 | $this->meta['itemprop'][$name] = $item; 78 | 79 | return $this; 80 | } 81 | 82 | public function property(string $property, ?string $content) : Meta { 83 | if (!$content) { 84 | return $this; 85 | } 86 | 87 | $item = PropertyMeta::create($property, $content); 88 | $this->meta['property'][$property] = $item; 89 | 90 | return $this; 91 | } 92 | 93 | public function httpEquiv(string $httpEquiv, ?string $content) : Meta { 94 | if (!$content) { 95 | return $this; 96 | } 97 | 98 | $item = HttpEquivMeta::create($httpEquiv, $content); 99 | $this->meta['httpEquiv'][$httpEquiv] = $item; 100 | 101 | return $this; 102 | } 103 | 104 | public function link(string $rel, ?string $href) : Meta { 105 | if (!$href) { 106 | return $this; 107 | } 108 | 109 | $item = LinkMeta::create($rel, $href); 110 | $this->meta['link'][$rel] = $item; 111 | 112 | return $this; 113 | } 114 | 115 | public function title(?string $content) : Meta { 116 | if (!$content) { 117 | return $this; 118 | } 119 | 120 | $this->name('title', $content); 121 | 122 | foreach ($this->socialMeta as $socialMeta) { 123 | $socialMeta->title($content); 124 | } 125 | 126 | return $this; 127 | } 128 | 129 | public function description(?string $content) : Meta { 130 | if (!$content) { 131 | return $this; 132 | } 133 | 134 | $this->name('description', $content); 135 | 136 | foreach ($this->socialMeta as $socialMeta) { 137 | $socialMeta->description($content); 138 | } 139 | 140 | return $this; 141 | } 142 | 143 | public function image(?string $content) : Meta { 144 | if (!$content) { 145 | return $this; 146 | } 147 | 148 | $this->name('image', $content); 149 | 150 | foreach ($this->socialMeta as $socialMeta) { 151 | $socialMeta->image($content); 152 | } 153 | 154 | return $this; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/Stitcher/Page/Adapter/PaginationAdapter.php: -------------------------------------------------------------------------------- 1 | isValid($adapterConfiguration)) { 19 | throw InvalidConfiguration::invalidAdapterConfiguration('pagination', '`variable`, `perPage`'); 20 | } 21 | 22 | $this->variable = $adapterConfiguration['variable']; 23 | $this->perPage = $adapterConfiguration['perPage'] ?? 12; 24 | $this->variableParser = $variableParser; 25 | } 26 | 27 | public static function make(array $adapterConfiguration, VariableParser $variableParser): PaginationAdapter 28 | { 29 | return new self($adapterConfiguration, $variableParser); 30 | } 31 | 32 | public function transform(array $pageConfiguration): array 33 | { 34 | $paginationPageConfiguration = []; 35 | $entries = $this->getEntries($pageConfiguration); 36 | $pageCount = (int) ceil(count($entries) / $this->perPage); 37 | 38 | for ($pageIndex = 1; $pageIndex <= $pageCount; $pageIndex++) { 39 | $entriesForPage = array_splice($entries, 0, $this->perPage); 40 | 41 | $entryConfiguration = $this->createPageConfiguration( 42 | $pageConfiguration, 43 | $entriesForPage, 44 | $pageIndex, 45 | $pageCount 46 | ); 47 | 48 | $paginationPageConfiguration[$entryConfiguration['id']] = $entryConfiguration; 49 | } 50 | 51 | return $paginationPageConfiguration; 52 | } 53 | 54 | public function isValid($subject): bool 55 | { 56 | return is_array($subject) && isset($subject['variable']); 57 | } 58 | 59 | protected function getEntries(array $pageConfiguration): ?array 60 | { 61 | $variable = $pageConfiguration['variables'][$this->variable] ?? null; 62 | $entries = $this->variableParser->parse($variable)['entries'] 63 | ?? $this->variableParser->parse($variable) 64 | ?? $variable; 65 | 66 | return $entries; 67 | } 68 | 69 | protected function createPageConfiguration( 70 | array $entryConfiguration, 71 | array $entriesForPage, 72 | int $pageIndex, 73 | int $pageCount 74 | ): array { 75 | $pageId = rtrim($entryConfiguration['id'], '/'); 76 | $paginatedId = "{$pageId}/page-{$pageIndex}"; 77 | 78 | $entryConfiguration['id'] = $paginatedId; 79 | $entryConfiguration['variables'][$this->variable] = $entriesForPage; 80 | 81 | $paginationVariable = $this->createPaginationVariable($pageId, $pageIndex, $pageCount); 82 | $entryConfiguration['variables']['_pagination'] = $paginationVariable; 83 | 84 | unset($entryConfiguration['config']['pagination']); 85 | 86 | return $entryConfiguration; 87 | } 88 | 89 | protected function createPaginationVariable(string $pageId, int $pageIndex, int $pageCount): array 90 | { 91 | return [ 92 | 'current' => $pageIndex, 93 | 'previous' => $this->createPreviousPagination($pageId, $pageIndex), 94 | 'next' => $this->createNextPagination($pageId, $pageIndex, $pageCount), 95 | 'pages' => $pageCount, 96 | ]; 97 | } 98 | 99 | protected function createPreviousPagination(string $pageId, int $pageIndex): ?array 100 | { 101 | if ($pageIndex <= 1) { 102 | return null; 103 | } 104 | 105 | $previous = $pageIndex - 1; 106 | 107 | return [ 108 | 'url' => "{$pageId}/page-{$previous}", 109 | 'index' => $previous, 110 | ]; 111 | } 112 | 113 | protected function createNextPagination(string $pageId, int $pageIndex, int $pageCount): ?array 114 | { 115 | if ($pageIndex >= $pageCount) { 116 | return null; 117 | } 118 | 119 | $next = $pageIndex + 1; 120 | 121 | return [ 122 | 'url' => "{$pageId}/page-{$next}", 123 | 'index' => $next, 124 | ]; 125 | } 126 | } 127 | --------------------------------------------------------------------------------