Using require and include from PHP class to load outside the class - php

I've been attempting to create a PHP loader class that'll take care of all of my directory issues. I've mostly gotten it to work, but it breaks when including global functions.
Here's what I'm using:
<?php
class Loader {
public function __construct() { ... }
private function check_if_file_exists($file) { ... }
public function load($file) {
$this->check_if_file_exists($file); //Throws fatal Exception if not a file
//The "important" stuff: works with global vars, not with global functions:
extract($GLOBALS, EXTR_REFS);
ob_start();
require_once "{$this->path}/$file";
return ob_get_clean();
}
}
This lets me do the following:
<?php
$loader = new Loader();
$loader->load('file.php'); //Class takes care of path stuff--don't worry about it
//This works:
print $variable_in_file_dot_php;
//This does NOT work:
function_in_file_dot_php();
How can I make it so that function_in_file_dot_php(); works?

Better you use AutoLoader class already available in php.Refer this url http://php.net/manual/en/language.oop5.autoload.php

i'm going to try to answer your question as a technical curiousity, but i strongly advise you not to do this.
Referring to the include/require documentation I see that variables defined inside included files will inherit the variable scope of the line that called require. In your case this will be the variable scope of Loader::load() method inside some instance of Loader class
Therefore $variable_in_file will not be available globally. unless you
define the $var before calling the include statement, thus giving it global scope.
declare it global inside your calling method (Loader::load())
You acomplish #2 with extract($GLOBALS...) however in order to do #1, you must have a priori knowledge of what is being included before you include it... invalidating your attempt at generalization with the Loader class.
function_in_file() however should be available in the global scope, I'd like to see your file.php and error message. Here's mine.
$cat foo.php
public function load($file) {
extract($GLOBALS,EXTR_REFS);
require_once $file;
}
}
$variable = 1;
$loader = new Loader();
$loader->load('file.php');
echo "\n" . $variable;
echo "\n" . method();
$cat file.php
<?php
function method() {
echo "hi i am a method";
}
outputs
$php foo.php
hello i am a variablehi i am a method
but seriously, don't do this. You seem to be trying to use includes() as a vector of code reuse, when it is mostly envisioned as a method for code separation. You are messing with phps' natural scoping in a hard to debug and unpredictable way. This is an anti-pattern.

Related

PHP template inclusion outside global scope

I'm currently creating a framework, just for the sake of a personal study-project, i.e. to be able to evaluate php-frameworks in general futurewise. As I'm starting to build up more appreciation for some of the frameworks, especially about the way they designed their modules and made them available in 'their scope', during the development of this project I'm also baffled as to why almost all framework-developers include the (view)templates inside the scope of their classes.
Perhaps there are some developers roaming these boards that are able to be constructive about it.
An example (which for the sake of clarity is very much oversimplified).
Why do this...
<?php
class TemplateInc {
public $template;
public function __construct($page) {
$this->template = $page;
}
public function render() {
require_once $this->template;
}
}
$template_include = new TemplateInc('mypage.html');
$template_include->render();
?>
...when its also possible to do it like this, at the same time being able to also use the global-scope variables in the templates (if appropriate) ?
<?php
class TemplateName {
public $template;
public function __construct($page) {
$this->template = $page;
}
public function getTemplate() {
return $this->template;
}
}
$templatename = new TemplateName('mypage.html');
require_once $templatename->getTemplate();
?>
Usually the template class would also make additional values available to the template. E.g.:
public function render(array $vars) {
extract($vars);
require $this->template;
}
Or the template could make use of $this->.. to access other methods/objects/values/services which are part of the class. None of this is possible if you do the require outside of the class. Then only global variables are available to the template, of which you hopefully don't have any (don't use global). Doing all this inside a method allows you do to something like extract($vars) without cluttering the global scope.
Further, instead of a nicely self-contained call to $template->render(), you now need to do require $template->render(), which kind of negates the point of writing a class in the first place; the caller is still doing all the work and the class itself basically doesn't (can't) do anything.

spl_autoload_register vs __autoload

