I'm getting a "Call to a member function process_data() on a non-object in page.class.php on line 35" even though the object has been called.
Here is the index.php extraction showing the object being instantised
// require our common files
require("modules/module.php");
require("registry/objects/datetime.class.php");
require("registry/objects/page.class.php");
// load in all the objects
$datetime = new dateandtime;
$page = new page;
$module = new module;
It then passes to the Process class
require("template.class.php");
$template = new template($php_path . "controllers/themes/adm/" . $page . ".html");
// Place in both commonly used language and page specific language
$template->language($php_path . "controllers/language/en/adm/common.php");
$template->language($php_path . "controllers/language/en/adm/" . $page . ".php");
// Tell the page's module to load in data it needs
$module->process_data("module_" . $page);
// Output the final result
$template->output();
It's at this point PHP is throwing the error. The contents of the module.php file is as follows
class module {
public function process_data ($child) {
require($child . ".php");
read_data();
return true;
}
}
I've tried moving the instance declaration to within the second pasted code, but that generates more errors, because the class that "module" calls in then uses some of the "template" classes as well - so the same issue occurs just further down the line.
What am I getting wrong her, or completely missing, I'm sure it's the latter but I really need help here. Thanks
It looks to me as if variable $module was not in the scope when you try to call object method. Could you try var_dump($module) before $module->process_data("module_" . $page). What is the result of this function? Quick solution may be declaring $module global, but globals are not a very good idea anyway (but you may check if it works).
Related
Currently, I have a pretty average php auto loader loading in my classes. I've come to a point in development where I will need to override a class with another class based on a variable. I'm running a custom SaaS application, and we have the occasional organization that will demand some weird change to the way the system functions. In the past, we've filled up our code with garbage by massive IF statements for orgs, such as
if(ORGID == 'ABCD'){
//do this insane thing
}else{
//Normal code here.
}
So, I've been toying with the idea of a dynamic auto loader. ORGID is one of the very first defines in the application. The entire application is running under a fixed namespace of COMPANY\PRODUCT; Here's a code sample of what I was thinking I could do.
class MyLoader {
static public function load($name) {
$temp = explode('\\',$name);
$class = array_pop($temp);
$name = str_replace('_',DIRECTORY_SEPARATOR,$class);
if(file_exists(ROOT.'includes/_classes/' . $name . '.php')){
include(ROOT.'includes/_classes/' . $name . '.php');
}
}
}
spl_autoload_register(__NAMESPACE__ .'\MyLoader::load');
Since ROOT and ORGID are defined before the autoloader comes into play, I thought about doing this
class MyLoader {
static public function load($name) {
$temp = explode('\\',$name);
$class = array_pop($temp);
$name = str_replace('_',DIRECTORY_SEPARATOR,$class);
if(file_exists(ROOT.'includes/_classes/' . ORGID . '/' . $name . '.php')){
include(ROOT.'includes/_classes/' . ORGID . '/' . $name . '.php');
}elseif(file_exists(ROOT.'includes/_classes/' . $name . '.php')){
include(ROOT.'includes/_classes/' . $name . '.php');
}
}
}
spl_autoload_register(__NAMESPACE__ .'\MyLoader::load');
While this works, I have to copy/paste my entire class into the org specific class file, then make changes. I can't extend the primary class, because the classes share the same name. The only option I've been able to come up with that would allow me to extend my classes in such a way is to never load the base class.
Instead of
$myObj = new myClass();
I call
$myObj = new customMyClass();
and I have a file called customMyClass(); which simply extends myClass without making any changes to it. This way, the auto loader will load customMyClass and then load myClass. If an organization has their own customMyClass in their organization folder, then it will load in that class, which will then properly load in myClass.
While this works, we have hundreds of class files, which would double if we had a custom file for each.
I've seen a couple of examples that use eval to handle similar situations. Is that really the only way to do this type of thing?
UPDATE:
Just so I'm clear, the end goal is so that the thousands of places we've called $myObj = new myClass(); doesn't need to be rewritten.
I'm building a script that got a static class used to load few things including files and views.
class load
{
public static function view($file_path, $area)
{
debug::log('getting view <b>' . $area . $file_path . '</b>.');
ob_start();
self::file($file_path, 'areas/' . $area . '/views');
debug::log('flushing view <b>' . $area . $file_path . '</b>.');
eturn ob_get_clean();
}
public static function file($file, $folder)
{
if(is_file($file_path = ROOT . '/' . $folder . '/' . $file))
{
if(require_once $file_path)
{
debug::log('file <b>' . $file_path . '</b> included.');
return true;
}
}
else
debug::kill('requested file <b>' . $file_path . '</b> does not exist.');
}
}
In the controller Im calling the view method to get a view:
$html = load::view('public', 'path/to/view/file.php');
Obviously, Im not able to access the variables from the controller at the view file using this practice, so I did a small modification on the view class to capture the vars:
public static function view($file_path, $area, $vars = array())
And added the following lines of codes to get the keys into vars:
while(list($n_list_var,$v_list_var)=each($vars))
$$n_list_var = $v_list_var;
But again I can't access the vars since Im using a method to load a file.
I have a method to load the files because I wanna test and log each file include attempt and not repeat the code every time I need include a file. And I have the loader view inside the loader class so I have all the methods of this kind together. Should I give up on using a class to load files? Should I use the loader view method on a extendable class from my controller?
Instead of going ahead and modify my entire script I would like to hear some opinions ... what would be the best practice to go? Or is there a way to solve my problem? Maybe using __set and __get magic methods?
Thanks,
Why not just pass a $vars argument to load::file() and extract( $vars ) (possibly moving the vars you use inside file() into class variables to prevent them from being overwritten)?
I'm suggesting using extract() instead of:
while(list($n_list_var,$v_list_var)=each($vars))
$$n_list_var = $v_list_var;
By the way, it would be a good idea to name your class Load.
I am working with a Drupal theme, and I see a lot of variables which look like were created with extract(). Is it possible to track back, and see where that array is?
I take you are referring to the variables passed to a template file, which effectively are extracted from an array.
The code that does that in Drupal 7 is in theme_render_template().
function theme_render_template($template_file, $variables) {
extract($variables, EXTR_SKIP); // Extract the variables to a local namespace
ob_start(); // Start output buffering
include DRUPAL_ROOT . '/' . $template_file; // Include the template file
return ob_get_clean(); // End buffering and return its contents
}
The function is called from theme(), which executes the following code.
// Render the output using the template file.
$template_file = $info['template'] . $extension;
if (isset($info['path'])) {
$template_file = $info['path'] . '/' . $template_file;
}
$output = $render_function($template_file, $variables);
$render_function by default is set to 'theme_render_template', but its value is set with the following code (in theme()).
// The theme engine may use a different extension and a different renderer.
global $theme_engine;
if (isset($theme_engine)) {
if ($info['type'] != 'module') {
if (function_exists($theme_engine . '_render_template')) {
$render_function = $theme_engine . '_render_template';
}
$extension_function = $theme_engine . '_extension';
if (function_exists($extension_function)) {
$extension = $extension_function();
}
}
}
Just echo the $GLOBALS variable and you might find where it came from if the array was not unset.
Im not familiar with Drupal so this is just a suggestion, but if drupal has a templating structure or if an array is passed from a controller or such then possible that extract is used,
You could use get_defined_vars within your view to get all vars and its possible that there is an array there that you can cross reference with variables you know of that are in the same array or such.
<?php
$vars = get_defined_vars();
print_r($vars);
//or maybe
print_r($this);
?>
I'm learning OO PHP and am trying to get some of the coding practices straight. Here is a pared down version of some code I'm using for error (and exception) handling:
final class MyErrorExceptionHandler {
private $level = array(); // error levels to be handled as standard errors
private $path = array(); // full path to file
private $path_short; // filename plus working dir
public function myErrorHandler($severity, $message, $file, $line) {
if (error_reporting() & $severity) { // error code is included in error_reporting
$this->level = array(E_WARNING => 'warning',
E_NOTICE => 'notice',
E_USER_WARNING => 'user warning',
E_USER_NOTICE => 'user notice');
if (array_key_exists($severity, $this->level)) { // handle as standard error
/*$this->severity = $severity;
$this->message = $message;
$this->file = $file;
$this->line = $line;*/
$this->printMessage($severity, $message, $file, $line);
} else { // fatal: E_USER_ERROR or E_RECOVERABLE_ERROR use php's ErrorException converter
throw new ErrorException($message, 0, $severity, $file, $line);
}
}
} // fn myErrorHandler
private function printMessage($severity, $message, $file, $line) {
echo ucfirst($this->level[$severity]) . ': ' . $message;
$this->shortenPath($file);
echo ' in ' . $this->path_short . ' on line ' . $line;
} // fn printMessage
private function shortenPath($file) {
$this->path_short = $file;
$this->path = explode(DIRECTORY_SEPARATOR, $file);
if (count($this->path) > 2) { // shorten path to one dir, if more than one dir
$this->path_short = array_pop($this->path); // filename
$this->path_short = end($this->path) . DIRECTORY_SEPARATOR . $this->path_short; // dir+file
}
} // fn shortenPath
} // cl MyErrorExceptionHandler
The title of this question is probably a little bit off because I'm not 100% on the terminology. Basically I'm trying to figure out a few things.
Is it right to explicitly declare $level and $path as arrays?
Should $level be declared as it is (and made $this->level)? If so, have I assigned its value (E_WARNING etc.) in a wise place? Would the constructor (not shown here) be a smarter choice?
Note the commented block in myErrorHandler(). Originally I had declared all of these properties at the top of the class, and then called $this->printMessage() without any parameters. Which is the more correct way? If I keep the code as is, would I want to then use $this->severity = $severity etc. inside printMessage()?
So, would it be better to:
replace
$this->shortenPath($file);
echo ' in ' . $this->path_short . ' on line ' . $line;
with
$path_short = $this->shortenPath($file);
echo ' in ' . $path_short . ' on line ' . $line;
ultimately, and give a return value in shortenPath()?
I realize this is a mishmash of several different questions, but what I'm trying to get at is a common inquiry about the proper style of declaring/using variables/properties, specifically when dealing with methods.
To summarize: When should I use $this->foo = $foo?
EDIT: sorry, I have assumed below that you would create a new instance of the 'object' with each error which obviously you are not doing. Just edited my answer to reflect this.
"When should I use $this->foo = $foo?"
There can be several cases in which you would do this, but it's usually if you create $foo within a method and wish to have that then accessed by the entire object.
For example, if you wanted to call on an object and use that within this particular object (if it doesn't make sense to extend). You would do something like:
$foo = new DataModel();
$this->foo = $foo;
OR
$this->foo = new DataModel();
That object may be a decorator or something else related to error handling and the above code would usually feature in your constructor. You could then access the methods of that object any time by using:
$this->foo->objectMethod();
..and to express something noted in the comments to this answer:
"would you assign $file to the object as that is used in several methods?"
I wouldn't assign $file to the object,
here's why. The semantics of the word
"property" means "belongs to". In your
case, your class is a error handler.
$file doesn't belong to the error
handler, it belongs to an error
instance. If your class was
MyErrorHandler_Error (created for each
instance of a triggered error), then
$file would be a property of that
class, along with $line and $level.
To answer what I can from your other questions:
It's neither. I would consider it preference.
Yes - any variables or values which should be available to your entire object and required for the object to run properly, should probably be set within your constructor, if not within your variable declarations (not sure of terminology there) at the top of the class.
read the comments below. Because this particular class deals with multiple instances of errors - assigning the properties of those errors to the object wouldn't be best practice as you will be overwriting them with each new error. However, it does make sense to store all of your errors and error properties within an array assigned to the object if you require to access historical data. For example, at the moment, if you create a new error - that is all you are doing. You have no way of accessing any old errors this object has created.
see above
You should also think about conflicts when assigning properties to objects. Are you likely to reassign, because if so, the old property will be gone. Fairly simple but still something you have to consider.
Is it possible to define private variables in a PHP script so these variables are only visible in this single PHP script and nowhere else? I want to have an include file which does something without polluting the global namespace. It must work with PHP 5.2 so PHP namespaces are not an option. And no OOP is used here so I'm not searching for private class members. I'm searching for "somewhat-global" variables which are global in the current script but nowhere else.
In C I could do it with the static keyword but is there something similar in PHP?
Here is a short example of a "common.php" script:
$dir = dirname(__FILE__);
set_include_path($dir . PATH_SEPARATOR . get_include_path());
// Do more stuff with the $dir variable
When I include this file in some script then the $dir variable is visible in all other scripts as well and I don't want that. So how can I prevent this?
There are a few things you could do to keep $dir out of subsequent files
Example 1
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path());
This is the most obvious.
Example 2
$dir = dirname(__FILE__);
set_include_path($dir . PATH_SEPARATOR . get_include_path());
// work with $dir
unset($dir);
Just unset the variable after defining it and using it. Note this will unset any variable named $dir used prior to including this script.
Example 3
define('DIR_THIS', dirname(__FILE__));
set_include_path(DIR_THIS . PATH_SEPARATOR . get_include_path());
It is less likely I suppose to redefine a global constant like this.
Example 4
function my_set_include_path {
$dir = dirname(__FILE__);
set_include_path($dir . PATH_SEPARATOR . get_include_path());
// Do more stuff with the $dir variable
$my_other_var = 'is trapped within this function';
}
my_set_include_path();
You can define as many variables within that function and not affect the global namespace.
Conclusion
The first method is the easiest way to solve this problem, however because you want to use $dir again, it may not be ideal. The last example will at least keep that $dir (and any others defined in that function) out of the global namespace.
The only way you're going to accomplish anything close to what you want is to wrap everything in that included file in a function, and call it. If the file needs to execute itself you could still do
<?php
run_myfile()
function run_myfile() {
...
}
?>
There is no generic way to make a variable scoped to only a file outside of namespaces, classes, or functions.
Well, I'm probably getting flailed for this, but you if you are totally desperate you could use a Registry for that. I've whipped up a small one that does without classes (since I assume from And no OOP is used here so I'm not searching for private class members. means you don't want to do it with OOP at all)
function ®istry_get_instance()
{
static $data = array();
return $data;
}
The static $data variable inside is persisted inside the function scope, so you can call the function wherever you like and always get the same contents. The crucial point is returning by reference, e.g.
$registry = ®istry_get_instance(); // get $data array by reference
$registry['foo'] = 'bar'; // set something to $data
unset($registry); // delete global reference to $data
print_r(®istry_get_instance()); // show $data
Obviously you'd still have $registry as a variable in the global scope when calling this method from the global scope. So, you could add some more functions to make the Registry more convenient to use, e.g. for setting data to the Registry:
function registry_set($key, $value)
{
$registry = ®istry_get_instance();
$registry[$key] = $value;
}
and for getting it out again:
function registry_get($key)
{
$registry = ®istry_get_instance();
if(array_key_exists($key, $registry)) {
return $registry[$key];
} else {
trigger_error(sprintf(
'Undefined Index: %s', htmlentities($key)
), E_USER_NOTICE);
}
}
and for checking if a key exists:
function registry_isset($key)
{
$registry = ®istry_get_instance();
return array_key_exists($key, $registry);
}
which you could then use like:
registry_set('foo', 'bar'); // setting something to the registry
var_dump( registry_isset('foo') ); // check foo is in the registry now
echo registry_get('foo'); // prints 'bar'
echo registry_get('punt'); // raises Notice
You could populate the Registry from an include file with an additional method like this:
function registry_load_file($file)
{
if(!is_readable(realpath($file))) {
return trigger_error(sprintf(
'File is not readable: %s', htmlentities($file)
), E_USER_WARNING);
}
$config = include $file;
if(!is_array($config)) {
return trigger_error(sprintf(
'Expected file %s to return an array', htmlentities($file))
, E_USER_WARNING);
}
$registry = ®istry_get_instance();
$registry += $config;
}
with the include file having to return an array:
// config.php
return array(
'setting1' => 'something'
);
and then you can do
registry_load_from_file('config.php'); // add the contents of config to registry
print_r(registry_get_instance()); // show content of registry
Of course, this is now six functions in the global scope just for not having a global variable. Don't know if it's worth it, especially since I consider static in functions and all that reference stuff doubtful practice.
Take it as a proof of concept :)
Why not just put everything in a static class? Then you only have a single "variable" that could possibly conflict with the global namespace.
class MyClass {
public static $myvar = 1;
public static $myvar2 = "xyz";
public static function myfunction() {
self::$myvar++;
self::$myvar2 = "abc";
}
}
// References to class items, if needed
MyClass::myfunction();
MyClass::$myvar += 3;
If the problem you are trying to is just:
$dir = dirname(__FILE__);
set_include_path($dir . PATH_SEPARATOR . get_include_path());
// Do more stuff with the $dir variable
Then the solution would be to change the include path relative to '.' in your ini settings. E.g. change:
include_path=includes:/usr/local/php
to
include_path=./includes:/usr/local/php
Note that a script does not come into scope except where you explicitly include/require it (both the _once check applies globally) however I would recommend strongly against calling include/require from within a function - its much more transparent having the includes/requires at the top of the script.
I think that the problem you are trying to solve is based on a false premise and you should look for another way of fixing it. If you want the code in an include file to behave differently depending on what includes it, then really you should seperate it out into 2 seperate files - or maybe even 3 - 2 for the different behaviours and 1 for the common.
C.