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.
Related
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.
Is there a way in PHP to tell (programmatically, obviously) if a given class is an internal class (such as DateTime) or a user class (class MyClass)?
In case you wonder (and I'm sure you do), this is because ReflectionClass::newInstanceWithoutConstructor() throws an exception when used on internal classes, and as I'm writing a library to deep-copy objects, it must skip these internal classes.
Yes, I could just catch the ReflectionException, but this exception is thrown for other reasons as well (such as a non-existing class), and is not thrown for all system classes. so it's not exactly fulfilling my needs.
A cleaner solution than using shell_exec whould be to use reflection:
$reflection = new ReflectionClass('SomeClass');
if($reflection->isUserDefined()) {
// 'SomeClass' is not an PHP internal
}
Instead of an string ('SomeClass') you can also pass an object. For more information lookup Reflection and
ReflectionClass::isUserDefined() in the PHP Manual
Interesting question, one way I can think is by checking the namespace, for example all of your classes would be defined under namespace MyApp and then check:
if(class_exists('\\DateTime')){
continue;
}
Kind of ugly, I know.
Food for thought, based on Дамян Станчев's suggestion:
You could just run a PHP interpreter via shell_exec() that will spew out get_declared_classes(). Capture the output of that, and you should have a "clean" list of system classes.
Extending Mogria's answer, this one should work just fine (don't give me credit for this though, as it was Mogria's answer that got it right ;-)):
function getUserDefinedClasses() {
return array_filter(get_declared_classes(),
function ($class) {
$reflectionClass = new ReflectionClass($class);
return $reflectionClass->isUserDefined();
});
}
You should be able to imitate the reflection behaviour by extending the class you're trying to copy, and overriding the __construct function:
<?php
class MyClass extends ExtendingClass {
public function __construct() {
/* Override default constructor */
}
}
?>
Which could essentially be made dynamic by using eval:
<?php
function newInstanceWithoutConstructor($class) {
$className = $class . "Extended" . rand(0, 99999999);
while (class_exists($className)) {
$className = $class . "Extended" . rand(0, 99999999);
}
eval("class " . $className . " extends " . $class . " { public function __construct() { } }");
return new $className();
}
$newInstance = newInstanceWithoutConstructor("DateTime");
?>
HOWEVER: Using eval can be useful in this case, but also reveals a rather large security-hole if anything user submitted can be submitted in any way to change the contents of $class. If you understand these limitations, and security implications, you should be able to use this.
Can't you use get_declared_classes() in the beginning of your script, store the data in an array and then do an array_diff() with the stored data and the response from get_declared_classes() and check if the class you're checking is in the difference using in_array() ?
This example prints out all the classes with your classes seeming to be at the end of the list. Maybe this can help.
What about storing calling get_declared_classes() data before any autoloading/include/require is made and later checking class name in this storage?
I have been using active record for quite a while, and I wanted a little change of scenery, some developer friends suggested looking into ORM, all of the ORM projects I have looked at require a separate class extending the ORM class.
My question is: is there any way to dynamically create a class using PHP from within a function without eval?
This is what I have:
<?php
class Constructor
{
function new_class($class)
{
$myself = get_called_class();
eval("class {$class} extends {$myself} { }");
}
function say_hi()
{
$class = get_called_class();
echo "Hi, {$class}!";
}
}
$constructor = new Constructor;
$constructor->new_class("Greeter");
$greeter = new Greeter;
$greeter->say_hi(); // Hi, Greeter!
But, my client informs me that eval is blocked on his environment due to him being on shared hosting.
You probably don't want to do that. But as a workaround, you could use the same approach as via eval(), but once you have constructed the string which you would feed to eval you just write it out as a file and include it again.
Something like this:
function my_eval($str)
{
$filename = uniqid().'.tmp';
file_put_contents ($filename, $str);
include $filename;
unlink ($filename);
}
I've written this from memory and not tested it, but I think it should do the trick. Only caveat I'd see right now is that you'd still essentially be doing the same as eval(), and this variant wouldn't allow you to create variables in the same scope as the calling context (although you could use $GLOBALS[] to get around that for global scope variables).
I've got a class I wrote to work with the front end (web browser side) of a shopping cart.
It's fairly simple in that I send the class a product ID that I bury in the URL and then query a database populating the classes variables for use in retrieving the data through some public methods.
To interface with my actual physical web page I have a file I call viewFunctions.php. Wherein I instantiate my class called ItemViewPackage():
<?php
require_once(dirname(__FILE__) . '/ItemViewPackage.php');
$viewObject = new ItemViewPackage($_GET['page']);
So, I have shoppingcartpage.php (the physical url) that requires the file viewFunctions.php that loads my class ItemViewPackage().
The output page shoppingcartpage.php calls functions like get_item_info('title') or get_item_info('price') which in the viewFunctions.php file is made like so:
function get_info($type){
echo $viewObject->get_info($type);
}
Now, right off the bat, this isn't working because, I assume, $viewObject is not global. So I make $viewObject global like so:
function get_info($type){
global $viewObject;
echo $viewObject->get_info($type);
}
But, this doesn't work either, I still get an error for "Call to a member function get_info() on a non-object"
Now, the only thing that works is:
function get_info($type){
$viewObject = new ItemViewPackage($_GET['page']);
echo $viewObject->get_info($type);
}
But, I don't want to re-instantiate my object every time I make a call to this function (which is several times for several bits of information). I'd rather instantiate once at the top of my viewFunctions.php doc and use that object every time I call this function.
Am I going about this completely wrong?
Thanks in advance.
DIAGRAM (hopefully it helps visualize)
What for do you need viewFunctions.php anyway? It's only wrapping the ItemViewPackage. Remove that and use the ItemViewPackage directly, e.g.
// shopping.php
include_once 'ItemViewPackage.php';
$viewObject = new ItemViewPackage($_GET['page']);
<div><?php echo $viewObject->get_info('title'); ?></div>
<div><?php echo $viewObject->get_info('price'); ?></div>
Then you dont have to bother with globals or Singletons. If you dont want a second instance, dont instantiate a second one. It's simple as that in PHP. If there is anything in viewFunctions.php that modifies the output of the $viewObject instance, consider making that into a class and have it aggregate the $viewObject into a property, e.g.
// viewFunctions.php
include_once 'ItemViewPackage.php';
$viewObject = new ItemViewPackage($_GET['page']);
$helper = new ViewObjectHelper($viewObject);
then you can access the $viewObject from within the Helper object with $this->propertyName.
As for reducing load to the database: this is a solved problem. Consider using a cache.
You want the singleton pattern, please see this answer:
Creating the Singleton design pattern in PHP5
This allows you to get an instance of your class in any scope, and it will also be the same instance.
What scope is the $viewObject created in?
Note: that even though it appears to be in the global scope because it is not in a function within the shown file, if the file is included from within a function it will be in that scope...
i.e.
file1.php
include 'file2.php';
function includefile($file) {
include $file;
}
includefile('file3.php');
file2.php
$global = 'this is global';
file3.php
$notglobal = 'this is not';
<?php
require_once(dirname(__FILE__) . '/ItemViewPackage.php');
$viewObject = new ItemViewPackage($_GET['page']);
function get_info($type){
global $viewObject;
echo $viewObject->get_info($type);
}
This should work from viewFunctions.php and any file that includes it such as shopping.php. So from shopping.php we can do either:
echo get_info($type);
or
echo $viewObject->get_info($type)
This alone raises some logical flags in my head. Not sure why you want to wrap the object again.
You can redirect calls to some properties/functions by using __get, __call.
Is there a way to do it for classes?
I would like to convert all mentions of some_class_name in the code to, MY_VERSION_some_class_name (not only for one class, it's a pattern).
This would be easy if methods / properties were the target of this renaming policy.
Can you think of a way to do it for classes in PHP?
Edit: I need this for referencing to different variants of classes in different situations. I need one class name to be resolved to different variants of this class, depending on a condition known at runtime, and persistent through the whole session.
Thanks
p.s.
If you are curious why I want to do this, look at Maintaining variants of an application
You can't convert all mentions of some_class_name in the code to another class. However, you can use variables as class names:
$className = "MyClass";
$obj = new $className;
$className::myMethod();
All you have to do is change the variable and you will be using a different class. If you have to do this for a lot of classes, you might want to create some sort of factory for it.
$factory = System::getFactory();
$obj = $factory->getAuthObj();
Basically, the System class would return a different object based on what class needed to be used for that particular run time.
Aiden's untested approach: variable variables and generating a string:
<?php
$dog = "12";
function getDogClass($myNum)
{
global $dog;
// Do something dynamic here;
$dog = "Dog_" . $myNum;
return "dog";
}
class Dog_13rc1
{
function __construct()
{
printf("Woof!");
}
}
class Dog_12
{
function __construct()
{
printf("Grrrr");
}
}
$myObj = new ${getDogClass('13rc1')}();
?>
As long as the call to getDogClass() returns a string of the name of a variable in scope then you are good.
The output is woof.
This means the only refactoring you need is to find/replace occurences of the class name with that call.
But this is grim. and probably slow. Also, a maintenance/bug-tracking nightmare type of hack.
The magic function you want is __autoload:
function __autoload($class_name) {
// include your special class in here
include $class_name . '.php';
}
Read more here: http://php.net/manual/en/language.oop5.autoload.php