Running in a sub-directory 2

Hi, I have a simple slim app, I can’t make it work on shared hosting. I’ve already tried using the subdirectory and it doesn’t work. In the Windows environment, class mapping works correctly, in Linux (shared hosting) it is not working for routes where I have endpoints mapped to controller methods.

I would really appreciate help, I’m seriously considering abandoning the slim, as I always find it very difficult to make simple things work on it…

my project structure is:
├── config/ Configuration files
├── routes/ Routes files
├── public/ Web server files (DocumentRoot)
│ └── index.php The front controller
├── src/ PHP source code (The App namespace)
├── vendor/ Reserved for composer
└── composer.json Project dependencies

I have a route file for Auth that simply doesn’t work on Linux for the /auth/register and /auth/login routes:

<?php

use Slim\Routing\RouteCollectorProxy;
use Src\Auth\Controller\AuthController;

$app->group('/auth/login', function(RouteCollectorProxy $app) {
    $app->get('', \Src\Action\LoginAction::class );
    $app->post('', [AuthController::class, 'login']);
});

$app->get('/auth/logout', AuthController::class .':logout');
//     //->addMiddleware(new AuthMiddleware());

$app->group('/auth/register', function(RouteCollectorProxy $app) {
    $app->post('', AuthController::class .':register');
});

for GET in /auth/login it works perfectly. The class code is:

<?php

namespace Src\Action;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

final class LoginAction {
    public function __invoke(Request $request, Response $response): Response
    {
        $response->getBody()->write(json_encode(['Login' => 'UI']));

        return $response->withHeader('Content-Type', 'application/json');
    }
}

the AuthController class code is:

<?php

namespace Src\Auth\Controller;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Src\Auth\Service\AuthService;

final class AuthController {

    public function __construct(
        private AuthService $authService,
    ) {}
    
    public function login(Request $request, Response $response, array $args): Response {
        $response = $response->withHeader('Content-Type', 'application/json');

        try {
            [$email, $password] = array_values($request->getParsedBody());
            $ret = $this->authService->login($email, $password);

            $response->getBody()->write(json_encode([
                    "status"  => $ret->__get('status'),
                    "message" => $ret->__get('message'),
                    "data"    => $ret->__get('data')
                ]));
            $response = $response->withStatus($ret->__get('status'));

            return $response;

        } catch (\Exception $e) {
            $error = ['message' => 'An error occurred', 'details' => $e->getMessage()];
            $response->getBody()->write(json_encode($error));
            return $response->withStatus(500);
        }
    }
   
    public function register(Request $request, Response $response, array $args): Response {
        $data = $request->getParsedBody();
        $ret = $this->authService->createUser($data);

        $response = $response->withHeader('Content-Type', 'application/json');
        $response = $response->withStatus( $ret->__get('status'));
        $response->getBody()->write(json_encode([
             'status'  => $ret->__get('status'),
             'message' => $ret->__get('message'),
             'data'    =>  $ret->__get('data')
        ]));

        return $response;
    }
}

The errors faced when trying to access routes that are mapped to AuthController:

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Slim Application Error</title>
    <style>
        body {
            margin: 0;
            padding: 30px;
            font: 12px/1.5 Helvetica, Arial, Verdana, sans-serif
        }

        h1 {
            margin: 0;
            font-size: 48px;
            font-weight: normal;
            line-height: 48px
        }

        strong {
            display: inline-block;
            width: 65px
        }
    </style>
</head>

<body>
    <h1>Slim Application Error</h1>
    <div>
        <p>The application could not run because of the following error:</p>
        <h2>Details</h2>
        <div><strong>Type:</strong> RuntimeException</div>
        <div><strong>Code:</strong> 0</div>
        <div><strong>Message:</strong> Callable Src\Auth\Controller\AuthController::login() does not exist</div>
        <div><strong>File:</strong> /home/delivery/www/api/vendor/slim/slim/Slim/CallableResolver.php</div>
        <div><strong>Line:</strong> 144</div>
        <h2>Trace</h2>
        <pre>#0 /home/delivery/www/api/vendor/slim/slim/Slim/CallableResolver.php(97): Slim\CallableResolver-&gt;resolveSlimNotation(&#039;Src\\Auth\\Contro...&#039;)
