seacms-app/App.php
2023-03-19 13:21:37 +01:00

204 lines
7.4 KiB
PHP

<?php
/**
* SPDX-License-Identifier: EUPL-1.2
* Authors: see /README.md
*/
namespace SeaCMS;
use Exception;
use Pico;
use SeacmsAppPlugin;
use SeaCMS\Api\SpecialOutputException;
use SeaCMS\App\TestInterface;
use Throwable;
set_error_handler(function (
int $errno,
string $errstr,
string $errfile = '',
int $errline = -1
) {
if (!isset($GLOBALS['errorMessages'])){
$GLOBALS['errorMessages']= [];
}
$GLOBALS['errorMessages'][] = <<<HTML
<div>
Error : $errstr<br>
in file '$errfile' (line '$errline').<br/>
ErrCode : $errno
</div>
HTML;
return true;
});
class App
{
/**
* plugins path in vendor
* @var string
*/
public const PLUGINS_PATH = 'vendor/picocms/plugins/';
/**
* themes path in vendor
* @var string
*/
public const THEMES_PATH = 'vendor/picocms/themes/';
/**
* Pico instance.
* @var Pico
*/
protected $pico;
public function __construct(string $contentFolderFromRoot, ?TestInterface $testRunner = null)
{
// sanitize content folder
$cwd = getcwd();
if (empty($contentFolderFromRoot)){
$contentFolderFromRoot = 'content';
} else {
$contentFolderFromRoot = str_replace('\\','/',$contentFolderFromRoot);
}
if (!is_dir($cwd)){
throw new Exception("getcwd returned a path that is not a directory !");
}
if (!is_dir("$cwd/$contentFolderFromRoot")){
$contentFolderFromRoot = 'vendor/picocms/pico/content-sample';
}
if (substr($contentFolderFromRoot,-1) !== '/'){
$contentFolderFromRoot .= '/';
}
// instance Pico
$this->pico = new Pico(
$cwd, // root dir
$contentFolderFromRoot, // config dir
self::PLUGINS_PATH, // plugins dir
self::THEMES_PATH // themes dir
);
$this->pico->loadPlugin(new SeacmsAppPlugin($this->pico, $testRunner));
$this->update_SERVERIfNeeded($this->pico, $contentFolderFromRoot);
}
public function runPico(): string
{
return $this->pico->run();
}
/**
* instanciate Pico and run it then echo output
* @param string $contentFolderFromRoot where is the root folder
*/
public static function run(string $contentFolderFromRoot)
{
try {
$app = new App($contentFolderFromRoot);
$output = $app->runPico();
self::appendErrorMessagesIfNeeded($output);
} catch (SpecialOutputException $th) {
$output = $th->getJsonResponse()->send();
} catch (Throwable $th) {
$output = <<<HTML
<div style="color:red;">
Exception : {$th->__toString()}
</div>
HTML;
self::appendErrorMessagesIfNeeded($output);
} finally {
echo $output;
}
}
/**
* append error messages from $GLOBALS['errorMessages']
* only if $_GET['debug'] === 'yes'
* @param string &$output
*/
protected static function appendErrorMessagesIfNeeded(string &$output)
{
if (!empty($_GET['debug']) && $_GET['debug'] === 'yes' && !empty($GLOBALS['errorMessages'])){
$formattedMessages = implode("\n", $GLOBALS['errorMessages']);
$formattedMessages = <<<HTML
<div style="background-color:navajowhite;">
$formattedMessages
</div>
HTML;
if (preg_match('/<\/body>/i', $output, $match)) {
$output = str_replace($match[0], $formattedMessages.$match[0], $output);
} else {
$output = $output.$formattedMessages;
}
}
}
/**
* detect if rewrite mode then update $_SERVER in consequence
* @see Pico:evaluateRequestUrl()
*
* @param Pico $pico
* @param string $configDir
*/
protected function update_SERVERIfNeeded(Pico $pico, string $configDir)
{
$requestUrl = null;
$config = [
'rewrite_url' => false,
'themes_url' => self::THEMES_PATH,
'plugins_url' => self::PLUGINS_PATH,
];
$nbLevels = 0;
// use QUERY_STRING; e.g. /pico/?sub/page
$pathComponent = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
if ($pathComponent) {
$pathComponent = strstr($pathComponent, '&', true) ?: $pathComponent;
if (strpos($pathComponent, '=') === false) {
$requestUrl = trim(rawurldecode($pathComponent), '/');
}
}
if (isset($_SERVER)){
if (!empty($requestUrl)){
$supposedEndUrlForRewrite = $configDir.$requestUrl.'/index.php';
$supposedEndUrlForNotRewrite = $configDir.'index.php';
if (!empty($_SERVER['SCRIPT_NAME']) && substr($_SERVER['SCRIPT_NAME'],-strlen($supposedEndUrlForRewrite)) == $supposedEndUrlForRewrite){
$_SERVER['SCRIPT_NAME'] = str_replace($supposedEndUrlForRewrite,$configDir.'index.php',$_SERVER['SCRIPT_NAME']);
if (!empty($_SERVER['SCRIPT_FILENAME'])){
$_SERVER['SCRIPT_FILENAME'] = str_replace($supposedEndUrlForRewrite,$configDir.'index.php',$_SERVER['SCRIPT_FILENAME']);
}
if (!empty($_SERVER['REQUEST_URI'])){
$_SERVER['REQUEST_URI'] = str_replace([$configDir.$requestUrl.'/',$configDir.$requestUrl],$configDir,$_SERVER['REQUEST_URI']);
}
$config['rewrite_url'] = true;
$nbLevels = count(explode('/',$configDir.$requestUrl));
} elseif (!empty($_SERVER['SCRIPT_NAME']) && substr($_SERVER['SCRIPT_NAME'],-strlen($supposedEndUrlForNotRewrite)) == $supposedEndUrlForNotRewrite) {
$nbLevels = count(explode('/',$configDir)) -1;
}
} elseif (!empty($_SERVER['SCRIPT_NAME']) && is_string($_SERVER['SCRIPT_NAME'])) {
$matches = [];
$configDirForMatch = preg_quote($configDir,'/');
if (preg_match("/^(.*)$configDirForMatch(.*)index.php$/",$_SERVER['SCRIPT_NAME'],$matches)){
$rootPath = $matches[1];
$requestUrl = !empty($matches[2]) ? (substr($matches[2],-1) == '/' ? substr($matches[2],0,-1) : $matches[2]) : '';
if (!empty($requestUrl)){
$supposedEndUrlForRewrite = $configDir.$requestUrl.'/index.php';
if (!empty($_SERVER['SCRIPT_FILENAME'])){
$_SERVER['SCRIPT_FILENAME'] = str_replace($supposedEndUrlForRewrite,$configDir.'index.php',$_SERVER['SCRIPT_FILENAME']);
}
if (!empty($_SERVER['SCRIPT_NAME'])){
$_SERVER['SCRIPT_NAME'] = str_replace($supposedEndUrlForRewrite,$configDir.'index.php',$_SERVER['SCRIPT_NAME']);
}
}
$nbLevels = count(explode('/',$configDir)) -1;
$config['rewrite_url'] = true;
}
}
}
$previous = implode('',array_fill(0,$nbLevels,'../'));
$config['themes_url'] = $previous.$config['themes_url'];
$config['plugins_url'] = $previous.$config['plugins_url'];
$pico->setConfig($config);
}
}