Why is $this->controller not callable?

I worked through a tutorial I found which built a small application framework and can’t get the controller that parses the URL into controller, method and parameters to work properly.

I have two methods in the Client controller: index() and display(). But both of my pages are taking me to the view in the index methoid.

I have narrowed things down to a line in the App controller that returns false instead of true, so the correct method is not set.

Could you please take a look at my class and tell me if you see what is causing this issue?

<?php

class App 
{
	
	// Set default controller and default method
	protected $controller = 'Home';
	protected $method = 'index';
	protected $params = [];
	
	public function __construct()
	{
		$url = $this->parseUrl();
		
		// get the controller
		if (file_exists('../app/controllers/' . $url[0] . '.php')) {
			$this->controller = $url[0];
			unset($url[0]);
		}
		
		require_once INC_ROOT . '/app/controllers/' . ucfirst($this->controller) . '.php';
		
		$this->controller = new $this->controller;
		
		// get the method
		
		if (isset($url[1])) {
		
			if (is_callable($this->controller, $url[1])) {  // is_callable here returns false
				$this->method = $url[1];
				unset($url[1]);
			}
		}
		
		// if there is anything left over, they are the parameters
		$this->params = $url ? array_values($url) : [];
		
		call_user_func_array([$this->controller, $this->method], $this->params);
	}
	
	/*** tutorial made this public, but said it might be better to be protected***/
	protected function parseUrl() 
	{
		
		if (isset($_GET['url'])) {
			return $url = explode( '/', filter_var(rtrim($_GET['url'], '/'), FILTER_SANITIZE_URL));
		}
		
	}
}

$url[1] is not a (callable) method of the controller.

Thanks for responding so quickly, @Dormilich.

So the second parameter of is_callable() has to be a method of the controller? I think the person making the tutorial intended to just check if $url[1} was legitimate. What function would be the best here to replace is_callable()?

oh, just seeing that you use it incorrectly. object methods are tested differently from functions (similar to call_user_func()):

is_callable([$object, $method])

I understand. Earlier, the ‘instructor’ defined $url = $this->parseUrl(); and then used $url as a method, but in fact it is an array. He also used method_exists as a substitute for is_callable, which would create the same problem then. I think it is because the 2nd item in the url refers to the method in the controller that is being called.

Could you please recommend to me the best way to handle this -

        // Has a second parameter been passed?
        // If so, it might be the requested method
        if (isset($url[1])) {
            if (method_exists($this->controller, $url[1])) {
                $this->method = $url[1];

                unset($url[1]);
            }
        }

which is a dangerous assumption:

class test
{
    protected function foo() {}
}

$obj = new test;
var_dump( method_exists($obj, 'foo') ); // true
var_dump( is_callable([$obj, 'foo']) ); // false
2 Likes

The explanation in the tutorial was that method_exists just determines if there is a method, but is_callable takes it one step further and checks to see if the method actually works in the situation. so that makes sense sort of. But my results would indicate that (using method_exists) $url[1] is a function, not a method of the controller.

So that brings me back to my issue: I’m trying to isolate the method name from the url ($url[1]) and use it if it is okay to use it. How should I do this? e.g. In the URL http://localhost/ApplicationName/ControllerName/MethodName/Parameter

with is_callable(), otherwise call_user_func_array() later will quit with a fatal error.

Which brings me full-circle back to my original question. What should be different here to make this class work?

use is_callable() correctly. see post #4

I think it might depend on what version PHP this is expected to be run on.

http://php.net/manual/en/function.is-callable.php

As of PHP 5.3.0 is_callable() reports constructors as not being callable. This affects PHP 5 style constructors (__construct) as well as PHP 4 style constructors (i.e. methods with the same name as the class). Formerly, both cases have been considered callable.

True, version 5.3 can pretty much be forgotten, but maybe the tutorial is an older one?

I have to step away for a few hours, but I’ll check that out this evening. I don’t think the PHP version is too old though - I think the videos were done in 2014 by Alex Garret and posted to YouTube.

Okay, I am having problems understanding what are the required parameters for is_callable to be the most effective in this situation. I finally came up with

if (is_callable(array($this->controller, $url[1]))) {  
	$this->method = $url[1];
	unset($url[1]);
} 

which returns true. But I want to make sure it returns true for the right reason.

http://php.net/manual/en/language.types.callable.php

A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1. Accessing protected and private methods from within a class is allowed.

That seems to say that I finally got it right. But could you please reassure me about that by at least saying that I did get it right?

yes.

Thank you.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.