Passed
Push — master ( e37f2c...989b30 )
by Caen
03:20 queued 12s
created

VirtualPage::__call()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
c 2
b 0
f 0
nc 3
nop 2
dl 0
loc 15
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Pages;
6
7
use BadMethodCallException;
8
use Closure;
9
use Hyde\Framework\Actions\AnonymousViewCompiler;
10
use Hyde\Markdown\Models\FrontMatter;
11
use Hyde\Pages\Concerns\HydePage;
12
use Hyde\Pages\Contracts\DynamicPage;
13
use Illuminate\Support\Facades\View;
14
15
/**
16
 * A virtual page is a page that does not have a source file.
17
 *
18
 * @experimental This feature is experimental and may change substantially before the 1.0.0 release.
19
 *
20
 * This can be useful for creating pagination pages and the like.
21
 * When used in a package, it's on the package developer to ensure
22
 * that the virtual page is registered with Hyde, usually within the
23
 * boot method of the package's service provider so it can be compiled.
24
 *
25
 * This class is especially useful for one-off pages, but if your usage grows,
26
 * you may benefit from creating a custom page class instead to get full control.
27
 */
28
class VirtualPage extends HydePage implements DynamicPage
29
{
30
    public static string $sourceDirectory = '';
31
    public static string $outputDirectory = '';
32
    public static string $fileExtension = '';
33
34
    protected string $contents;
35
    protected string $view;
36
37
    /** @var array<string, callable> */
38
    protected array $macros = [];
39
40
    /**
41
     * Static alias for the constructor.
42
     */
43
    public static function make(string $identifier = '', FrontMatter|array $matter = [], string $contents = '', string $view = ''): static
44
    {
45
        return new static($identifier, $matter, $contents, $view);
46
    }
47
48
    /**
49
     * Create a new virtual page instance.
50
     *
51
     * The virtual page class offers two content options. You can either pass a string to the $contents parameter,
52
     * Hyde will then save that literally as the page's contents. Alternatively, you can pass a view name to the $view parameter,
53
     * and Hyde will use that view to render the page contents with the supplied front matter during the static site build process.
54
     *
55
     * Note that $contents take precedence over $view, so if you pass both, only $contents will be used.
56
     * You can also register a macro with the name 'compile' to overload the default compile method.
57
     *
58
     * @param  string  $identifier  The identifier of the page. This is used to generate the route key which is used to create the output filename.
59
     *                              If the identifier for a virtual page is "foo/bar" the page will be saved to "_site/foo/bar.html".
60
     * @param  \Hyde\Markdown\Models\FrontMatter|array  $matter  The front matter of the page. When using the Blade view rendering option,
61
     *                                                           this will be passed to the view.
62
     * @param  string  $contents  The contents of the page. This will be saved as-is to the output file.
63
     * @param  string  $view  The view key for the view to use to render the page contents.
64
     */
65
    public function __construct(string $identifier, FrontMatter|array $matter = [], string $contents = '', string $view = '')
66
    {
67
        parent::__construct($identifier, $matter);
68
69
        $this->contents = $contents;
70
        $this->view = $view;
71
    }
72
73
    public function getContents(): string
74
    {
75
        return $this->contents;
76
    }
77
78
    public function getBladeView(): string
79
    {
80
        return $this->view;
81
    }
82
83
    /**
84
     * Get the contents that will be saved to disk for this page.
85
     */
86
    public function compile(): string
87
    {
88
        if (isset($this->macros['compile'])) {
89
            return $this->__call('compile', []);
90
        }
91
92
        if (! $this->contents && $this->view) {
93
            if (str_ends_with($this->view, '.blade.php')) {
94
                return AnonymousViewCompiler::call($this->view, $this->matter->toArray());
95
            }
96
97
            return View::make($this->getBladeView(), $this->matter->toArray())->render();
98
        }
99
100
        return $this->getContents();
101
    }
102
103
    /**
104
     * Register a macro for the instance.
105
     */
106
    public function macro(string $name, callable $macro): void
107
    {
108
        $this->macros[$name] = $macro;
109
    }
110
111
    /**
112
     * Dynamically handle calls to the class.
113
     */
114
    public function __call(string $method, array $parameters): mixed
115
    {
116
        if (! isset($this->macros[$method])) {
117
            throw new BadMethodCallException(sprintf(
118
                'Method %s::%s does not exist.', static::class, $method
119
            ));
120
        }
121
122
        $macro = $this->macros[$method];
123
124
        if ($macro instanceof Closure) {
125
            $macro = $macro->bindTo($this, static::class);
126
        }
127
128
        return $macro(...$parameters);
129
    }
130
}
131