PHP MySQL Novice to Ninja 6e - Method name must be a string



I’m working through this book and I’m getting a weird error in the Apache error logs. I’m at Chapter 9, when we switch over to creating a framework EntryPoint class at CMS-EntryPoint-Interface. Anyway at this point, the website runs just fine but I get a (so-called) Fatal Error in the Apache error log:

PHP Fatal error: Uncaught Error: Method name must be a string in /var/www/classes/Ninja/EntryPoint.php:105\nStack trace:\n#0 /var/www/html/index.php(37): Ninja\EntryPoint->run()\n#1 {main}\n thrown in /var/www/classes/Ninja/EntryPoint.php on line 105, referer: http://mywebsite/

Note: it happens on every route in the website not just the one shown. Also the domain name of the website has been subbed out

Here’s a code excerpt around the failing line, with a few extra debugging lines added n which weren’t in the book:

if (isset($routes[$this->route]['login']) && 
	($routes[$this->route]['login'] == true) &&
	!$authentication->isLoggedIn()) {				
		header('location: /login/error');

//otherwise if the user is logged in, then the action can be called
else {
	echo('$this->route: '. $this->route);
	echo('<br />');
	echo('$this->method: '. $this->method);
	echo('<br />');
	echo('var_dump($routes): ');
	echo('<br />');
	//Define $controller dependent on $routes
	$controller = $routes[$this->route][$this->method]['controller'];
	echo('var_dump($controller): ');
	echo('<br />');
	//Define $action dependent on $routes
	$action = $routes[$this->route][$this->method]['action'];
	echo('$action: '. $action);
	echo('<br />');
	echo('var_dump($action): ');
	//Define $page dependent on the method and URL
	$page = $controller->$action();
	//Define $title as whatever is output by $page
	$title = $page['title'];

N.B. code is actually from a couple of chapters further on but I think the principle applies.

Specifically the failing line is:

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

which I’m pretty certain about not only because of the error message & line number but by selectively killing code execution before and after the questioned line, with die() I can make the error message stop appearing only when I kill the code before the above line; obviously both die() calls cripple the functionality of the actual website.

So the thing I’m struggling with is that $action is a string. The outputs from my debugging are verbose as the the $routes array contains multiple Controllers but here’s an edited highlight (from the empty route):

$this->method: GET
var_dump($routes): array(10) { [""]=> array(1) { [“GET”]=> array(2) { [“controller”]=> object(Ijdb\Controllers\Joke)#7 (3) {…} [“action”]=> string(4) “home” } … }
var_dump($controller): object(Ijdb\Controllers\Joke)#7 (3) { … }
$action: home
var_dump($action): string(4) “home”

So my question is why am I getting this error in the logs? I thought fatal errors were, you know … fatal, but this one seems to be more of a flesh wound! I know it is not actually stopping the website from working but having a false error in the logs gets in the way of being able to look for real errors & it’s bugging me. Any assistance gratefully received

I don’t quite have the same set up as the book, so for full disclosure I’m running:

  • PHP (PHP 7.0.33-0ubuntu0.16.04.3)
  • on a Ubuntu 16.04 server, using
  • a Apache/2.4.18 (Ubuntu) webserver and
  • mysql Ver 14.14 Distrib 5.7.25


Try call_user_func([$controller, $action])



Tagging the author of said book: @TomB



So I’ve been struggling with that problem for about a day … and no sooner had I posted than I figured it out. It’s always the way!

The issue was caused because I had no favicon.ico file in my root public directory. So when my browser made it’s second request to the server for this file, .htaccess rerouted the request for the unknown resource to index.php. Now because the router code doesn’t have a catch-all route handler, it was generating a fatal error, it was just on a secondary file request I didn’t care about so it looked like everything was working to me.

Weird but but glad I figured it out.

BTW all of the controller methods that redirect instead of returning a template & title, e.g. Joke->delete(), should kill the code execution after the redirect, or when the code branch returns to EntryPoint without those variables set, you get more errors