I try
function some_class($class) {
require PATH.'test'.$class.'.php';
}
function test_class($class) {
require PATH.'some'.$class.'.php';
}
spl_autoload_register("test_class");
spl_autoload_register("some_class");
$tst = new SomeClass();
SomeClass class locate in "PATH/some/SomeClass.php", but spl_autoload_register call only "test_class" function and not call "some_class". But if I change functions position, then it find new SomeClass.
Autoload functions are called in sequential order, the first registered function is called first, the second one is called after and so on, until one of them finds the class
(unless $prepend is used). If none do, a fatal error is triggered.
However, in your example, you are using require instead of include, so the first function will always fail with a fatal error if the file doesn't exist, so the second one will never be called. Even if you replace it with include, if both files exist, only the first registered function will be invoked.
It's a good idea to use file_exists or include when using multiple autoload functions.
You can also throw exceptions in your autoload functions, so that you can handle undefined classes gracefully instead of killing the script if a class is not found. Per example:
function my_autoload($class) {
$filename = "class.{$class}.php";
if (!file_exists($filename))
throw new Exception("$class not found");
include_once $filename;
}
spl_autoload_register('my_autoload');
try {
$a = new Foo;
} catch (Exception $e) {
// Foo wasn't found
}
// continuing the script...
Obviously it depends on your needs, as throwing an exception will halt the execution of subsequent autoload functions.
Finally, it discouraged to use __autoload at all, spl_autoload_register providing a lot more flexibility that the former.
You'd be better to just use one autoload function. Something like this:
function some_class($class) {
if (file_exists(PATH.'test'.$class.'.php')) {
include PATH.'test'.$class.'.php';
} elseif (file_exists(PATH.'some'.$class.'.php')) {
include PATH.'some'.$class.'.php';
} else {
die('class '.$class.' not found');
}
}
spl_autoload_register("some_class");
spl_autoload_register-
What exactly does it mean? Let's try to understand it with the help of an example:
function Autoloader($className)
{
require_once $path.$className.'.php';
}
spl_autoload_register('Autoloader');
$myObj = new MyClass();
Here we have just instantiated a class named as “MyClass” with out specifying include or require statements.
Is not it cool ? So how have we done it ? Let’s dig in.
Here we have registered a function named as “Autoloader” with spl_autoload_register() function. When, we instantiate “MyClass”, class name(MyClass) is passed by PHP to “spl_autoload_register()”, where registered function picks it up as a parameter. And in defination of Autoloader function, we have included the path of that class file.
This is simply great!! Just think of a situation, where you are having couple of classes and all of them are not going to be used in each case OR on each page. In such cases, spl_autoload_register() can be a real saviour for you.
You just need to instantiate and spl_autoload_register() will take care of rest.
Instead of registering a function you can also do it as follow:
spl_autoload_register(function ($class) {
include 'classes/' . $class . '.class.php';
});
But this chunk of code works only with PHP >= 5.3.0
You can consider spl_autoload_register() as a replacement of __autoload(), which can be defined as:
__autoload() — Attempt to load undefined class
Let’s try to understand with the help of an example.
<?php
// we've writen this code where we need
function __autoload($classname) {
$filename = "./". $classname .".php";
include_once($filename);
}
// we've called a class ***
$obj = new myClass();
?>
But it has a demerit that you need to write this function in each of your class file.

Unable to access global variable in included file

I am having an unexpected issue with scope. The include documentation (also applies to require_once) says the required file should have access to all variable at the line it was required.
For some reason I am not able to access a class instantiated with global scope inside a function that was required in.
Would anyone know why? I am obviously missing something.
I got it working through a reference to $GLOBALS[], but I still want to know why it is not working.
UPDATE:
The error I am getting is:
Fatal error: Call to a member function isAdmin() on a non-object in <path>.php on <line>
Code:
$newClass = new myClass();
require_once("path to my file");
----- inside required file -----
function someFunction() {
$newClass->someMethod(); // gives fatal error. (see above).
}
Functions define a new scope, so inside a function you cannot access variables in the global scope.
Variable Scope
within user-defined functions a local
function scope is introduced. Any
variable used inside a function is by
default limited to the local function
scope
About included files, the manual states:
When a file is included, the code it
contains inherits the variable scope
of the line on which the include
occurs.
So if you include something in a function, the included file's scope will be that of the function's.
UPDATE: Looking at your code example edited into the question, global $newClass; as the first line of the function should make it working.
$newClass = new myClass();
require_once("path to my file");
----- inside required file -----
function someFunction() {
global $newClass;
$newClass->someMethod();
}
Be aware though that using global can quickly make your code more difficult to maintain. Don't rely on the global scope, you can pass the object to the function as a parameter, or use a Singleton/Registry class (some tend to argue against the latter, but depending on the case it can be a cleaner solution).
The included code doesn't have a scope different than the code surrounding it. For example:
function a() {
echo $b;
}
This will fail even if echo $b is in an included file. If you replace the above with:
function a() {
include 'file.php';
}
... and file.php contains:
echo $b;
... then it's the same thing as if you wrote:
function a() {
echo $b;
}
Think of it this way: whenever you use include / require, the contents of the included file is going to replace the include / require statement, just as if you removed the statement and pasted the contents of the file in its place.
It doesn't do anything else as far as scope is concerned.

