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");
});
}
}
Related
Likely this has already been asked, but nevertheless, here goes. This may fall under best practice or security... I'm not really sure.
In my application, I am using a nested object, that is called in the __construct() function. Sort of like this:
class user {
public $userID = NULL;
public $someObject = NULL;
public function __construct() {
$this->userID = getThisUser();
$this->someObject = new objectBuilder($this->userID);
}
public function getThisUser() {
// ...
}
}
class objectBuilder {
public $buriedVar = NULL;
public function __construct($uid = NULL) {
if( !isset($uid) ) {
$this->buriedVar = setTheObject($uid);
} else {
$this->buriedVar = setTheObject(0);
}
}
public function setTheObject($id) {
// ...
return "random string";
}
}
$tom = new user();
Obviously terrible outline here, but the point is, I can then call $tom->someObject->buriedVar and it'll return "random string".
While looking for a way to nest classes, I noticed no one recommends this as a method for storing objects inside of another object. I'm curious of a few things:
1) Is this insecure?
2) Are the vars inside the nested object exclusive to the call made inside $tom->__construct(), or if I create another object using new objectBuilder() is it overwriting the one inside $tom->someObject? I haven't noticed this, but am not sure how to test for that entirely.
3) Is there something else I'm missing? A best practice reason not to instantiate an object inside a class? I've been using it for years and it works great for what I've done. Is it a speed thing?
1) Is this insecure?
Not inherently, no.
2) Are the vars inside the nested object exclusive to the call made
inside $tom->__construct(), or if I create another object using new
objectBuilder() is it overwriting the one inside $tom->someObject? I
haven't noticed this, but am not sure how to test for that entirely.
This is a fundamental question between class and object. Objects are instances of a class and there can be multiple. The only things that would be overwritten are static properties and methods. You could test it like this:
<?php
$obj1 = new objectBuilder();
$obj2 = new objectBuilder();
if ($obj1 !== $obj2) {
echo "objects are not the same\n";
}
if ($obj1->buriedVar !== $obj2->buriedVar) {
echo "nested objects are not the same either\n";
}
$obj3 = new objectBuilder(1);
if ($obj1->buriedVar != $obj3->buriedVar) {
echo "even the values of two different buried vars with different values are different.\n";
}
if ($obj1->buriedVar == $obj2->buriedVar) {
echo "counter-example: nested variables with the same values set are similar.\n";
}
It helps to know the difference between equality and identity (see this SO post).
3) Is there something else I'm missing? A best practice reason not to
instantiate an object inside a class? I've been using it for years and
it works great for what I've done. Is it a speed thing?
You touched on it briefly. What you should know is that this is not scalable and is difficult to test.
Imagine you're creating a website for dogs.
<?php
class Bio
{
public function __construct()
{
$this->dog = new Dog('Terrier');
}
}
class Dog
{
private $animal = 'dog';
private $noise = 'woof!';
private $breed;
public function __construct($breed=null)
{
$this->setBreed($breed);
}
public function setBreed($breed)
{
$this->breed = $breed;
}
}
What if you want to add a new breed? Well... That's easy enough:
class Bio
{
// ...
public function __construct($breed)
{
$this->dog = new Dog($breed);
}
// ...
}
Cool! You've solved everything.
Except...
One day you want to create a section for cats, because one of your best writers also loves cats, and you sense an untapped market.
Uh oh...
You can refactor the code, of course. But you wrote it a long time ago. Now you have to go in and figure out where everything went. No big deal.. A bit annoying but you fixed it!
But now you have another problem. Turns out that the same author wants to add different traits to the breed. You're surprised this hasn't come up sooner but, hey, it's probably a good thing to have.
Now you need to go in to the Dog object, and the Cat object, and add traits.
Every single time.
On. Every. Bio.
After some reconfiguring, you've created something monstrous like this:
$article1 = new Bio('Terrier', 'dog', ['independent']);
$article2 = new Bio('Persian', 'cat', ['flat-faced']);
//... and so on, and so on
The next time the author asks for something, you fire her and then tear your hair out in a mad rage.
Or, from the beginning, you use Dependency Injection.
<?php
class Bio
{
private $animal;
public function __construct(AnimalInterface $animal)
{
$this->animal = $animal;
}
}
interface Animal
{
public function getType();
public function setBreed($breed);
public function getBreed();
public function setTraits(array $traits);
public function getTraits();
}
abstract class AbstractAnimal implements AnimalInterface
{
private $breed;
private $traits = [];
abstract public function getType();
public function setBreed($breed)
{
$this->breed = $breed;
}
public function getBreed()
{
return $this->breed;
}
public function setTraits(array $traits)
{
$this->traits = $traits;
}
public function getTraits()
{
return (array)$this->traits;
}
}
class Cat extends AbstractAnimal
{
public function getType()
{
return 'cat';
}
}
class Dog extends AbstractAnimal
{
public function getType()
{
return 'dog';
}
}
This pattern requires little to no editing after it has been created.
Why? Because you are injecting the object to nest into the class, rather than instantiating it in the object.
$bio1 = new Bio($dog); $bio2 = new Bio($cat); can always stay like this. Now you just edit the $dog and $cat objects. The added benefit is that these objects can be used anywhere.
But what about utility classes?
(This is where testability comes in. If you haven't worked with unit testing, I recommend reading up on it in the link to PHPUnit below. I'm not going to dwell on how that works as it's off topic).
Dependency Injection is well and good if you have classes that require customization. But what about utility classes that just house various functions?
class Utils
{
public function add($a, $b)
{
return $a + $b;
}
}
You might think that you can call this function safely from the constructor. And you can. However, one day you might create a log method in your Utils class:
public function log($msg)
{
exec("cat '$msg' > /tmp/log.txt");
}
This works just fine. However, when you run tests, your /tmp/log.txt file complains. "Invalid permissions!". When this method is run via your website, log.txt needs to be writeable by www-data.
You could just chmod 777 /tmp/log.txt, but that would mean everyone who has access to your server can write to that log. Additionally, you may not want to always write to the same log when you're testing as when you're navigating through the web interface (Personally, I would find it confusing and cluttering).
PHPUnit and other unit testing services allow you to mock various objects. The problem is that you have classes calling Utils directly.
You have to find a way to manually override the constructor. Look at PHPUnit's manual to find out why this maybe isn't ideal.
So if you're not using Dependency Injection, what do you do?
PHPUnit suggests, amongst other fixes, moving this Utils object instantiation to another method and then stubbing/mocking that method in your unit test (I want to emphasize that this is after recommending Dependency Injection).
So the next best?
public function __construct()
{
$this->init();
}
private function init()
{
$this->utils = new Utils;
}
Now when you unit test, you can create a fake init method and it will be called as soon as the class is created.
In conclusion, the way you are currently instantiating classes is not scalable or easily testable in many real world situations. While it may be all right in limited situations, it is better to get used to the DI (Dependency Injection) pattern, because it will save you lots of headaches in the future.
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.
I have a main file (framework.php) that requires a few classes. For simplicity of my question lets assume only messages with the class Messages and automate with the class Automate. The messages class stores session messages that appear to the user on the frontend whenever they refresh their page .etc. When I add a message in Automate I do so via $msg = new Messages; $msg->add('s', 'Hello World!'); However I keep repeating $msg = new Messages everytime I want to add a $msg, and because this is messy, I want to just call the class once. So I initialize the class in the constructor like so:
class Automate
{
protected $msg;
//public $connection;
public $cke;
public $debug = false;
public $disallow_insert;
public $bImgUp = BACKEND_IMAGE_UPLOAD_PATH;
public $fImgUp = FRONTEND_IMAGE_UPLOAD_PATH;
public function __construct() {
global $disallow_insert;
$this->disallow_insert = $disallow_insert;
$this->cke = (bool) self::ckeCheck();
$this->msg = new Messages();
}
and in my main file I add it $msg = new Messages(); so I can just call the display function echo $msg->display();
However this method does not work (no errors, but still), the only thing that seems to work is when I initialize the $msg = new Messages(); before every add or display. What am I doing wrong?
I should mention I'm using this session based message script.
UPDATE:
I have determined the the messages are getting added by commenting out the clear function in the messages class. It almost seems as though the variable is getting unset before displaying.
I figured it out after researching OOP practices. I made the messages class a singelton via:
public static function getInstance()
{
static $instance = NULL;
if (NULL === $instance) {
$instance = new static();
}
return $instance;
}
and in my classes
var $msg;
public function __construct() {
$this->msg = Messages::getInstance();
}
I figure the messages class needed to be constructed once, not multiple times which is why the function clear kept being called!
I'm new to DI ,using Pimple. Using: php 5.3.5 (wamp), namespaces as well.
I'm refactoring code, using it, but came to a problem (s):
I have my Container that extends from Pimple, lets call it PContainer.php:
class ReuseableContainer extends Pimple{
private function initOutterClass(){
$this['special_location_class'] = '\SpecialLocation';
$this['special_location'] = function($c){return new $c['special_location_class']($c['location_details'],$c['location']);};
}
private function initGlobalFunctions(){
$this['getGeneralDataFromArray'] = function($c){
// returning a function
return function($arr){
foreach ($arr as $key => $value){
// do something
$new_data = $c['general_data_type'];
$new_data->id = $value['id'];
$new_data->name = $value['name'];
}
}
}
public function __construct(){
$this['location_class'] = '\Location';
$this['location_details_class'] = '\LocationDetails';
$this['general_data_type_class'] = '\GeneralDataType';
// define some objects
$this['location'] = function ($c) {
return new $c['location_class']();
};
$this['location_details'] = function ($c) {
return new $c['location_details_class']();
};
$this['general_data_type'] = function ($c) {
return new $c['general_data_type_class']();
};
$this->initOutterClass();
$this->initGlobalFunctions();
}
}
global $container ;
$container = new Pimple();
// embed the SomeContainer container
$container['embed'] = $container->share(function () { return new ReuseableContainer(); });
Ok. So i got a SpecialHelper.php which holds:
final class SpecialLocation{
public $name;
public $location;
public $picture;
public function __construct($location){
$this->location; // dependent on class: Location
}
}
final class SpecialUser{
private $id;
private $location;
public function __construct(\Location $location,$id=''){
$this->id = $id;
$this->location = $location; // $container['embed']['location'];
}
and we got our GeneralHelper.php which holds:
final class Location{
public $lat;
public $lng;
public function __construct($lat='',$lng=''){ $this->lat = $lat; $this->lng = $lng;}
}
final class LocationDetails{
public $id;
public $addresss;
public function __construct($id='',$address=''){$this->id = $id; $this->address = $address;}
}
class GeneralDataType{
public $id;
public $name;
public function getName(){ return $this->name;}
public function getId(){ return $this->id;}
}
and we have our "Special Class" controller, which looks something like this:
final class SpecialController{
public function foor($some_array){
$this->doSomething($some_array);
}
private function doSomething($ret_value){
// do something
$arr = array();
foreach($ret_value as $key => $value){
$something = $container['embed']['getGeneralDataFromArray']($value);
$special_location = $container['embed']['special_location'];
$arr[] = special_location;
}
return $arr;
}
}
Finally we have our main "driver", main.php
require('PContainer.php');
....
...
$some_array = array(....);
$special_controller = new SpecialController();
$special_controller->foor($some_array);
Problems:
1) I had to add initOutterClass function inside ReuseableContainer to decouple the "Special" classes, how could have i decoupled them in a better way? creating a new "special" 9container or something? as EVERYTHING now sitts inside the container.. same goes to the initGlobalFunctions()
2) regarding SpecialHelper.php: i have there SpecialLocation, which one of its properties is a \Location class, i've put it in the constructor , but if i have 20 object properties that are dependent, i must put them all as INPUT params for the constructor?? same goes to the SpecialUser class, it has a $location which if i could i would have made $this->location = $container['embed']['location']; instead of $this->location = $location; resulting in a dependent on the DI! :/
3) I've had to create SpecialHelper.php in a different file, despite wanting to put it in the "special class controller", just so there won't be any unknowns (due to require statement order)
4) MOST importantly: about the "Special class" controller, how do i solve the doSomething method? i must create "Special Location" object inside the loop but i get that $container is unrecognized (despite being global, as of scope probably) but more over it's really dependent! and it's a private function, i don't wish to pass the container to EVERY class i'll use from now on, it isn't IoC right?
Any help is appriciated... i'm trying to understand the best practices..
Thank you
4)Most important: IoC is correct. That an implementation is not correctly working does not reflect the principle of IoC itself.
If you want to use the global $container within a function, then should you use the global keyword within that function. That is how PHP works. Making it static is solving the problem of reference, but does not make a real difference.
An IoC container resolves the dependencies for the caller. The caller does not have to know anything about the internals of the callee - and he doesn't care either. So, there should be some kind of contract by which the exchange of data is regulated. If you have that situation, then you have IoC.
3)That problem is too vague to answer, but imo also not relevant from a practical perspective. Does it work? Ok, good to know. :-)
2)The clue of IoC is the use of contracts. The IoC container is there to connect the caller to the proper contract. The contract resolves to a concrete callee. The callee will return information inline with the contract. The caller understands the answer. Therefor will you need that the input and output in this process is independent of a certain implementation at a certain time. So don't use 20 object properties as input, but use an array or general object instead.
1) I get the idea that you are mixing functional flow (data flow) with technical flow (relationships between classes). An IoC container serves the purpose of the technical flow, it optimizes the dependency in the relationships between classes. For instance, if you want to connect to a database, then might you reuse an existing connection instead of creating new connections all the time. Or if you want to use a special functionality on several moments in your flow, then might you use IoC for that.
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();