user9749463
user9749463

Reputation:

Getting Undefined index error and Method name must be a string in ..error in PHP

While upgrading to PHP7, I encountered these problem.

The issue is am trying to create codes that I can reuse like a mini-framework and the RUN function where the problem is used to load the relevant template and supplying the variables. It complains about

undefined index

of these 2 variables

$controller = $routes[$this->route][$this->method]['controller'];
$action = $routes[$this->route][$this->method]['action'];

and it also complained about this line

$page = $controller->$action(); 

which displayed

Fatal error: Uncaught Error: Method name must be a string in...

public function run() {

            $routes = $this->routes->getRoutes();   

            $authentication = $this->routes->getAuthentication();

            if (isset($routes[$this->route]['login']) && !$authentication->isLoggedIn()) {
                header('location: /login/error');
            }
            else if (isset($routes[$this->route]['permissions']) && !$this->routes->checkPermission($routes[$this->route]['permissions'])) {
                header('location: /login/permissionserror');    
            }
            else {
                            $controller = $routes[$this->route][$this->method]['controller'];
                            $action = $routes[$this->route][$this->method]['action']; 
                            $page = $controller->$action();
                $title = $page['title'];

                if (isset($page['variables'])) {
                    $output = $this->loadTemplate($page['template'], $page['variables']);
                }
                else {
                    $output = $this->loadTemplate($page['template']);
                }

                echo $this->loadTemplate('layout.html.php', ['loggedIn' => $authentication->isLoggedIn(),
                                                             'output' => $output,
                                                             'title' => $title
                                                            ]);

            }

This is the index.php

try {
    include __DIR__ . '/../includes/autoload.php';

    $route = ltrim(strtok($_SERVER['REQUEST_URI'], '?'), '/');

    $entryPoint = new \Ninja\EntryPoint($route, $_SERVER['REQUEST_METHOD'], new \Ijdb\IjdbRoutes());
    $entryPoint->run();
}
catch (PDOException $e) {
    $title = 'An error has occurred';

    $output = 'Database error: ' . $e->getMessage() . ' in ' .
    $e->getFile() . ':' . $e->getLine();

    include  __DIR__ . '/../templates/layout.html.php';
}

The code is much, so, I can't display the whole code here since am using MVC pattern, but if there is anything you still want to know, I will gladly post it here

Upvotes: 1

Views: 285

Answers (2)

Veshraj Joshi
Veshraj Joshi

Reputation: 3579

This code is runnable in php 7.2.7 (MAMP and LAMP), your way of dynamic function calling is invalid and your two variables are empty. This is not exact as yours but you can take logic form this demo.

Ok i am just providing a very simple example of reflection with mapping url to class along with functions. I make folder structure like below- folder structure of the image

  1. Here .htaccess is used to redirect all the url to index.php (if no file exists).
  2. index.php include all code that could initialize code(for now only three files were there - uri.php, urlMapping.php and actions.php)
  3. URI.php - have function that provide values like basepath, baseurl, uri
  4. urlMappig.php - that allows you to provide which url hit which class along with method
  5. actions.php will call dynamic class and function (reflection)

now look into code of index.php

    <?php
        include_once('URI.php');
        include_once('urlMapping.php');
        include_once('actions.php');

    ?>

aNow code insise uri.php file -

<?php
   // all function should be accessible to all file loaded now
   // return full url
    function full_url (){ 
        return (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
    }
    // returns current directory
    function directory() {
        $parts = explode("/", __DIR__);
        return $parts[count($parts)-1];
    }
    // return base url
    function base_url(){ 
        $dir = directory();
        return  substr(full_url(), 0, strpos(full_url(), $dir)+strlen($dir));
    }
    // return uri
    function uri() { 
        return substr(full_url(), strlen(base_url())+1);
    }
?>

Now code in urlMapping.php

Note - file name and name of the class must be same as you map url in this file so that you can make call to dynamic classes and function on actions.php. If don't this will not work

<?php
    // this $urlMap will be accessed in actions.php
    $urlMap = [
        // here we use only uri so .. https://example.com/login hit LoginController class along with login function, this is just only the mapping
        'login' => ['class'=>'LoginController',
                    'function'=>'login'],
    ];
?>

Now actions.php code

<?php
    // if call is not like example.com/ means no uri is there
    if(uri()!='')
    {
        // if your uri exists in route collection or urlmapping collection then make call to your dynamic class and methods
        if(array_key_exists(uri(), $urlMap))
        {
            // include the class file dynamically from controllers folder
            include_once('controllers/'.$urlMap[uri()]['class'].'.php');
            // making references for dynamic class
            $controlerObject = new $urlMap[uri()]['class']();
            // call function dynaically from the referece
            $controlerObject->{$urlMap[uri()]['function']}();           

        }
        else
        {
            // you can make 404 page not
            echo 'No routing found';
        }
    }
    // call for home page 
    else
    {
        include_once('index.html.php');
    }
?>

Now controllres/LoginController.php class,

Note - As mentioned above file name and name of the class as in urlMapping.php

<?php
    class LoginController{
        public function login()
        {
            // .... something your code goes here

            echo 'hello from the login function of login controller';
        }
        //...... other function you can define
    }
?>

Upvotes: 3

brevis
brevis

Reputation: 1201

Before calling $page = $controller->$action(); check if controller and action are exists:

if (isset($routes[$this->route][$this->method]['controller']) 
    && isset($routes[$this->route][$this->method]['action'])) {
    $controller = $routes[$this->route][$this->method]['controller'];
    $action = $routes[$this->route][$this->method]['action'];
    $page = $controller->$action();
    // ...
} else {
   // return 404 Page not found
}

Upvotes: 0

Related Questions