0

What is the best way to obtain the class name for a requested PHP file if my unknown class follows the following pattern?

index.php

<?php
    require_once("startup.php");

    class NotIndex extends View
    {
        public function getView()
        {
            echo "<pre>View Data</pre>";
        }
    }
?>

For testing I want to create an instance of the NotIndex class within the startup.php script.

startup.php

<?php
    require_once("view.php");

    //Include the script again to bring in the class definition
    include($_SERVER['SCRIPT_FILENAME']);

    //Make sure I'm seeing any errors
    error_reporting(E_ALL);
    ini_set('display_errors','On');

    //If I knew the class I could just do this
    $Page = new NotIndex();
    $Page->getView();

    //Todo: Find the class from $_SERVER['SCRIPT_FILENAME']
    //Todo: Once we find the class create an instance and call getView()

    exit;
?>

view.php

<?php
    abstract class View
    {
        abstract function getView();
    }
?>

I'm thinking a regular expression solution might be simple since the desired data will always be between 'class' and 'extends'. I don't have much experience formulating the desired expression and would appreciate some insight.

The following solution may or may not be a good way of doing this, but it did lead me to a non-regular expression solution which is always a plus. If anyone can improve on this, then I will gladly give them credit for both my questions above. Credit actually goes to @Nik answering Stack Overflow question Extracting function names from a file (with or without regular expressions). If I do this in my startup script then I can probably count on the unknown class being the last one in the array. However, I plan on integrating this into my framework and would like a more complete solution.

Since the class name is unknown, you may ask how can we be sure? Well, the abstract class View comes right before the unknown class in the array. Any solution would surely have to check for this. So without guarantee the last class is the needed class, I probably need to traverse backwards in the array until I have identified the abstract View class and the previous value in the array would be the unknown subclass.

startup.php

<?php
    require_once("view.php");

    //Include the script again to bring in the class definition
    include($_SERVER['SCRIPT_FILENAME']);

    //Make sure I'm seeing any errors
    error_reporting(E_ALL);
    ini_set('display_errors','On');

    //If I knew the class I could just do this
    $Page = new NotIndex();
    $Page->getView();

    //Todo: Find the class from $_SERVER['SCRIPT_FILENAME']
    //Todo: Once we find the class create an instance and call getView()
    $Classes = get_declared_classes();
    $ViewObject = array_pop($Classes);

    $NewPage = new $ViewObject();
    $NewPage->getView();

    exit;
?>

View Data

Please expand any opinions on this usage.

So this solution only works if I include my abstract View class immediately before I include the unknown subclass script again via include($_SERVER['SCRIPT_FILENAME']);. So this implies the get_declared_classes() function adds the classes in the order they were defined or included. This could be a problem.

<?php
    require_once("view.php");

    //Include the script again to bring in the class definition
    include($_SERVER['SCRIPT_FILENAME']);
    include("killsolution.php");

    //Make sure I'm seeing any errors
    error_reporting(E_ALL);
    ini_set('display_errors','On');

    //If I knew the class I could just do this
    $Page = new NotIndex();
    $Page->getView();

    //Todo: Find the class from $_SERVER['SCRIPT_FILENAME']
    //Todo: Once we find the class create an instance and call getView()
    $Classes = get_declared_classes();
    $ViewObject = array_pop($Classes);

    $NewPage = new $ViewObject();
    $NewPage->getView();

    exit;
?>

Fatal error: Call to undefined method killsolution::getView()


(This question is a subset of another open Stack Overflow question, What are some PHP object-oriented framework initialization techniques?)

Community
  • 1
  • 1
daganh
  • 421
  • 2
  • 5
  • I'm currently checking out: http://www.php.net/manual/en/function.get-declared-classes.php which was mentioned in a similar but unrelated question. The unknown class is actually the last member in the array. I might try to pop that value and test if it is indeed a subclass of View just to be sure. – daganh Apr 20 '11 at 08:06

2 Answers2

1

Use __autoload().

If you must continue down this path then you can use PHPs Tokenizer to obtain the class name from a file path. Regex is not really the answer to this problem.

A friend of mine has written Überloader (A brute-force autoloader for PHP5.) which uses this very technique when it indexes class files. The _check_file() method from it will be of particular interest to you.

Using Überloader itself may even be a far better solution to your problem. It maintains a cache of class names and their relevant file paths so it is quicker than using the Tokenizer on each page load. Überloader is designed for legacy projects that have not planned or thought about their class naming conventions or file structures.

Treffynnon
  • 21,365
  • 6
  • 65
  • 98
  • Your right the _check_file() method is useful and thanks for the link. Once I've identified the class, this is surely a complete solution to verify. Upvoting for expanding on question. – daganh Apr 20 '11 at 08:29
0

The following solution allows you to iterate over classes defined at runtime and in my case search for a user defined subclass of View. The get_declared_classes() function will return both system and user defined classes in the order they were included. My system contained over 120 system classes before the requested script class was included. For this reason I iterate backwards in the returned array.

startup.php

//Include the script again to bring in the class definition
include($_SERVER['SCRIPT_FILENAME']);

//Make sure I'm seeing any errors for testing
ini_set('display_errors','On');

//Don't assume we will find a subclass of View
$RequestClass = false;

//Iterate over declared classes starting from the last defined class
for($i = count($classes = get_declared_classes()) - 1; $i >= 0; $i--)
{
    //Test if unknown class is a subclass of abstract View
    if(get_parent_class($classes[$i]) == 'View')
    {
        //Store the name of the subclass of View
        $RequestClass = $classes[$i];

        //We found our unknown View
        break;
    }
}

if($RequestClass)
{
    //We can now instantiate the class or do something useful
    $Page = new $RequestClass();
    $Page->getView();
}

exit;

Depending on when you call the get_declared_classes() function, the array could change. I know my unknown class will always be a subclass of View, therefore I can identify it no matter how many classes are defined afterwards. However, just to elaborate, if we have an unknown class that does not extend another class, then we can still obtain the last defined class using:

$LastClass = array_pop(get_declared_classes());

So we can always find the requested page's class if we search for it immediately after it is defined. Theres alot of ways this will break, but most people I suspect will never need this functionality anyways.

daganh
  • 421
  • 2
  • 5