processwire / processwire-requests

ProcessWire feature requests.
39 stars 0 forks source link

Add ability to include a PHP file at the top of index.php #521

Open Toutouwai opened 7 months ago

Toutouwai commented 7 months ago

Short description of the enhancement

This is something that's been mentioned in a couple of existing requests (https://github.com/processwire/processwire-requests/issues/442, https://github.com/processwire/processwire-requests/issues/502) but I'd like to highlight the most generic version of the request in the issue title.

There are times where you want to execute some code before PW boots up or does anything at all. There are potentially many different reasons a developer might want to do this - here is one example: I have a custom cache system and I want to check for the existence of a cache file and where it exists return the contents of that file without PW needing to boot up at all. (Just to be clear, there's a bit more to it than that and so it can't be done via .htaccess as per ProCache).

Currently the only way to do this is to edit index.php, but this is a core file and changes made here are liable to be lost during a PW upgrade. So it would be great if PW supported this sort of thing by allowing for including a non-core file in index.php.

Perhaps the way to do this with the lowest overhead is to check for a file with a specific name and include it if it exists. After line 29 could be something like this:

$customInclude = $rootPath . '/index_include.php';
if(file_exists($customInclude)) include_once($customInclude);

A developer could possibly want to include more than one file with different naming and in different locations but they could handle those includes from index_include.php

BernhardBaumrock commented 7 months ago

Personally I have never needed this, but it might be nice to have at some point!

@ryancramerdesign if you add this, could you please also add support for config-local.php like shown here: https://github.com/processwire/processwire/pull/267 ?

I think both features have the same idea behind and therefore they should have a common naming convention:

index-local.php loads before index.php
config-local.php loads after config.php

Maybe filenames should reflect the before/after?

index-before.php loads before index.php
config-after.php loads after config.php

I don't really like config-after, but maybe that's just because I'm so used to config-local.php - but that would at least make sure that all my instances running with config-local.php would not break on an update...

ryancramerdesign commented 7 months ago

@Toutouwai I'd consider the main index.php file to be part of your site files, and likewise your .htaccess file (at least if you have made modifications to them). I modify my index.php file on many of my installations for one reason or another. For instance, I usually delete the composer stuff, since I don't use it. The index.php file has only changed twice in the lifetime of PW 3.x, and those changes were minor and not technically even required. I prefer not to add unnecessary file_exists() checks to index.php (or site/config.php) when these files are already easily customizable. The index file does do a check for /vendor/autoload.php, so I guess another option is you could always add code to that auto-include if needed?

BernhardBaumrock commented 7 months ago

@ryancramerdesign I understand that you want to avoid additional filesystem requests, which makes sense when those are more expensive on shared systems. Also it makes sense what you say that you consider index.php to be site-specific.

Though I think there is room for improvement and I have a suggestion.

At the moment the index.php looks like this:

<?php namespace ProcessWire;

/**
 * ProcessWire Bootstrap
 *
 * This file may be used to bootstrap either the http web accessible
 * version, or the command line client version of ProcessWire. 
 *
 * Note: if you happen to change any directory references in here, please
 * do so after you have installed the site, as the installer is not informed
 * of any changes made in this file. 
 * 
 * ProcessWire 3.x, Copyright 2020 by Ryan Cramer
 * https://processwire.com
 *
 * @version 3.0
 *
 * Index Versions
 * ==============
 * 300 Moved much of this file to a ProcessWire::buildConfig() method.
 * 252 Extract all fuel to local API vars when in external or cli mode.
 * 251 Add $config->debugIf option.
 * 250 PW 2.5 support.
 *
 */

if(!defined("PROCESSWIRE")) define("PROCESSWIRE", 300); // index version
$rootPath = __DIR__;
if(DIRECTORY_SEPARATOR != '/') $rootPath = str_replace(DIRECTORY_SEPARATOR, '/', $rootPath);
$composerAutoloader = $rootPath . '/vendor/autoload.php'; // composer autoloader
if(file_exists($composerAutoloader)) require_once($composerAutoloader);
if(!class_exists("ProcessWire\\ProcessWire", false)) require_once("$rootPath/wire/core/ProcessWire.php");
$config = ProcessWire::buildConfig($rootPath);

if(!$config->dbName) {
    // If ProcessWire is not installed, go to the installer
    if(is_file("./install.php") && strtolower($_SERVER['REQUEST_URI']) == strtolower($config->urls->root)) {
        require("./install.php");
        exit(0);
    } else {
        header("HTTP/1.1 404 Page Not Found");
        echo "404 page not found (no site configuration or install.php available)";
        exit(0);
    }
}

$process = null;
$wire = null;

try { 
    // Bootstrap ProcessWire's core and make the API available with $wire
    $wire = new ProcessWire($config);
    $process = $wire->modules->get('ProcessPageView'); /** @var ProcessPageView $process */
    $wire->wire('process', $process); 
    echo $process->execute($config->internal);
    $config->internal ? $process->finished() : extract($wire->wire('all')->getArray());

} catch(\Exception $e) {
    // Formulate error message and send to the error handler
    if($process) $process->failed($e);
    $wire ? $wire->trackException($e) : $config->trackException($e);
    $errorMessage = "Exception: " . $e->getMessage() . " (in " . $e->getFile() . " line " . $e->getLine() . ")";
    if($config->debug || ($wire && $wire->user && $wire->user->isSuperuser())) $errorMessage .= "\n\n" . $e->getTraceAsString();
    trigger_error($errorMessage, E_USER_ERROR);
}

This is something that shouts to me "do not touch me" even though I'm quite experienced.

What if the index.php file was changed to something like this:

<?php
// this is the main entry point for processwire
// you can adjust it to your needs if necessary

// optionally load the composer autoloader
// you can remove this if you are not using composer
require "wire/composer_autoload.php";

// load processwire
require "wire/index.php";

I think that would be a lot easier for everybody to understand, it should have no real overhead and it should also not break anything on existing installations, because existing index.php files would just work as before.

Customisations could then look like this:

<?php
require "index-before.php";
require "wire/index.php";

The only downside compared to Robin's suggestion is that this would not work out of the box for modules relying on some functionality before even booting PW.

adrianbj commented 7 months ago

Just a small comment to weigh in. I have started putting my composer vendor folder outside the webroot (for security reasons) and loading it from config.php, so the autoload in PW's index.php doesn't do anything. I see (https://github.com/processwire/processwire-requests/issues/191) already talks about this and Ryan mentions modifying index.php there.

Toutouwai commented 7 months ago

I'd consider the main index.php file to be part of your site files, and likewise your .htaccess file (at least if you have made modifications to them).

Thanks @ryancramerdesign, I see what you're saying. I do routinely use a modified .htaccess for new installations and never replace it during upgrades and I've noticed that the core checks that .htaccess is up-to-date with the PW version and warns you if it needs updating. This is great because it gives peace-of-mind that when the core does need a changed .htaccess users who are not routinely updating that file will be alerted.

I didn't realise that a similar check is being performed on index.php but I've just noticed SystemUpdaterChecks::checkIndexFile() so that gives me reassurance that it's safe to treat index.php in a similar way.

I do like @BernhardBaumrock's idea though so will keep the request open until you've had a chance to respond to that.