PhpRenderer and basePath

I’ve got \Slim\Views\PhpRenderer working as expected and most of Odan’s PhpViewExtensionMiddleware (from here) working too. The problem is that, from within the middleware, $this->app->getBasePath() is returning an empty string. In other contexts, $app->getBasePath(); returns the same.

The file structure is standard, with the web root set to /var/www/slim4/public/ and all other “application” folders at the same level as public.

The output of Odan’s check.php looks like this:

System ....................... Linux
Webserver .................... Apache/2.4.38 (Debian)
PHP-Version .................. ✓ 8.0.8
PHP-SAPI ..................... fpm-fcgi
Apache mod_cgi ............... Not found
Current directory: public/ ... ✓ OK
File: public/index.php ....... ✓ Found
File: public/.htaccess ....... ✗ Not found
File: public/../.htaccess .... ✗ Not found
URL: /public ................. ✓ Valid
Done

The .htaccess files are missing because they are not implemented. The appropriate directives are in the Apache site config file instead. The site works except that I cannot obtain a base path.

I have tried BasePathMiddleware and hard coding a basePath with $app->setBasePath('/various/absolute/or/relative/paths/'); with and without the BasePathMiddleware, all without success.

Any insight appreciated?

Hi @dafriend

I would try to change the DocumentRoot first to the public/ directory, e.g.:

DocumentRoot /var/www/slim4/public

To proxy all .php files in your vhost to the fcgi server using their real php file locations, you can try to change a more flexible match:

ProxyPassMatch ^/(.*\.php)$ fcgi://127.0.0.1:9000/var/www/slim4/public/$1

Thanks @odan for the suggestions.

The DocumentRoot is (and was) set as you describe - /var/www/slim4/public/

This setup is a developer’s workstation and Apache resides on that workstation. No particular proxy has been configured for it.

Adding the suggested ProxyPassMatch directive results in Apache returning a 500 Internal Server Error. So I changed the SAPI setup to apache2handler and running check.php now gives me the following.

System ....................... Linux
Webserver .................... Apache/2.4.38 (Debian)
PHP-Version .................. ✓ 8.0.8
PHP-SAPI ..................... apache2handler
Apache mod_rewrite ........... ✓ Enabled
Apache mod_cgi ............... ✓ Enabled
Current directory: public/ ... ✓ OK
File: public/index.php ....... ✓ Found
File: public/.htaccess ....... ✗ Not found
File: public/../.htaccess .... ✗ Not found
URL: /public ................. ✓ Valid
Detect Slim basePath .........
Done

Note that a basePath is still not found.
Check.php is running from /var/www/slim4/public/
What would the expected value of basePath be in this case?

It is interesting to note that if I remove BasePathMiddleware and explicitly set a base path with

$app->setBasePath('/var/www/slim4/public/');

every request turns into a 404. Without the above every page works perfectly. Doesn’t seem to matter what the value provided is either.

Any other thoughts? I appreciate your time and expertise.

apache2handler

Ok, then it should be pretty “standard” to set up. You can find the configuration for Apache in the Slim documentation

You should really add the .htaccess file(s) as documented in the BasePathMiddleware.

Note that the Slim basePath is a URL path and not a filesystem path.

For example, when you set the DocumentRoot to /var/www/slim4/public/, then the basePath is '' (empty string). This is usually the case on a production server.

$app->setBasePath('');

On a development machine, you may want to run multiple websites or applications on the same apache instance. Then just keep the DocumentRoot default value, e.g. /var/www/html and create
a sub-directly per project, e.g. /var/www/html/slim4. Also add the two .htaccess files. The Slim (URL) basePath is then /slim4 (without the public path).

$app->setBasePath('/slim4');

I’m sorry but I disagree about using .htaccess. Consider what Apache.org has to say about using them.

You should avoid using .htaccess files completely if you have access to httpd main server config file. Using .htaccess files slows down your Apache http server. Any directive that you can include in a .htaccess file is better set in a Directory block, as it will have the same effect with better performance.

You might find this article a convincing explanation of why they should be avoided. Even if you’re not convinced it is an interesting read.

Because I do have access to the server config files, the appropriate mod_rewrite and <Directory> directives have been implemented therein.

That was my original assumption. However, let’s be absolutely clear about what we mean by URL. Toward that end, consider the following URL generic syntax diagram.

URL generic syntax
URL = scheme:[//authority]path[?query][#fragment]

According to that syntax do you:

  • agree that a “basePath” would make up some portion of the path element?
  • agree that “basePath” does not contain any part of the scheme:[//authority] elements?

If you agree with both questions then basePath is not a URL. Instead, it is a string representing a portion of a filesystem path leading to a folder below the folder set as DocumentRoot.

The purpose of basePath is to eliminate the need for bad URLs like this.

https://www.example.com/my-app/public

Right?

Now that all that is out of the way, let’s return to my original issue. (Which I am now convinced has nothing to do with my configuration or settings.)

I was working through the code in your Slim 4 - PHP Templates blog post. All was going well and made sense until I got to the Assets section where you present a technique for setting a value in a <base href="..."> tag. Here is a snippet from the example

<base href="<?= $basePath ?>/"/>

As I understand it, the href needs to hold a complete URL, i.e. https://example.com/ And if it doesn’t “need” to be, that is what I’d like to have.

Examining the <base> tag the middleware created is where the empty string I mention in the OP first became apparent to me.

The value of $basePath is set in a piece of middleware the Blog presents -PhpViewExtensionMiddleware - which adds some “attributes” to a \Slim\Views\PhpRenderer instance. Here’s the relevant line of code from PhpViewExtensionMiddleware::process(...)

$this->phpRenderer->addAttribute('basePath', $this->app->getBasePath());

The crux of the matter is that $this->app->getBasePath() which, because I do have a perfectly “standard” setup, returns an empty string. This is what it should do in my case, but what we need is the base URL.

One way to get a full URL is accomplished like so.

// Remove these two lines of code
$this->phpRenderer->addAttribute('uri', $request->getUri());
$this->phpRenderer->addAttribute('basePath', $this->app->getBasePath());
// Replace with this code
$uri = $request->getUri();
$this->phpRenderer->addAttribute('uri', $uri);
$baseURL = $uri->getScheme() . '://' . $uri->getHost();
$this->phpRenderer->addAttribute('basePath', $baseURL);

This may or may not work for people serving out of a subdirectory. I’m not going to test for that setup.

Yes, that will work. My preference is to leave /var/www/html in the state that Apache creates when it is first installed. Individual projects are hung off of /var/www/, e.g. /var/www/slim4. Each project has a virtual host configuration (in /etc/apache2/sites-available) where the DocumentRoot is explicitly set. /var/www is locked down so it won’t accept HTTP requests.

Thanks for the really interesting Blog postings!