Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build out a smarter View class. #89

Merged
merged 3 commits into from
Mar 15, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 180 additions & 10 deletions src/Utils/View.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,22 @@

namespace TheFrosty\WpUtilities\Utils;

use RuntimeException;
use function array_merge;
use function array_unshift;
use function count;
use function dirname;
use function extract;
use function file_exists;
use function get_object_vars;
use function is_array;
use function is_object;
use function ob_get_clean;
use function ob_start;
use function realpath;
use function sprintf;
use function str_replace;
use function trailingslashit;

/**
* Class View
Expand All @@ -14,27 +26,185 @@
final class View
{

/**
* List of paths to load views from.
* Internal loader selects the first path with file that exists.
* Paths that are loaded with addPath are prepended to the array.
* @var array $viewPaths
*/
protected array $viewPaths = [];

/**
* View data
* @var array $viewData
*/
private array $viewData = [];

/**
* View constructor.
*/
public function __construct()
{
$this->setDefaultPaths();
}

/**
* Return a view file.
* @param string $view The view file to render from the `views` directory.
* @return string|null
*/
public function get(string $view): ?string
{
$file = $this->sanitizeFileExtension($view . '.php');

return $this->getViewPath($file);
}

/**
* Render a view.
* @param string $filename The view file to render from the `views` directory.
* @return string
* @param array $viewData
*/
public function get(string $filename): string
public function render(string $filename, array $viewData = []): void
{
return sprintf('%1$s/views/%2$s', dirname(__DIR__, 2), $filename);
$this->load([$filename, $viewData]);

/*
* Clear view data, so we can use the same object multiple times,
* otherwise the view data will persist and may cause problems.
*/
$this->viewData = [];
}

/**
* Render a view file.
* Return a view.
* @param string $filename The view file to render from the `views` directory.
* @param array $args
* @param array $viewData
* @return string
*/
public function retrieve(string $filename, array $viewData = []): string
{
ob_start();
$this->render($filename, $viewData);

return ob_get_clean();
}

/**
* Set variables to be available in any view
* @param array|object $vars
*/
public function render(string $filename, array $args = []): void
public function setVars($vars = []): void
{
$filename = $this->get($filename);
if (file_exists($filename)) {
extract($args);
include $filename;
if (is_object($vars)) {
$vars = get_object_vars($vars);
}

if (is_array($vars) and count($vars) > 0) {
foreach ($vars as $key => $val) {
$this->viewData[$key] = $val;
}
}
}

/**
* Add View Path. Prepend the paths array with the new path
* @param string $path
*/
public function addPath(string $path): void
{
array_unshift($this->viewPaths, trailingslashit(realpath($path)));
}

/**
* Get view data.
* @return array
*/
public function getViewData(): array
{
return $this->viewData;
}

/**
* Internal view loader
* @param array<string, array> $args
* @throws RuntimeException
*/
private function load(array $args): void
{
$this->setDefaultPaths();
[$view, $data] = $args;

// Add a file extension the view
$file = $this->sanitizeFileExtension($view . '.php');

// Get the view path
$viewPath = $this->getViewPath($file);

// Display error if view not found
if ($viewPath === null) {
$this->viewNotFoundError($file);
}

if (is_array($data) && !empty($data)) {
$this->viewData = array_merge($this->viewData, $data);
}

extract($this->viewData);
include $viewPath;
}

/**
* Set the default paths.
* The default view directories always need to be loaded first
*/
private function setDefaultPaths(): void
{
if (empty($this->viewPaths)) {
$this->viewPaths = [sprintf('%1$s/views/', dirname(__DIR__, 2))];
}
}

/**
* Sanitize the file extension.
* @param string $file
* @return string
*/
private function sanitizeFileExtension(string $file): string
{
return str_replace('.php.php', '.php', $file);
}

/**
* Get the view path.
* @param string $file
* @return string|null
*/
private function getViewPath(string $file): ?string
{
$file = $this->sanitizeFileExtension($file);
foreach ($this->viewPaths as $viewDir) {
if (file_exists($viewDir . $file)) {
return $viewDir . $file;
}
}

return null;
}

/**
* Display error when no view found.
* @param string $file
* @return mixed
* @throws RuntimeException
*/
private function viewNotFoundError(string $file): void
{
$errText = PHP_EOL .
'View file "' . $file . '" not found.' . PHP_EOL .
'Directories checked: ' . PHP_EOL .
'[' . implode('],' . PHP_EOL . '[', $this->viewPaths) . ']' . PHP_EOL;

throw new RuntimeException($errText);
}
}