I am looking for a better understanding of how static methods work in php. I have been reading the article on the php manual site about static keyword in releation to methods, and class objects and I am curious about something.
Lets say I have this class:
class Something{
protected static $_something = null;
public function __construct($options = null){
if(self::$_something === null && $options != null){
self::$_something = $options
}
}
public function get_something(){ return self::$_something }
}
So you instantiate this on index.php, so you do something like:
$class_instantiation = new Something(array('test' => 'example'));
Great, at this point $_something contains an array of key=>value, on this same page we can do:
var_dump($class_instantiation->get_something()); // var dumps the array we passed in.
BUT
If we now create sample.php and do:
$class_instantiation = new Something();
var_dump($class_instantiation->get_something());
We get null back (I assume you went to index.php, instantiated the class and passed in the array, saw the var_dump THEN navigated to sample.php. It is understandable how this would return null if you only went to sample.php without first going to index.php.)
I assumed that static methods are "saved across all instances of the class", thus I should be able to instantiate the class with or with out an object passed into the constructor, assume that something is there and get back my array we created on index.php
So my question is:
How does static methods really work in terms of classes? Is there a way to do what I am trying to do with out the use of third party tools if I am just passing objects around?
The static property is static across the same PHP execution. If you run it on index.php, then at the end of execution it is destroyed. On sample.php it will be a new execution. This works as you expect (same execution):
//index.php
class Something{
protected static $_something = null;
public function __construct($options = null){
if(self::$_something === null && $options != null){
self::$_something = $options ;
}
}
public function get_something(){ return self::$_something; }
}
$class_instantiation = new Something(array('test' => 'example'));
var_dump($class_instantiation->get_something());
$class_instantiation2 = new Something();
var_dump($class_instantiation2->get_something());
Both objects dump:
array(1) {
["test"]=>
string(7) "example"
}
static in PHP also means that you could access to property/method without instatiating the class. It's hard to keep static variables across the same PHP execution as, often, your execution will end with a server response but as AbraCadaver says, they act as you expect within the same execution (same request, read that way)
Related
I am testing a class, let's call it ClassUnderTest using another class, let's call it OtherClass. In my Test I do:
$OtherClassStub = $this->createStub(OtherClass::class);
$OtherClassStub->method(...)
->willReturn(...);
$ClassUnderTest->otherClass = $OtherClassStub;
That works. But when the $ClassUnderTest calls new OtherClass(), the original OtherClass class is created instead of the stub.
How can I achieve that every possible instance of OtherClass in the context of the test is replaced by the stub?
From your description I infer that in principle you have something like this:
class OtherClass {
protected function someMethod(): bool
{
// determine $x ...
return $x;
}
}
class ClassUnderTest {
public OtherClass $otherClass;
public function methodToBeTested(): bool
{
$otherClass = new OtherClass();
return $otherClass->someMethod();
}
}
class ClassUnderTestTest extends TestCase {
public function testMethodToBeTested(): void
{
$otherClassStub = $this->createStub(OtherClass::class);
$otherClassStub->method('someMethod')
->willReturn(true);
$classUnderTest = new ClassUnderTest();
$classUnderTest->otherClass = $otherClassStub;
$result = $classUnderTest->methodToBeTested();
$this->assertTrue($result);
}
}
Now the assertion in your test may hold or it may fail. Why? Because you are not calling the method you stubbed on the $otherClassStub. Instead you instantiate a new $otherClass object in the method you're testing (or somewhere down the line).
Either your ClassUnderTest should always use the OtherClass object from the ClassUndertTest::otherClass attribute (assuming that's why you put it there in the first place).
Or you could use some other form of dependency injection, e.g. by using a framework like Symfony or Laravel. (In the case of Symfony you can even use only the DependencyInjection Component, no idea if that's possible with Laravel, too.)
The simple answer to your actual question is: you cannot change the behaviour of the new keyword. Calling new on a class will always instantiate a new object based on exactly that class, unless the constructor of that class defines something else.
(You might want to get the concept of classes and objects straight, your code example as well as your question seem to indicate that you're not quite clear on that. Maybe reading up on that as well as on the concept of dependency injection will help you.)
Perhaps a solution to your problem is presented here:
How to Build a PHP Plugin Module System
This is one way to load classes as plugins and they can be called from each other. With modifying this system a bit, you can create as many "new OtherClass()" as you like from your code and still access everything from other classes. If you want multiple instances of a class, perhaps modify it into this direction:
function load ($module,$instance) {
if (isset($this->$module->$instance)) { return true; }
From above link:
<?php
class Core {
// (A) PROPERTIES
public $error = ""; // LAST ERROR MESSAGE
public $pdo = null; // DATABASE CONNECTION
public $stmt = null; // SQL STATEMENT
public $lastID = null; // LAST INSERT/UPDATE ID
// (B) LOAD SPECIFIED MODULE
// $module : module to load
function load ($module) {
// (B1) CHECK IF MODULE IS ALREADY LOADED
if (isset($this->$module)) { return true; }
// (B2) EXTEND MODULE ON CORE OBJECT
$file = PATH_LIB . "LIB-$module.php";
if (file_exists($file)) {
require $file;
$this->$module = new $module();
// EVIL POINTER - ALLOW OBJECTS TO ACCESS EACH OTHER
$this->$module->core =& $this;
$this->$module->error =& $this->error;
$this->$module->pdo =& $this->pdo;
$this->$module->stmt =& $this->stmt;
return true;
} else {
$this->error = "$file not found!";
return false;
}
}
}
ps. thank you for the mod, who made me work a bit more to keep this answer online. the answer is so much better now.
Before anyone says anything. I've been to many articles, and I just can't wrap my head around the purpose of a constructor.
I've found an example on a site. Here's the code:
<?php
class dogtag {
public $Words;
}
class dog {
public $Name;
public $DogTag;
public function bark() {
print "Woof!\n";
}
public function __construct($DogName) {
print "Creating $DogName\n";
$this->Name = $DogName;
$this->DogTag = new dogtag;
$this->DogTag->Words = "My name is $DogName. If you find me, please call 555-1234";
}
}
class poodle extends dog {
public function bark() {
print "Yip!\n";
}
}
$poppy = new poodle("Poppy");
print $poppy->DogTag->Words . "\n";
?>
It echoes out the following:
Creating Poppy My name is Poppy. If you find me, please call 555-1234
May someone please explain this code to me like I'm 5. I just don't get it.
A constructor is used to do any initial process required once a new class object was initiated. Ok so that was pretty fancy right? Lets break down what that means with an example. Lets first make a class, and inside that class put some variables, some functions, and a constructor! (It's easier for me to explain the concept behind a constructor with a simpler class. babysteps.)
<?php
class myInfo
{
protected $limit;
public function __construct($limit = 10)
{
$this->limit = $limit;
}
public function awesomesauce() {
//...some random code...
}
}
$variable = new myInfo();//initiating an instance of class myInfo
?>
The code $variable = new myInfo(); is initiating an object of type myInfo. When that initiation happened, the php code knows that right away, the constructor function public function __construct($limit = 10) has to be called. In this case, the value of $limit is made to be 10. However, if I later on do the following code:
$variableTwo = new myInfo(20);
The variable passed inside the parenthesis would be passed directly into the constructor function.
So depending on the specific situation I could either pass no variables when creating an instance of type myInfo, in which case the constructor function would use the default value of ($limit = 10) or I could pass a value. If php did not have the ability to use constructors, I would literally have to manually change that variable with a new line of code every time I initiated a new object of class myInfo. Now this is just a simple example. Imagine if you need to not only initiate values, but run functions that work with API's somewhere else on your server. You can definitely see the benefit of having this happen automatically instead of being forced to write multiple lines of code every single time an object is initiated.
Let me know if that helped.
I'm building an API service and have a parent class:
classAPI {
public responseCode = "";
public responseMessageLog ="";
function run{
// here I call my user auth class
$oUser = new classUser(...);
}
}
Inside my classUser I do a bunch of stuff and then write a bunch of variables: responseMessageLog (which is running log of where the script went) and responseCode (which is set to 0 or 1 or whatever depending on success or failure or warning).
I need to access my responseCode and responseMessageLog variables from within my User class and my parent API class, but I don't really want to be passing these variables into each child class and then passing them back. I would like it that when I update the variable in the child class it updates everywhere in all my class.... kind of like a global variable would... but I know that's not a good idea.
How have others stopped passing variables down the rabbit trail of classes.
in this class I
Passing dependencies isn't a rabbit hole you want to avoid--it makes for more testable code. However, you don't need to pass every property, you can pass the parent object.
In your case just pass the classAPI object into the constructor of the classUser and in the constructor assign it to property. The classAPI properties are public so you can access them in an instance of classUser.
ClassAPI {
public $responseCode = "";
public $responseMessageLog ="";
public function run{
// here I call my user auth class
$oUser = new ClassUser($this, ...);
}
}
ClassUser {
public $myClassApi = null;
public function __construct(ClassAPI $myClassApi) {
$this->myClassApi = $myClassApi;
}
public function someFunction() {
echo $this->myClassApi->responseCode;
}
}
Added notes:
In case it comes up in another answer, don't use static properties to do what you're trying to do.
Capitalize your class names.
In production code I might add an initialization function in ClasUser instead passing the ClassAPI directly into the constructor.
I'm currently working on a project where I have to work with huge arrays. With huge, I mean 1k elements or more. Since these are a lot of arrays and i sometimes mess things up, I decided to create a class with static functions so i can call the functions which would make the entire project easier to read. This is what I currently have:
ArrayAccess.class.php:
require "dep/arrays/elements.php";
class ArrayAccess {
public static function get_value_from_element($element) {
return $elements[$element];
}
}
elements.php:
<?php
$elements = array(
"sam" => 6, ... and so on ...
I simply want to be able to use ArrayAccess::get_value_from_element($element) in my project. It is so much easier to read than all these indexes everywhere. However, the array is defined in the elements.php file - I can't use that in the class.
So how can I access the array in my class? Please note, I cannot copy it into the class, the file would be larger than 400k lines, this is not an option.
You can return a value from an include (or require in this case) and store that to a static property the first time the function is called.
elements.php:
<?php
return array("sam" => 6, ...);
DataAccess.php:
class DataAccess {
private static $elements = array();
public static function get_value_from_element($element) {
if(self::$elements === array()) {
self::$elements = require "elements.php";
}
return self::$elements[$element];
}
}
You should also avoid naming your class ArrayAccess, since it already exists in PHP.
In elements.php
<?php
return array( // use return so you can retrieve these into a variable
"sam" => 6, ... and so on ...
Then in the class
<?php
class ArrayAccess {
public static $elements = null; // set up a static var to avoid load this big array multiple times
public static function get_value_from_element($element) {
if(self::$elements === null) { // check if null to load it from the file
self::$elements = require('elements.php');
}
return self::$elements[$element]; // there you go
}
}
If you don't want do the if statement in the getter every time, you should probably find some where else to load the file into the static variable before using the getter.
An alternative is to declare $elements as global in your class:
require "dep/arrays/elements.php";
class ArrayAccess {
public static function get_value_from_element($element) {
global $elements;
return $elements[$element];
}
}
Well,
I have a problem (ok, no real problem, but I wanna try out something new) with creating objects. Actually I have some orders, which contains a list of orderitems.
These orderitems are used and so spreaded in the whole application, and I need a way to create them. The main problem is, I want to be able to create these objects in many different ways.
Actually I do this in the class constructor and check if the argument which is given.
(I'm using php, so there is no overloading support from the language as you surely know :))
A simple and quick Example
class foo {
protected $_data=null;
public function __contruct($bar){
if (is_array($bar)){
$this->_data=$bar;
}
else {
$dataFromDb=getDataFromDatabase
$this->_data=$dataFromDb;
}
}
}
Anyway, if I want to create my object by giving another type of parameter, lets say a xml-document encapsulated in a string I need to put all this stuff in my constructor.
If the process for creating an object is more complicated, I eventually need to create a seperate method for each type, I want to initiate. But this method is only called when this special type is created. (I think you got the problem :))
Another problem comes to mind, if I need more parameters in the constructor to create a concrete object, I have modify all my code, cause the contructor changed. (Ok, I can give him more and more parameters and work with default values, but that is not what I really want).
So my Question is, which pattern fits this problem to solve my creation of a concrete object. I thought about creating a factory for each way I want to create the concrete object. But I'm not sure if this is a common solution to solve such a problem.
IF its only the signature of the constructor changing i would do it like so (a la the Zend Framework universal constructor):
class foo {
// params
public function __construct($options = null)
{
if(null !== $options)
{
$this->setOptions($options);
}
}
public function setOptions(array $options){
foreach ($options as $name => $value){
$method = 'set' . $name;
if(method_exists($this, $method)
{
$this->$method($value);
}
}
return $this;
}
}
And this essntially means all your constructor parameters are array elements with named keys, and anything you want used in this array during initialization you create a setter for and then its automatically called. The down side is the lack of effective hinting in IDEs.
On the otherhand if you want to have specific constructors then i might go with a factory but still use much the same approach:
class foo {
public static function create($class, $options)
{
if(class_exists($class))
{
$obj = new $class($options);
}
}
}
Of course you could alternatively use PHP's reflection to determine how to call the constructor instead of just injecting an arbitrary array argument.
you could simply make it a factory with optional params :)
class Example_Factory
{
public static function factory($mandatoryParam, $optionalParam = null)
{
$instance = new self;
$instance->setMandatory($mandatoryParam);
if ($optionalParam !== null) {
$instance->setOptional($optionalParam);
}
return $instance;
}
public function setMandatory($in)
{
// do something....
}
public function setOptional($in)
{
// do some more...
}
}