=== Base.php ===
<?php
class Base
{
public static function e()
{
static $number = 0;
$number++;
var_dump($number);
}
}
=== A.php ===
<?php
class A extends Base {}
=== B.php ===
<?php
class B extends Base {}
=== test.php ===
function __autoload($classname)
{
require_once("{$classname}.php");
}
Base::e();
A::e();
B::e();
php test.php, result is:
int(1)
int(2)
int(2)
Why not result is 1,1,1?
Try
require "Base.php";
Base::e();
require "A.php";
A::e();
vs.
require "Base.php";
require "A.php";
Base::e();
A::e();
The former will yield int(1) int(2), while the latter yields int(1) int(1).
Why?
When a class is bound, the static variable content is copied at exactly that moment how it currently is. There is no backing up of the original value of the static variable.
That implies, when the static variable is 0 when class A is bound, A::e() will have 0 as static value; in case it's 1, A::e() will also have 1 as value.
Similar for B::e() then, as Base::e() and A::e() are independent as the values are copied (no references). It will also have the same static variable Base::e() has at the binding time of B.
I did some research on this problem and it is really weird.
Static properties inside methods remain their state between instances of object. Which may be confusing. Also there are two statics one is the static function and the other one is static variable inside method.
It may be connected with autoloader. I did similar example with your but without using static methods but using static variable inside method. The result is 1:1:1 using both autoloader and same file.
<?php
class Base
{
public function t()
{
static $number = 0;
$number++;
var_dump($number);
}
public static function e()
{
static $number = 0;
$number++;
var_dump($number);
}
}
$base = new Base();
$base->t();
$a = new A();
$a->t();
$b = new B();
$b->t();
Also if you won't execute Base::e() the result is correct.
I've did require_once without autoloading and it still works. So it is definitly because of autoloader.
If you put
require_once "Base.php";
require_once "A.php";
require_once "B.php";
instead of autoloader function it works. Why is that I have no idea I've tried to find anything considering static variables with autoloader but without success. However, this answer may give you some clue.
Related
I have two classes, class A and class B, and in a function in class A I want to call a function in B, but the it also requires constants from another file. After requiring both the config.php and classB.php files, I am able to create an instance of class B, but when I try to call the function in B, it gives me a warning saying the constant has not been defined.
In config.php:
$constant = 'blah';
Inside class A I have:
function functionA() {
require_once "config.php";
require_once "classB.php";
$b = new B();
$b -> functionB($constant);
}
The call to function B is giving an error saying $constant is not defined, but when I call new B() there is no problem. Am I doing something wrong?
Aron is correct. In this case you want to use include or require. You must make sure that you have no function or class declarations in the files you're including though.
This is occurring because the require will not occur on every call to the method, just the first. The subsequent calls to the method will result in an undefined variable error.
Additional top tip: You can return values from included files to assign them directly to a variable
A.php:
return 'abc';
B.php:
Class B {
public static function load()
{
$x = include 'A.php';
var_dump($x);
}
}
I imagine that your code should look as follows:
function functionA() {
require "config.php";
require_once "classB.php";
$b = new B();
$b -> functionB($constant);
}
use the sentence include 'config.php';
instead of require_once, if your config.php file just has variable declarations.
I have the following:
class bar {
function __construct(){
// set $a_from_fire to $a from fire() method arg
// set $b_from_fire to $b from fire() method arg
}
}
class foo extends bar {
function fire ($a, $b){
}
}
I need to set $a_from_fire and $b_from_fire using the arguments from foo->fire()
So if I do this:
$test = new foo;
$test->fire(1, 2);
These vars will be set:
$a_from_fire == 1; // true
$b_from_fire == 2; // true
I don't think you can do it in any "correct" way. My first thought was to use __call, but that of course is only called for undefined functions.
And there's not really any way to dynamically rename the methods, unless you're already using RunKit. (not that I know of or could find anyway).
If it's purely for debug purposes, you could set your own class autoloader to pre-process the files, change the method names, and then use the __call magic method on your parent class.
spl_autoload_register(function($class){
$hackPath = '/home/_classes/'.$class;
if (!file_exists($hackPath)){
$realPath = '/home/classes/'.$class;
$file = file_get_contents($realPath);
$processedContent = //use regex or something to prepend all function names with an _.
file_put_contents($hackPath,$processedContent);
}
require_once $hackPath;
});
Then in your parent class
class parent {
public function __call($funcName,$arguments){
$this->myLogFunc($funcName,$arguments);
//since you prepended with an underscore
return call_user_func_array('_'.$funcName,$arguments);
}
This is a terrible way to do what you're asking, but it could work. The pre-processing of files might be slow, but you'd only need to do it if the originals changed (you can use filemtime to check if it's changed).
This isn't possible because __construct() is called when the object is first instantiated, so fire($a, $b) will always run after __construct()
If you just want to set the variables when fire() is called, simply do:
class bar {
protected $a_from_fire;
protected $b_from_fire;
}
class foo extends bar {
public function fire($a, $b) {
$this->a_from_fire = $a;
$this->b_from_fire = $b;
}
}
There are a few posts relevant to this but I'm not seeing anything near to the situation I'm struggling with.
I've inherited a rather large codebase which the original designer has taken some interesting methods of design. I'm attempting to call a method of a class that's defined. The class file itself has some global variables set. I call this method from a function, where also, I've included this file. When the method runs, the global variables are no longer defined. See below:
My File:
<?php //myScript.php
echo("Calling foo(): ");
foo();
function foo() {
include '../../php/class.bar.php';
$bar = new bar();
$bar->doSomething();
}
?>
../../php/class.bar.php:
$GLOBAL_ARRAY_ONE[0] = 'Here I am';
$GLOBAL_ARRAY_ONE[1] = 'JT';
class bar {
public $itsFoo = array();
public $itsBar = array();
public function doSomething() {
global $GLOBAL_ARRAY_ONE;
$this->itsFoo[0] = $GLOBAL_ARRAY_ONE[0];
$this->itsFoo[1] = $GLOBAL_ARRAY_ONE[1];
var_dump($this->itsFoo);
}
}
So, when I run "myScript.php" The output is: calling foo(): NULL
I personally wouldn't declare global arrays in a script like that but, I see no reason why I shouldn't have access to them.
Any ideas? Thanks!
Since you include the file inside a function - GLOBALS cannot be defined there (neither a class). What you probably want to do is to include class.bar.php outside (of foo()):
<?php //myScript.php
include '../../php/class.bar.php';
echo("Calling foo(): ");
foo();
function foo() {
$bar = new bar();
$bar->doSomething();
}
?>
Is it possible to encapsulate, a variable or function let say, in PHP without wrapping them in a class? What I was doing is:
//Include the file containing the class which contains the variable or function
include('SomePage.php');
//Instantiate the class from "SomePage.php"
$NewObject = new SomeClassFromSomePage();
//Use the function or variable
echo $NewObject->SomeFuncFromSomeClass();
echo $NewObject->SomeVarFromSomeClass;
My intention is to avoid naming conflict. This routine, although it works, makes me tired. If I cannot do it without class, it is possible not to instantiate a class? and just use the variable or function instantly?
To use class methods and variables without instantiating, they must be declared static:
class My_Class
{
public static $var = 123;
public static function getVar() {
return self::var;
}
}
// Call as:
My_Class::getVar();
// or access the variable directly:
My_Class::$var;
With PHP 5.3, you can also use namespaces
namespace YourNamespace;
function yourFunction() {
// do something...
}
// While in the same namespace, call as
yourFunction();
// From a different namespace, call as
YourNamespace\yourFunction();
PHP Namespaces were made to archive the exact same goal:
<?php // foo.php
namespace Foo;
function bar() {}
class baz {
static $qux;
}
?>
When using call namespaced functions like this:
<?php //bar.php
include 'foo.php';
Foo\bar();
Foo\baz::$qux = 1;
?>
This is a way to encapsulate without Class
<?php
(function (){
$xyz = 'XYZ';
})();
echo $xyz; // warning: undefined
Encapsulation Alternative
With this method you can minimize unintentional using array key(uses it instead of variables). Can also use value stored in array anywhere after assigning. Shorter array key area length with variable in keys, inside encapsulation function; outside encapsulation function, variables can be used in keys but otherwise long discriptive keys. Nested encapsulation can also be used.
Example
<?php
define('APP', 'woi49f25gtx');
(function () {
$pre = 'functions__math__'; // "functions" is main category, "math" is sub.
$GLOBALS[APP][$pre . 'allowedNumbers'] = [3,5,6];
$GLOBALS[APP][$pre . 'square'] = function ($num) {
return $num * $num;
};
$GLOBALS[APP][$pre . 'myMathFunction'] = function ($num) use ($pre) {
if(in_array($num,$GLOBALS[APP][$pre . 'allowedNumbers'])) return 'not allowed';
return $GLOBALS[APP][$pre . 'square']($num);
};
})();
echo $GLOBALS[APP]['functions__math__myMathFunction'](4);
I have A.php and B.php
A.php
<?php
error_reporting(E_ALL);
ini_set("display_errors",1);
class myClass
{
function hello()
{
return 'hello';
}
}
?>
B.php
<?php
error_reporting(E_ALL);
ini_set("display_errors",1);
require_once('/A.php');
$a = new myClass();
testing();
function testing()
{
echo $a ->hello();
}
?>
B.php inherits A.php ,
if i run B.php,but it show
"Fatal error: Call to a member function hello() on a non-object."
So the question is simple, how can i correct this ,but "$a = new myClass();" is not inside the function, since in .NET world can do this, i believe PHP also possible.
And one more question is, what is the modify of function in A.php ,if i have not state private/public/protected?
This is not inheritance. B.php merely includes A.php. This means all the code in A.php becomes available to the execution context of B.php.
Inheritance is a class relationship
class A{}
class B extends A{}
Class B can be said to inherit from A. So, a hierarchy has been formed.
Much as we humans inherit traits from our parents, classes inherit properties from their parents. For this reason, it is very common to talk about these classes in terms of their place in the hierarchy. Class A is a parent to class B. Class B is a child to class A.
But the actual problem you are having, the source of the error message, is a scope issue. You create $a in the global scope but attempt to access it from within a function's scope, in this case testing().
The best way to solve this is to pass your instances of myClass into testing()
$a = new myClass();
testing( $a );
function testing( $myClassObj )
{
echo $myClassObj->hello();
}
And to answer your final question - in PHP5 - class members not explicitly declared with an access modifier become implicitly public.
If you want to use $a in the function, you need to either put
global $a;
as the first line of the function, or access it as $GLOBALS['a']
use this
function testing()
{
global $a;
echo $a ->hello();
}
A couple of things I would change here, I'll explain in a moment.
A.php
<?php
error_reporting(E_ALL);
ini_set("display_errors",1);
class myClass
{
// Here, access modifier.
public function hello()
{
return 'hello';
}
}
?>
B.php
<?php
error_reporting(E_ALL);
ini_set("display_errors",1);
require_once('/A.php');
testing();
function testing()
{
// Here, change where variable is defined to place it in scope.
$a = new myClass();
echo $a ->hello();
}
?>
When no access specifier is given on a method, it defaults to public. However, it is generally better to just declare what you want the method to have. You will appreciate this if you find yourself actively coding in multiple languages as they will each have a different default.
Right now the variable $a is not in the scope of the function testing(). Allow me to rearrange your program and you shall see why. You could have written it, like so:
B.php
<?php
function testing()
{
echo $a ->hello();
}
error_reporting(E_ALL);
ini_set("display_errors",1);
require_once('/A.php');
$a = new myClass();
testing();
?>
You see, where testing() is now defined, $a does not exist. It hasn't been defined yet. So it is not inside testing()'s scope. You'll have to either define $a inside testing() or else pass it in as a parameter. In my first pass, I changed your code to define $a inside testing(). If you will need to use it in multiple functions, then I would suggest changing testing() to take it as a parameter. Like this:
function testing(myClass $a) {
echo $a->hello();
}
Then pass it in this way:
$a = new myClass();
testing($a);
I believe $a is not defined in the function. Try using global $a first, and then calling hello().