#1 /home/delivery/www/api/vendor/slim/slim/Slim/CallableResolver.php(70): Slim\CallableResolver-&gt;resolveByPredicate(&#039;Src\\Auth\\Contro...&#039;, Array, &#039;handle&#039;)
#2 /home/delivery/www/api/vendor/slim/slim/Slim/Routing/Route.php(345): Slim\CallableResolver-&gt;resolveRoute(Array)
#3 /home/delivery/www/api/vendor/slim/slim/Slim/MiddlewareDispatcher.php(73): Slim\Routing\Route-&gt;handle(Object(Nyholm\Psr7\ServerRequest))
#4 /home/delivery/www/api/vendor/slim/slim/Slim/MiddlewareDispatcher.php(73): Slim\MiddlewareDispatcher-&gt;handle(Object(Nyholm\Psr7\ServerRequest))
#5 /home/delivery/www/api/vendor/slim/slim/Slim/Routing/Route.php(321): Slim\MiddlewareDispatcher-&gt;handle(Object(Nyholm\Psr7\ServerRequest))
#6 /home/delivery/www/api/vendor/slim/slim/Slim/Routing/RouteRunner.php(74): Slim\Routing\Route-&gt;run(Object(Nyholm\Psr7\ServerRequest))
#7 /home/delivery/www/api/vendor/slim/slim/Slim/Middleware/BodyParsingMiddleware.php(65): Slim\Routing\RouteRunner-&gt;handle(Object(Nyholm\Psr7\ServerRequest))
#8 /home/delivery/www/api/vendor/slim/slim/Slim/MiddlewareDispatcher.php(129): Slim\Middleware\BodyParsingMiddleware-&gt;process(Object(Nyholm\Psr7\ServerRequest), Object(Slim\Routing\RouteRunner))
#9 /home/delivery/www/api/vendor/slim/slim/Slim/Middleware/RoutingMiddleware.php(45): Psr\Http\Server\RequestHandlerInterface@anonymous-&gt;handle(Object(Nyholm\Psr7\ServerRequest))
#10 /home/delivery/www/api/vendor/slim/slim/Slim/MiddlewareDispatcher.php(129): Slim\Middleware\RoutingMiddleware-&gt;process(Object(Nyholm\Psr7\ServerRequest), Object(Psr\Http\Server\RequestHandlerInterface@anonymous))
#11 /home/delivery/www/api/vendor/slim/slim/Slim/Middleware/MethodOverrideMiddleware.php(42): Psr\Http\Server\RequestHandlerInterface@anonymous-&gt;handle(Object(Nyholm\Psr7\ServerRequest))
#12 /home/delivery/www/api/vendor/slim/slim/Slim/MiddlewareDispatcher.php(129): Slim\Middleware\MethodOverrideMiddleware-&gt;process(Object(Nyholm\Psr7\ServerRequest), Object(Psr\Http\Server\RequestHandlerInterface@anonymous))
#13 /home/delivery/www/api/vendor/slim/slim/Slim/Middleware/ErrorMiddleware.php(77): Psr\Http\Server\RequestHandlerInterface@anonymous-&gt;handle(Object(Nyholm\Psr7\ServerRequest))
#14 /home/delivery/www/api/vendor/slim/slim/Slim/MiddlewareDispatcher.php(129): Slim\Middleware\ErrorMiddleware-&gt;process(Object(Nyholm\Psr7\ServerRequest), Object(Psr\Http\Server\RequestHandlerInterface@anonymous))
#15 /home/delivery/www/api/vendor/slim/slim/Slim/MiddlewareDispatcher.php(73): Psr\Http\Server\RequestHandlerInterface@anonymous-&gt;handle(Object(Nyholm\Psr7\ServerRequest))
#16 /home/delivery/www/api/vendor/slim/slim/Slim/App.php(209): Slim\MiddlewareDispatcher-&gt;handle(Object(Nyholm\Psr7\ServerRequest))
#17 /home/delivery/www/api/vendor/slim/slim/Slim/App.php(193): Slim\App-&gt;handle(Object(Nyholm\Psr7\ServerRequest))
#18 /home/delivery/www/index.php(3): Slim\App-&gt;run()
#19 {main}</pre>
    </div> <a href="#" onclick="window.history.go(-1)">Go Back</a>
</body>

</html>

This looks like an Composer autoload issue. Can you show us your composer.json file.

Hello, sure. This is my Composer.json:

{
    "require": {
        "slim/slim": "4.14.0",
        "nyholm/psr7": "1.8.1",
        "nyholm/psr7-server": "1.1.0",
        "php-di/php-di": "7.0.6",
        "slim/twig-view": "3.4.0",
        "slim/flash": "0.4.0",
        "firebase/php-jwt": "6.10.1",
        "vlucas/phpdotenv": "5.6.0"
    },
    "autoload": {
        "psr-4": {
            "Src\\": "src/",
            "Database\\": "database/",
            "Middlewares\\": "middlewares/",
            "Routes\\": "routes/"
        }
    },
    "config": {
        "process-timeout": 0
    },
    "scripts": {
        "start": "php -S localhost:8080 -t public/",
        "dump": "composer dump-autoload"
    }   
}

src/ PHP source code (The App namespace)

“Src\”: “src/”,

Generally, name namespace “Src” is not a good practice and should be renamed to “App” as you wrote in your project structure. It should work fine, if you put your classes into the App namespaces, this includes App\Middlewares etc… Simple is better.

    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },

Then rename the existing “Src” Namespaces to “App”.
After that run composer dump-autoload command to update the autoloader.

My second recommendation is to use single action controllers with a __invoke method.

public function __invoke(Request $request, Response $response): Response

Please try that and post your result.

Hello,
Thank you for your help.

I followed all the recommendations but the problem persisted.

Yesterday I redid part of the code and noticed something that wasn’t so clear to me: in Linux, the namespaces in uppercase letters were not being found with the directories in lowercase letters. On Windows this is not a problem. I redid all the directory naming, now everything works.

We can close this topic.