1

I'm working on a web app and learning more and more about php routing, server management etc. I'm good at front-end, but am a beginner in php developing. I'm learning more about using namespaces and composer etc.

I'm having a really weird issue. My folder structure looks like this:

root
  httpdocs
    index.php
    utils
      get-all-languages.php
      data-file-handler.php
      url-helpers.php
    builder
      content
        content.php
  vendor
  config
    config.php

I'm using namespace inside of the utils folder.

This is my url-helpers.php:

namespace App\Utils\Url;

function getRequestParam($param)
{
    // Retrieve the value of a specific parameter from the $_GET array
    return isset($_GET[$param]) ? $_GET[$param] : '';
}

function getCurrentUrl()
{
    // Get the current URL with the correct protocol and host
    $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
    $url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    return $url;
}

function getCurrentLanguage($currentUrl, $redirect = false)
{
    $queryParams = getRequestQueryParams($currentUrl);
    $language = getRequestParam('language', '', $queryParams);
    $languages = \App\Utils\Languages\getAllLanguages();

    if (empty($language) || !in_array($language, $languages)) {
        if ($redirect) {
            $firstLanguage = $languages[0];
            $fallbackUrl = addOrUpdateQueryParam($currentUrl, 'language', $firstLanguage);
            header('Location: ' . $fallbackUrl);
            exit();
        };
    }

    return $language;
}

It uses the get-all-languages.php file by the namespace App\Utils\Languages:

namespace App\Utils\Languages;

require_once(UTILS_DIR . 'data-file-handler.php');

function getAllLanguages()
{
    // Retrieve all languages from the 'languages.json' file
    $languagesData = \App\Utils\DataFileHandler\readDataFromFile(LANGUAGES_DIR . 'languages.json');

    // Sort the languages based on the 'order' key
    uasort($languagesData, function ($a, $b) {
        return $a['order'] <=> $b['order'];
    });

    // Get the sorted language keys
    $sortedLanguages = array_keys($languagesData);

    // Return the sorted languages
    return $sortedLanguages;
}

These two pages work fine, when I remove \App\Utils\DataFileHandler\readDataFromFile from the get-all-languages.php.

My data-file-handler.php looks like this:

namespace App\Utils\DataFileHandler;

function readDataFromFile($filePath)
{
    if (!file_exists($filePath)) {
        // Throw an exception if the file doesn't exist
        throw new Exception('File not found: ' . $filePath);
    }

    // Read and parse JSON data from a file
    $json = file_get_contents($filePath);
    return json_decode($json, true);
}

function saveDataToFile($filePath, $data)
{
    // Convert data to JSON format
    $json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

    // Write JSON data to a file
    file_put_contents($filePath, $json);
}

When trying to open my page, I get this error in the error logs:

AH01071: Got error 'PHP message: PHP Fatal error: Uncaught Error: Call to undefined function App\Utils\DataFileHandler\readDataFromFile() in /var/www/vhosts/feeds.mailmeisters.nl/httpdocs/utils/get-all-languages.php:10\nStack trace:\n#0 /var/www/vhosts/feeds.mailmeisters.nl/httpdocs/utils/url-helpers.php(23): App\Utils\Languages\getAllLanguages()\n#1 /var/www/vhosts/feeds.mailmeisters.nl/httpdocs/app/builder/content/content.php(12): App\Utils\Url\getCurrentLanguage()\n#2 /var/www/vhosts/feeds.mailmeisters.nl/httpdocs/index.php(56): require_once('...')\n#3 {main}\n thrown in /var/www/vhosts/feeds.mailmeisters.nl/httpdocs/utils/get-all-languages.php on line 10'

It can't find the function App\\Utils\\DataFileHandler\\readDataFromFile() in /var/www/vhosts/feeds.mailmeisters.nl/httpdocs/utils/get-all-languages.php:10

But I do not see what I'm doing wrong.

My composer.json is fine as well I think:

{
    "require": {
        "twig/twig": "^3.7"
    },
    "autoload": {
      "psr-4": {
        "App\\": "httpdocs/"
      }
    } 
}

Because the exact same setup works fine for my url-helpers.php. I don't get the error message that it can't find the function getAllLanguages();.

Does anyone know what I am doing wrong here and why my get-all-languages.php can't find the data-file-handler.php functions?

Thanks in advance!

ADyson
  • 57,178
  • 14
  • 51
  • 63

1 Answers1

1

Your UTILS_DIR constant is probably wrong. If it contains a relative path, this path is resolved at runtime from the location of the file that is using it and not the file that is defining it.

To prevent that, you may either :

  • Use an absolute path
  • Start your path with the magic constant __DIR__ which will effectively transform your relative path in an absolute path at runtime (recommended, because it still works if you move your code elsewhere). See here for more information.

Side note : the PSR-4 autoload you defined in composer.json will autoload classes only, not functions. Unless you have classes elsewhere in your "httpdocs" folder, it does nothing.

Halibut
  • 188
  • 9
  • Thanks a lot! @halibut. My util DIR is defined in my config.php: define('HTTPDOCS_DIR', __DIR__ . '/../httpdocs'); // Path to the 'httpdocs' folder define('UTILS_DIR', HTTPDOCS_DIR . '/utils/'); // Path to the 'utils' folder The fact that PSR-4 autoload doesn't load functions is news for me. Do you know if there's another way to autoload functions? – SybrenMeister Aug 02 '23 at 07:21
  • Composer can do that (see [here](https://stackoverflow.com/a/17925443/22310127)). But it will not lazy load the files, so I wouldn't recommend it. Instead, you could create one class per file and define your functions as static methods in these classes. Then you can remove all your `require` instructions. Composer will lazy autoload your classes and methods. All you have to do is make sure that your folders and files names respect the [PSR-4 standard](https://www.php-fig.org/psr/psr-4/). Eg : Utils/DataFileHandler.php file should contain class DataHandler from namespace App\Utils – Halibut Aug 02 '23 at 09:32