using require inside a class

i want to make a "loader class" that will require selected files.
so i just can call eg. loader::load('systemLibraries, 'applicationLibraries').
so inside this load() method i will use require. but i have tried this and it seems that the files required can't be used outside the class.
how can i make it globally accessed?
This should work fine:
class Loader{
function load($class_name)
{
require($class_name ".php");
}
}
Loader::load("MyClass");
$class = new MyClass;
Given that MyClass is in "MyClass.php"
This on the other hand, won't work
class Loader{
function load($class_name)
{
require($class_name ".php");
$class = new $class_name;
}
}
Loader::load("MyClass");
$class->doSomething();
If include.php looks like this
$var = "Hi";
You can't do this:
Loader::load("include");
echo $var;
As there are scope issues.
You are going to need to give us more information on exactly what you are trying to access.
Yes, as Chacha pointed out, make sure that you create the instance of the classes outside of your loader class. And since you have used the term system libraries which are usually always needed by the system, you can use the __autoload magic function to included them all automatically for you.

How can I initiate a PHP class and use it in several files?

I am stumped right now. In my last post about this question the answer was to use a singleton to make sure an object is only initiated 1 time but I am having the opposite problem.
If I have a file called index.php and then I include these files into it, class1.php, class2.php, class3.php, class4.php.
In index.php I will have,
<?PHP
$session = new Session();
require_once '/includes/class1php';
require_once '/includes/class2.php';
require_once '/includes/class3.php';
require_once '/includes/class4.php';
?>
then in all 4 of the test files I will try to access a method called get() from the session class, assume the session class file is already included into the index.php page as well.
Now if I try to use...
$testvar = $session->get($var1);
in any of the test class files I will get this error
Fatal error: Call to a member function get() on a non-object
the only way the code works without an error is if I use
$session = new Session();
in every file.
How can I fix/avoid having to initaite the class in every file when it is already initated in the index.php file?
the goal is to let me initiate a class in 1 file like index.php and then include the class files into that page, the catch is most of the classes use methods from other classes so would be nice if I didn't have to initiate every class in every file
Without seeing the code it's hard to tell, but I think I can make some assumptions. correct me if I'm wrong:
EDIT: So post your source so we can stop speculating
1) The files you are including are class files. in other words, they contain something like:
class a
{
function a(){}
function b()
{
}
}
2) You aren't trying to execute code in the class files, at load time, but at some later time by instantiating them
i.e.
require("class.a.php");
$myA = new a();
$a->b();
If you are trying to reference your session variable inside those classes, then you have a scope issue. A variable declared outside a class definition can't be used inside the class, unless it is declared as a global var inside the class.
class a
{
function a(){}
function willFail()
{
$session->doSomething(); //fails
}
function b()
{
global $session;
$session->doSomething(); //succeeds
}
}
Even then, you probably don't want to do that, but instead you should pass in your session as a variable if the class needs access to it:
class a
{
function a(){}
function b($session)
{
$session->doSomething(); // yay!
}
}
You could have a base class they all all extend from
Example
class test1 extends Base {
public function doSomething() {
$this->session->get('something');
}
}
class Base {
protected session;
public function __construct() {
$this->session = new Session();
}
}
You're kind of thinking about it backwards. Any file that will use the session object will need to include the file containing that class definition. The alternative is to use __autoload to pull the class in:
function __autoload($classname)
{
if ($classname == 'Session')
{
include_once 'Session.php';
}
}
EDIT : you'll need to put the file containing that autoload into every file that will use it.

Categories