I am just learning OOP with PHP and I am trying to get my head around this:
I am using php version 5.6.10 on my MAMP installation if that makes a difference??
This the code I have:
<?php
class Baddie {
public $evilness = 10;
}
class Boss extends Baddie {
public $evilness = 50;
public function changeEvilness($value)
{
//$this->$evilness = $value; Had this, which was a typo
$this->evilness = $value;
}
public function __destruct() {
echo "You beat the boss!";
}
}
$ganon = new Boss;
?>
//Note the code is from Rob Percivals Udemy course, hence the gaming references.
So when I call a new instance of the Boss class, it gets destroyed automatically. This prevents me from changing the "evilness" of the boss.
How do I change the code, or maybe a php setting that the destruct() is not called automatically, but only with the unset() function so that I can call other methods from with that class?
Changes
I have updated the typo error, but the answers provided still apply.
I tested the code below and it works? You have to change $this->$evilness to $this->evilness in the changeEvilness function
<?php
class Baddie {
public $evilness = 10;
}
class Boss extends Baddie {
public $evilness = 50;
public function changeEvilness($value)
{
$this->evilness = $value;
}
public function __destruct() {
echo "You beat the boss!";
}
}
$ganon = new Boss;
echo $ganon->evilness ."\n";
$ganon->changeEvilness(1337);
echo $ganon->evilness ."\n";
?>
this outputs:
50
1337
You beat the boss!
So as you can see, the constructor, changeEvilness() function and the destructor all get called.
Also it's good to know that the php process ends when the last line of code is reached. So because you have nothing after the $ganon = new Boss, it will stop the php process and call the destructor.
Related
I am rather new to using PHP OOP and attempting the following:
$this->array = new array();
$this->array[1] = new myClass($UniqueName);
Within myClass I have a loop sending a message to the console every 5 seconds displaying the unique name. While the application is running new instances of the myClass will be created ( 1, 2, 3, 4, etc..), older ones become irrelevant and should be removed.
When I unset an array object the loop from that class keeps sending messages to the console.
unset($this->array[1])
My concern is that the class is not really gone wasting resources. As for the loop I could manually cancel it before running unset but it seems like its hiding my problem from view.
Hopefully this makes sense and someone can help me understand if this is possible.
Solution to my own problem. Here is a stripped down version I am testing with. The Issue is with the timer within myClass. It seems that because the timer is still running the class will not destruct. This has just been wishful thinking on my part that it would also terminate. Removing the timer allows the destruct to occur.
class myClass extends DefaultConfig {
public $fileName;
public $logLevel;
public $loop;
public $config;
public $db;
function __construct ($loop, $db, $config) {
$this->fileName = 'myClassObject';
$this->logLevel = 3;
$this->loop = $loop;
$this->config = new Config($config);
$this->HB();
}
function __destruct() {
print "Destroying " . $this->fileName . "\n";
}
public function HB(){
$this->loop->addPeriodicTimer(1, function($timer){
$this->Logging(3, $this->fileName, $this->logLevel, "Server Message");
});
}
}
I am trying to add functions to class from a separate file, I wonder if this could be possible!
$mClass = new MyClass();
$mClass->new_Functions[0](10); // Is there a way to have it in this form?
class myClass
{
private $Pvar = 5;
$new_Fcuntions;
function __construct()
{
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
}
}
[additional.functions.php] file
function operate($y)
{
return $this->Pvar * $y;
}
----- Edited ------- as it wasn't clear!
"additional.functions.php" is a module and there will be multiple modules to be added to the application, and every module could have more than single function and modules could call one another!
additional.functions.php [module file]
function operate($y)
{
return $this->Pvar * $y;
}
function do-more($foo)
{
return $this->operate(20) + $foo;
}
another.functions.php [another module]
function do-another($foo)
{
return $this->do-more(30) - $foo;
}
function add($foo, $bar)
{
return $foo + $bar;
}
appreciate every participation, its been a while since I am trying to maneuver around with it!
Is this possible or should I give up!
It looks to me like you are looking for Traits, which are a new feature as of PHP 5.4.0. Using traits, you can have snippets of code "mixed in" to other classes, a concept known as "horizontal reuse".
If you are not looking for traits, it's possible that you could do what you wanted with Runkit, however I would suggest staying as far away from it as possible, if you are not genuinely interested in PHP internals as well.
In any event, whatever you are trying to do is very interesting
I got it to work with dependency injection. The pvar has to be public or create a __get method to return the private variable. I also used the function name because it seems cleaner to me to use it via name rather than it's position in the list but if you want to keep that then just put $key where you see $value from the line: $this->function_list[$value] = ...
function operate($y, $that)
{
return $that->Pvar * $y;
}
class Example {
public $function_list = array();
private $Pvar = 5;
public function __construct()
{
$list = get_defined_functions();
$that = $this;
foreach ($list['user'] as $key => $value) {
$this->function_list[$value] = function() use ($value, $that) {
print call_user_func_array($value, array_merge(func_get_args(), array($that )));
};
}
}
public function __get($key)
{
if (isSet($this->$key)) {
return $this->$key;
} else {
throw new \Exception('Key "'.$key.'" does not exist');
}
}
}
$Ex = new Example();
$Ex->function_list['operate'](10);
If you want to extend MyClass from your modules (and not to initialize it, like in your example code), than you could do it in a way like this:
<?php
namespace modules\MyModuleA;
class MyClassExtension
{
private $MyObject;
public function __construct(\MyClass $MyObject)
{
$this->MyObject = $MyObject;
}
public function doSomething($anyParameter)
{
return $this->MyObject->doSomethingElse($anyParameter * 5, 42, 'foo');
}
}
And MyClass:
<?php
class MyClass extends \Extensible
{
// some code
}
abstract class Extensible
{
private $extensions = [];
public function extend($extension)
{
$this->extensions[] = $extension;
}
public function __call($methodName, $parameters)
{
foreach ($this->extensions as $Extension) {
if (in_array($methodName, get_class_methods($Extension))
return call_user_func_array([$Extension, $methodName], $parameters);
}
throw new \Exception('Call to undefined method ' . $methodName . '...');
}
public function hasExtension($extensionName)
{
return in_array($this->extensions, $extensionName);
}
}
And put it all together:
<?php
$moduleNames = ['MyModuleA', 'MyModuleB'];
$MyObject = new \MyClass;
foreach ($moduleNames as $moduleName) {
$className = '\\modules\\' . $moduleName . '\\MyClassExtension';
$module = new $className($MyObject);
$MyObject->extend($module);
}
// Now you can call a method, that has been added by MyModuleA:
$MyObject->doSomething(10);
You should add an interface for the extension classes of course...
The problem is: What happens if any code in your application calls a method of $MyObject, that is not there, because the module has not been loaded. You would always have to check if ($MyObject->hasExtension('ModuleA')) { ... }, but, of course, the application shouldn't be aware of any module. So I would not design an application in such a way.
I would suggest to use traits (mix-ins). See PHP reference
If you can have another class in that file instead of file with functions
- the best solution will be Traits
http://php.net/manual/en/language.oop5.traits.php
or using inheritance
If you move that code to class you can avoid a lot of unnecessary code. I mean:
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
It'll be e.g.:
class myClass extends MyBaseClassWithMyAwesomeFunctions
{
private $Pvar = 5;
}
Maybe this approach helps you:
In the files with the additional functions, don't define named functions, but return a closure, that expects (at least) the object (instance of MyClass) as parameter:
<?php
// additional.functions.php
return function ($myObject) {
$Object->multiplyPvar($myObject->getTheNumber());
$Object->doSomethingElse(42, 'foo');
};
The client, that builds MyClass collects those functions from the files into the array:
<?php
$files = [
'/path/to/my/additional.functions1.php',
'/path/to/my/additional.functions2.php'
];
$initFunctions = [];
foreach ($files as $path)
$initFunctions[] = include $path;
$MyObject = new \MyClass($initFunctions);
The constructor then calls those functions:
<?php
class MyClass
{
public function __construct(array $additionalInitFunctions)
{
foreach ($additionalInitFunctions as $additionalInitFunction)
$additionalInitializerFunction($this); // you can also add parameters of course
}
}
This way the class keeps very well testable as well as the function files. Maybe this could help you in any way. You should never ever think about modifying the internal (private) state of an object directly from any code from outside of the class. This is not testable! Think about writing tests before you implement your code (called "test driven development"). You will see, it is not possible to test a class, if you allow any code outside of that class to modify the internal (private) state of the class instance. And you don't want to have this. If you change some internal implementation detail in your class without breaking the unit test of that class, you will anyways probably break some code in any of your additional.functions.php files and no test will tell you: "Hey: you've broken something right now".
I have a class that executes eval statements within a (very large) loop. The eval statements are database stored code (mixed html & php) that needs to be processed. There is however is a serious performance issue because of this.
Instead of parsing the eval-statement every time in the loop, I would like to create a dynamic methods from the available eval-codes coming from the database within my class.
I thought of the following pseudocode so that the eval code is converted to a method:
class foo
{
private $test=3;
public function doloop()
{
for($i=0;$i<5;$i++)
{
$string="echo 50 * \$this->test.'<br>';";
$func="evalcode_001";
if(!isset(${$func}))
{
${$func}=create_function('',$string);
}
${$func}();
}
}
}
$obj_foo = new foo();
$obj_foo->doloop();//must output '150<br>150<br>150<br> .....'
However when running it I get the error message "Using $this when not in object context in ...". So obviously I didn't really create methods within the class.
So my question is:
How do I create a method for a class dynamically and assign code for the given method. Basically I want to be able to do:
$obj_foo->evalcode_001();
in the above example.
Help appreciated
PS I am using PHP5.4
If you can change $this->test inside string (or write extra preg_match) you can use:
<?php
class foo
{
public $test=3;
public $functions = array();
public function doloop()
{
for($i=0;$i<5;$i++)
{
$string="echo 50 * \$this->test.'<br>';";
$func="evalcode_001";
if(!isset($this->functions[$func]))
{
$string = str_replace('$this','$object', $string );
$this->functions[$func]= create_function('$object',$string);
}
$this->functions[$func]($this);
}
}
public function __call($name, $arguments) {
if (isset($this->functions[$name])) {
return $this->functions[$name]($this);
}
}
public function otherMethod() {
echo "test";
}
}
$obj_foo = new foo();
$obj_foo->doloop();//must output '150<br>150<br>150<br> .....
$obj_foo->evalcode_001();
$obj_foo->otherMethod();
However as other said I wouldn't like to use anything like that in my real script
I think I have more or less managed to get a grasp on OOP/Inheritance, and the basics of method chaining I think I understood as well. However I am still confused about how to actually use some of it.
I wanted to do something that I've seen when working with Magento before:
In Magento, I've seen some sort of "selector-function" used in method chaining. It's a little hard to put into words, but it was something along the lines of:
$categoryName = Mage::getModel('catalog/category')->load($categoryId)->getName();
It's the load($categoryId) part that interests me, as in, a function that selects some instance of something and allows me to run a function on that specific instance.
Now, I am writing a module that allows me to configure certain promotions on our website. Seeing as we'll have many different promotions and I want them to be easily configurable and modifiable, I wanted to do something similar.
So, if I wanted to be able to do something like this:
$prm = new Promotion();
$prm->addPromo('xmasPromo');
$prm->addPromo('nyPromo');
$prm->getPromo('xmasPromo')->setName('Promotion for Xmas!');
$prm->getPromo('nyPromo')->setName('Promotion for New Years!');
echo $prm->getPromo('xmasPromo')->getName(); // returns: Promotion for Xmas!
echo $prm->getPromo('nyPromo')->getName(); // returns: Promotion for New Years!
How would the class definition for that have to look like?
This may be much more simple or much more complicated than I anticipate. In either case, thanks a lot!
Edit:
So I did some testing around with the info deceze gave me, but I'm still confused.
Bad naming and putting 2 classes in 1 file aside, here's what I did:
class file:
class Promotion {
private $__arr = array();
public function addPromo($name) {
$this->__arr[$name] = new Promo();
}
public function getPromo($name) {
$this->__arr[$name];
}
}
class Promo {
private $name;
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $name;
}
}
and the run file:
require_once 'class.php';
error_reporting(E_ALL);
$prm = new Promotion();
$prm->addPromo('xmasPromo');
$prm->addPromo('nyPromo');
$prm->getPromo('xmasPromo')->setName('Promotion for Xmas!');
$prm->getPromo('nyPromo')->setName('Promotion for New Years!');
echo 'X: '.$prm->getPromo('xmasPromo')->getName(); // returns: Promotion for Xmas!
echo "\n";
echo 'N: '.$prm->getPromo('nyPromo')->getName(); // returns: Promotion for New Years!
This gives me Fatal error: Call to a member function setName() on a non-object in /var/www/test/index.php on line 11.
But why? Shouldn't getPromo() give me back the object?
Thanks again..
Thanks to the great guys here, it works now. In case anyone were to pass by here with the same or a similar question, here's the final, working code:
Classes:
class Promotion {
private $__arr = array();
public function addPromo($name) {
$this->__arr[$name] = new Promo();
}
public function getPromo($name) {
return $this->__arr[$name];
}
}
class Promo {
private $name;
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
Test file:
require_once 'class.php';
error_reporting(E_ALL);
$prm = new Promotion();
$prm->addPromo('xmasPromo');
$prm->addPromo('nyPromo');
$prm->getPromo('xmasPromo')->setName('Promotion for Xmas!');
$prm->getPromo('nyPromo')->setName('Promotion for New Years!');
echo 'X: '.$prm->getPromo('xmasPromo')->getName(); // returns: Promotion for Xmas!
echo "\n";
echo 'N: '.$prm->getPromo('nyPromo')->getName(); // returns: Promotion for New Years!
Method chaining is really simple, all it does is use one particular element of PHP's syntax:
When a function returns an object, you can directly continue with -> after that function.
The longhand version can be:
$bar = $foo->bar();
$baz = $bar->baz();
echo $baz;
$foo->bar() returns an object ($bar) which has a method baz(), and that method returns some value ($baz). This can be written in shorthand like so:
echo $foo->bar()->baz();
$foo->bar() still returns an object which has a method baz(), so you can directly call it without assigning it to an intermediate variable. Maybe this makes it more obvious:
echo ( $foo->bar() )->baz();
You're calling the baz() method on whatever $foo->bar() returns.
$prm->getPromo('xmasPromo')->setName('Promotion for Xmas!');
As such, in your above case, all you need to do is to return an object which has the method setName from getPromo. I would assume getPromo is supposed to return some object of, say, the Promo class. If the Promo class has a method setName, you're all set.
If you want to chain methods you just need to always return the object like this
class Chain {
public function firstChain() {
//do something
return $this;
}
public function secondChain() {
//do some stuff
return $this;
}
}
Than when you have an instance of the class you do like this:
$obj = new Chain();
$obj->fistChain()->secondChain();
I need to call a static function from an object using the Singleton design, but using a variable as the class name.
The best way, $class::getInstance();, is only available in PHP 5.3, and the other way I found, call_user_func(array($class, 'getInstance'));, results in the maximum execution time being breached. Does anyone know why this is happening, or of a way for this to work / a workaround?
I know that this is not the best way for things to be done, and the Singleton design pattern would not be my first choice, but unfortunately it's not up to me.
Thanks in advance to anyone who contributes :)
I include the rest of the code involved:
abstract class Library
{
protected function __construct(){}
final private function __clone(){}
final public static function &getInstance()
{
static $libs = array();
$lib = get_called_class();
if(!isset($libs[$lib])) $libs[$lib] = new $lib();
return $libs[$lib];
}
}
public function &loadLibrary($lib)
{
// Filter $lib, and load the library class file...
// Following only works in PHP 5.3
// return $lib::getInstance();
// Following results in maximum execution time being breached.
return call_user_func(array($lib, 'getInstance'));
}
}
$someLibrary =& loadLibrary('someLibrary');
someLibrary.php:
class someLibrary extends Library
{
protected function __construct(){}
// Code...
}
Soulmerge make a valid point saying that get_called_class() is only in PHP 5.3, and therefore I must be using it, but alas, I just cheat my way round things as I usually do (Thanks to Chris Webb from http://www.septuro.com/ for the code - far too complex to be any of my own!).
if(!function_exists('get_called_class'))
{
class classTools
{
static $i = 0;
static $fl = null;
static function get_called_class()
{
$bt = debug_backtrace();
if(self::$fl == $bt[2]['file'].$bt[2]['line']) self::$i++;
else {
self::$i = 0;
self::$fl = $bt[2]['file'].$bt[2]['line'];}
$lines = file($bt[2]['file']);
preg_match_all('/([a-zA-Z0-9\_]+)::'.$bt[2]['function'].'/', $lines[$bt[2]['line']-1], $matches);
return $matches[1][self::$i];
}
}
function get_called_class()
{
return classTools::get_called_class();
}
}
I shall go over all my code again, as there must be a loop somewhere. Back to the drawing board I go :(
You should start by determining what it is that takes you into an infinite loop. Does your constructor (someLibrary::__construct()) have any code that directly/indirectly calls Library::getInstance(), for example?
EDIT get_called_class() was introduced in PHP 5.3, so if your code actually works, you're already running 5.3
you could try to solve this with eval().
To get you an idea:
$theVar = "relvantClassName";
$someObject = eval($theVar::getInstance());
$result = $someObject->performAction();