Let's say I have this class.
class foo{
function a(){
return $this;
}
}
And I instantiate it as:
$O = new foo();
$O->a()
->a()
->a();
Is there any way to know, in that last function ->a() how many times it was called before?
So, I could output like 'method ->a() has been called twice before this.'
I would like to find out this, without using increment values like declaring a property and then increment it increment it, everytime it is called in a function.
I am just hopping if there is a hidden feature in OOP that can provide for this solution
You can use a static variable inside the method:
class foo{
function a(){
// $count will be initialized the first time a() was called
static $count = 0;
// counter will be incremented each time the method gets called
$count ++;
echo __METHOD__ . " was called $count times\n";
return $this;
}
}
Note that static has a different meaning when used inside a method or function and it has nothing to do with static class members - although it is the same keyword. It means that the variable will be created and initialized only once when the method has been called for the first time.
However, in the example above there is no way to reinitialize that counter. If you want to do that you may introduce a parameter or something like this. Also you may not use a static variable but an object property.. There are tousand ways to do it, tell me your exact application needs I may give a more specific example....
In comments it was suggested to use a decorator for this job. I like this idea and will give a simple example:
class FooDecorator
{
protected $foo;
protected $numberOfCalls;
public function __construct($foo) {
$this->foo = $foo;
$this->reset();
}
public function a() {
$this->numberOfCalls++;
$this->foo->a();
return $this;
}
public function resetCounter() {
$this->numberOfCalls = 0;
}
public function getNumberOfCalls() {
return $this->numberOfCalls;
}
}
Usage example:
$foo = new FooDecorator(new foo());
$foo->a()
->a()
->a();
echo "a() was called " . $foo->getNumberOfCalls() . " times\n";
Related
I don't truly understand how chaining functions work on the values that are returned.
Let's say I have a function that returns a string or array
public static $query;
public static function getArray($arr) {
Database::$query = $arr;
return Database::$query;
}
public function single() {
return Database::$query[0];
}
Why, when I call it can I then not chain a function onto this to affect the string (In this example I was to append ' test' and how would I go about doing this?
Why can I simply not call
Database::getArray(array("test","test2"))->single();
Without getting a Call to a member function single() on array error. But instead, make it return only the first value of the array.
How would I go append doing what I'm trying to achieve here? Why is my logic wrong?
When you call a method, the return value is whatever that method decides to return; the return value doesn't have any automatic relationship with the object you called the method on. For instance:
class A {
public function foo() {
return 'Hello, World!';
}
}
$a = new A;
echo $a->foo();
The value returned is just an ordinary string, just as if foo was a global function not attached to any object.
In PHP, strings (and other "basic types" like arrays) are not objects, so you can't call any methods on them. Even if you could, those methods would be built into the language, and you couldn't just decide that ->single() could be called on any array.
What may be confusing is that some people write methods with the convention that they return an object, known as a "fluent interface", or more generally "chained methods". This is not a feature of the language, just a natural consequence of returning an object from a method:
class A {
public function foo() {
return new B;
}
}
class B {
public function bar() {
return 'Hello, World!';
}
}
$a = new A;
$b = $a->foo(); // $b is a B object
echo $b->bar();
// We can combine this into one line:
echo (new A)->foo()->bar();
There is nothing special about this chaining; it's just that wherever you have an object, you can call appropriate methods on it, just as wherever you have a number, you can do maths with it. Compare with a simple addition:
function foo() {
return 1;
}
$a = foo();
$a = $a + 2;
echo $a;
// We can combine this into one line:
echo foo() + 2;
// Or keep the assignment:
$a = foo() + 2;
echo $a;
The object doesn't know it's being chained - in fact, it shouldn't need to know anything about the code around it, and that's an important part of structured programming.
A common pattern is then to have modifying methods which return the object they just modified, so you can make a series of modifications in one go:
class A {
private $words = [];
public function addWord($word) {
$this->words[] = $word;
// $this is the current object, which is an instance of class A
return $this;
}
public function getString() {
return implode(' ', $this->words);
}
}
$a = new A;
// Calling $a->addWord(...) gives us back the same object
$a = $a->addWord('Hello');
$a = $a->addWord('World');
// Calling $a->getString() gives us back a string
echo $a->getString();
// We can combine this into one line:
echo (new A)->addWord('Hello')->addWord('World')->getString();
Note that you can only refer to $this if you have created an instance of the object (with the new keyword), not in a method declared as static. A static method can still have this kind of pattern, but it will need to return some other object, like new self (a new instance of the current class) or self::$foo (an object created earlier).
it's called fluent interface, if you want to chain methods from same class you have to return this from each of them which you want to call fluently, so your code should look like:
public static $query;
public function getArray($arr) {
Database::$query = $arr;
return $this;
}
public function single() {
return Database::$query[0];
}
after applying changes, the construct Database::getArray(array("test","test2"))->single(); will work, however you may consider renaming method getArray, because as its name suggests, it shouldn't be returning $this, but array
#EDIT
you should change the type of function getArray from public static function to public function to make it work, also your final statement will change to something like:
(new Database())->getArray(array("test","test2"))->single();
however, in this case, I would consider redesigning your class and creating some kind of singleton so that you instantiate Database class only once and store the object somewhere
I'm new to PHP and practicing using static variables. I decided to grab an example that I learnt from C++ and re-write it for PHP (example from the bottom of this article).
There's a class with two private variables (one static), a constructor and a get-method. The constructor assigns the static variable's value to the second private variable, and then increments.
<?php
class Something
{
private static $s_nIDGenerator = 1;
private $m_nID;
public function Something() {
$m_nID = self::$s_nIDGenerator++;
echo "m_nID: " . $m_nID . "</br>"; //for testing, can comment this out
}
public function GetID() {
return $m_nID;
}
}
// extra question:
// static variable can be assigned a value outside the class in C++, why not in PHP?
// Something::$s_nIDGenerator = 1;
$cFirst = new Something();
$cSecond = new Something();
$cThird = new Something();
echo $cFirst->GetID() . "</br>";
echo $cSecond->GetID() . "</br>";
echo $cThird->GetID() . "</br>";
?>
Using the echo test in line 9 to see if m_nID is getting a value I see:
m_nID: 1
m_nID: 2
m_nID: 3
But these values are not being returned by the "->GetID()" calls. Any ideas why?
Edit: both replies so far have solved this, I wish I could "check" them both, so thank you! I'll leave the original code in the question as-is for any future people who have a similar problem
Your background in C++ led up to this issue, which is an easy mistake to make. In PHP, all instance (or object) variables are referenced using $this->, and static (or class) variables with self::. Based on your code:
public function GetID() {
return $m_nID;
}
Access to the private variable $m_nID should be scoped like this:
public function GetID() {
return $this->m_nID;
}
And inside your constructor:
$m_nID = self::$s_nIDGenerator++;
It should have been:
$this->m_nID = self::$s_nIDGenerator++;
Q & A
Why is there no need to put $ before m_nID when using $this->
The above two ways of referencing instance and class variables come with a very different kind of syntax:
$this is the instance reference variable and any properties are accessed using the -> operator; the $ is not repeated for the property names themselves, although they're present in the declaration (e.g. private $myprop).
self:: is synonymous to Something:: (the class name itself); it doesn't reference an instance variable and therefore has no $ in front of it. To differentiate static variables from class constants (self::MYCONST) and class methods (self::myMethod()) it's prefixed with a $.
Extra
That said, $this->$myvar is accepted too and works like this:
private $foo = 'hello world';
function test()
{
$myvar = 'foo';
echo $this->$foo; // echoes 'hello world'
}
class Something{
private static $s_nIDGenerator = 1;
private $m_nID;
public function Something() {
$this->m_nID = self::$s_nIDGenerator++;
}
public function GetID() {
return $this->m_nID;
}
}
It is interesting to note the difference between using self::$s_nIDGenerator on a static variable vs using $this->s_nIDGenerator on a static variable, whereas $this-> will not store anything.
I try to create some sort of setup class, like global values for the page.
The PHP-code
class globals
{
public $page;
public function __construct()
{
}
public function set_page($value)
{
$this->page = $value; // Maybe from a database
}
}
class get
{
public function page()
{
$globals = new globals();
return $globals->page;
}
}
$globals = new globals();
$globals->set_page('My value');
echo get::page(); // Short function to be in a template
Question
My class forget the value I set. Why is that?
Do I have to use global variables?
Is this the correct approach for the problem?
The variable is set on an object, not on a class.
For each class, you can instantiate multiple objects. Each of those have their own variable scope.
Edit:
I forgot to include the easiest, and least verbose solution to your problem. AFAIK, you're looking for a way to check what page you're on. Constants will do just that:
defined('MY_CURRENT_PAGE') || define('MY_CURRENT_PAGE','My Value');
//use anywhere like so:
echo 'Currently on page: '.MY_CURRENT_PAGE;
My class forget the value I set. Why is that?
Quite simple: your page member function isn't static, yet you call it as though it is: get::page(). Even if you were to fix this, you're creating a new instance in the page method, but you're not preserving a reference too it anywhere, so each page call will create a new globals instance, that has nothing set.
Do I have to use global variables?
No, unless you're Really desperate, never use globals
Is this the correct approach for the problem?
No, if it doesn't work, it's not correct (IMHO).
Well, what is, you might ask. There are several ways to go about this:
class globals
{
public static $page = null;//make this static, meaning all instances will share this var
public function set_page($value)
{
self::$page = $value; // Maybe from a database
}
}
class get
{
private $_globalsInstance = null;
public function __construct(globals $instance = null)
{
$this->_globalsInstance = $instance;
}
private function _getGlobals()
{
if (!$this->_globalsInstance instanceof globals)
{
$this->_globalsInstance = new globals();
}
return $this->_globalsInstance;
}
public function page()
{
return $this->_getGlobals()::$page;
}
}
Personally, however, I wouldn't work like this, I'd just pass my instances to wherever I need them (as arguments to functions/methods or just instantiate them in a scope that will be accessible:
class globals
{
public $page = null;//make this static, meaning all instances will share this var
public function set_page($value)
{
$this->page = $value; // Maybe from a database
}
}
$page = new globals();
$page->set_page('foobar');
someFunction($page);
$someObject->renderPage($page);
require_once('specificScript.php');
//inside required script:
echo $page->page;
Do I have to use global variables?
Not, if your can use PHP 5.3
Is this the correct approach for the problem?
Better to use a generic class for this, or use static properties of objects
<?php
class globals
{
public static $page;
public function __construct()
{
}
public function set_page($value)
{
self::$page = $value; // Maybe from a database
}
}
class get
{
public static function page()
{
return globals::$page;
}
}
$globals = new globals();
$globals->set_page('My value');
echo get::page(); // Short function to be in a template
P.S.
But this is not a nice approach
$globals there
class get
{
public function page()
{
$globals = new globals();
return $globals->page;
}
}
and there
$globals = new globals();
$globals->set_page('My value');
are different inctances of globals class.
One of the solutions is to make $page var static
public static $page;
I hope this helps
UPD:
Also you might apply Singleton to globals class and request for its insnance instead of creating new one directly:
globals::getInstance()->setPage('Page');
and
return globals::getInstance()->getPage();
In this case $page doesn't have to be static.
I'm not sure the other answers are very clear. You have created 2 classes. As such they have different scopes. As writen you can't access the original variable $page from the get class because it's outside the scope. Your page function in fact creates a new version of the object $globals without $page set. Normally you would place both your set and get functions in the initial object/class. Though it would be possible to use two class by calling the first class from the second and setting the page. Why you would want to do that I'm not sure.
if I were writing the class it would look like this.
class globals
{
public $page;
public function __construct()
{
}
public function set_page($value)
{
$this->page = $value; // Maybe from a database
}
public function get_page()
{
return $this->page;
}
}
Actually I would probably set page to private not public. As public I guess you don't need a get function.
for using methods of the class without object you must use static definition. but anyway you put value for one class object and try to get it from another...
Perhaps this will help you continue on your coarse:
class globals
{
public static $page;
public function set_page($value)
{
self::$page = $value; // Maybe from a database
}
}
class get extends globals
{
public function page()
{
$globals = new globals();
return parent::$page;
}
}
$globals = new globals();
$globals->set_page('My value');
echo get::page();
?>
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
To pass value of a variable in one function to another function in same class
Can a value of a variable in one function be made available in another function on the same class using "GLOBAL" in PHP. If so please suggest how can this be achieved using Global.
You don't need to make a variable GLOBAL if you're within an object.
class myClass {
public $myVar = "Hello";
function myFunction() {
echo $this->$myVar;
}
}
This is one of the main points of objects - that you can assign different values to variables and get/set those variables within the different methods. And also that you can create multiple instances of objects each holding different information within the same structure and with the same methods available.
Additionally to what #Codecraft said (about using public properties), you can use:
indeed global variable (which is really something you should avoid doing),
passing values in parameters,
static variable within class,
Below is the example of using static variable (private), because I think this suits your needs best:
class MyClass {
private static $_something;
public function write() {
static::$_something = 'ok';
}
public function read() {
return static::$_something;
}
}
$x = new MyClass();
$x->write();
var_dump($x->read());
which outputs:
string(2) "ok"
This is in fact something like a global, but available only from inside your class (because of the keyword "private") and common among every instance of the class. If you use setting some non-static property, it will change across different instances of the class (one object may have different value stored in it than the other object has).
Comparison of solutions based on static and non-static variables:
Solution based on static variable will give you really global-like behaviour (value passed across different instances of the same class):
class MyClass {
private static $_something;
public function write() {
static::$_something = 'ok';
}
public function read() {
return static::$_something;
}
}
// first instance
$x = new MyClass();
$x->write();
// second instance
$y = new MyClass();
var_dump($y->read());
which outputs:
string(2) "ok"
And the solution based on non-static variables will look like:
class MyClass {
private $_something;
public function write() {
$this->_something = 'ok';
}
public function read() {
return $this->_something;
}
}
// first instance
$x = new MyClass();
$x->write();
// second instance
$y = new MyClass();
var_dump($y->read());
but will output:
NULL
which means that in this case the second instance has no value assigned for the variable you wanted to behave like "global".
Yes, a value of a variable in one function can be made available in another function on the same class using "GLOBAL". The following code prints 3:
class Foo
{
public function f1($arg) {
GLOBAL $x;
$x = $arg;
}
public function f2() {
GLOBAL $x;
return $x;
}
}
$foo = new Foo;
$foo->f1(3);
echo $foo->f2();
However, the usage of global variables is usually a sign of poor design.
Note that while keywords in PHP are case-insensitive, it is custom to use lower case letters for them. Not also that the superglobal array that contains all global variables is called $GLOBALS, not GLOBAL.
I know you can assign a function's return value to a variable and use it, like this:
function standardModel()
{
return "Higgs Boson";
}
$nextBigThing = standardModel();
echo $nextBigThing;
So someone please tell me why the following doesn't work? Or is it just not implemented yet? Am I missing something?
class standardModel
{
private function nextBigThing()
{
return "Higgs Boson";
}
public $nextBigThing = $this->nextBigThing();
}
$standardModel = new standardModel;
echo $standardModel->nextBigThing; // get var, not the function directly
I know I could do this:
class standardModel
{
// Public instead of private
public function nextBigThing()
{
return "Higgs Boson";
}
}
$standardModel = new standardModel;
echo $standardModel->nextBigThing(); // Call to the function itself
But in my project's case, all of the information stored in the class are predefined public vars, except one of them, which needs to compute the value at runtime.
I want it consistent so I nor any other developer using this project has to remember that one value has to be function call rather then a var call.
But don't worry about my project, I'm mainly just wondering why the inconsistency within PHP's interpreter?
Obviously, the examples are made up to simplify things. Please don't question "why" I need to put said function in the class. I don't need a lesson on proper OOP and this is just a proof of concept. Thanks!
public $nextBigThing = $this->nextBigThing();
You can only initialize class members with constant values. I.e. you can't use functions or any sort of expression at this point. Furthermore, the class isn't even fully loaded at this point, so even if it was allowed you probably couldn't call its own functions on itself while it's still being constructed.
Do this:
class standardModel {
public $nextBigThing = null;
public function __construct() {
$this->nextBigThing = $this->nextBigThing();
}
private function nextBigThing() {
return "Higgs Boson";
}
}
You can't assign default values to properties like that unless that value is of a constant data type (such as string, int...etc). Anything that essentially processes code (such as a function, even $_SESSION values) can't be assigned as a default value to a property. What you can do though is assign the property whatever value you want inside of a constructor.
class test {
private $test_priv_prop;
public function __construct(){
$this->test_priv_prop = $this->test_method();
}
public function test_method(){
return "some value";
}
}
class standardModel
{
// Public instead of private
public function nextBigThing()
{
return "Higgs Boson";
}
}
$standardModel = new standardModel(); // corection
echo $standardModel->nextBigThing();