php static array initialization workaround - php

I'm not sure what I should be searching to figure out this issue, so I'll show the code and describe the issue:
PHP Code:
<?php
class Foo
{
private static
$defaultSettings = array(
'bar' => new Baz() //error here
);
private
$settings;
public function __construct( $options = null )
{
$this->settings = isset( $options ) ? array_merge( self::$defaultSettings, $options ) : self::$defaultSettings;
}
}
class Baz
{
...code...
}
The Error:
Parse error: syntax error, unexpected T_NEW in [filename] on line [number]
What I'd like to do is have Foo::$defaultSettings contain an instance of an object, but I can't initialize the object when I create the array.
Is there a simpler way around this issue than a static initializer?
Static initializer code for Foo:
//self::init() would be called on the first line of __construct
private static function init()
{
static $initialized;
if ( !$initialized )
{
$initialized = true;
self::$defaultSettings['bar'] = new Baz();
}
}
I feel like there should be a simpler way around this issue than having to run an initializer.
Edit to add:
I could also make the initializer function public and call it immediately after the class definition as Foo::init(); which would reduce the overhead of the __construct function; however, I can't really see a single method call being significant savings.

Class properties cannot evaluate or instantiate anything, unfortunately. The closest you can do is run something from your constructor.

Related

Access private static class property from Ajax

I have a class similar to this:
class My_Class {
private static $array = null;
private static $another_array = null;
private function __construct() {
self:$another_array = array( 'data' );
}
// This gets executed from jQuery Ajax when user clicks a button
public static function process_ajax() {
self::generate_html();
}
private static function generate_html() {
if ( ! self::$array ) {
self::$array = array( 'some data' );
}
}
// This gets executed when user is trying to save Ajax generated form
public static function save_ajax_form() {
print_r( self::$another_array ); // prints [0] => 'data'
self::validate_data();
}
private static function validate_data() {
// WHY DOES THIS EVALUATE TRUE?
if ( ! is_array( self::$array ) ) {
}
}
}
How can I access My_Class::$array property from an Ajax call?
Even though you are declaring the variable static it is going to be initialized to null on every request - PHP is "stateless" in this way, static variables will not persist accross requests. Since you do want to persist the value you will need to use something like $_SESSION, APC or memcached to hold the value of $array.
When your ajax calls save_ajax_form() it immediately then calls validate_data(). The $array variable is still initialized to null since the call to generate_html() happened in a different request, so the check to see if it is not an array will return true.
See: Does static variables in php persist across the requests?
Obviously you could either change the scope declaration from private to public, or if you want to keep private, add a public accessor:
public function getArray()
{
self::process_ajax();
return self::$array;
}

unittest classes that use self:: in PHP

how would you test a method of a class that has collateral effects via self::? such as:
final class Foo{
private static $effect = false;
public static function doit( $arg ){
if( self::effect ) return;
self::check_args( $arg );
self::$effect = true;
}
private function check_args($arg){
#validate and trhow exception if invalid
}
}
I have to send several args to doit() to test it's validating the values correctly, but after the first run it's just bypassing it. it's basically a initialization method for a singleton that set's a initialized flag.
I can't really afford to mess with the class just yet. Is there any way i can make a copy/instatiate of the object in a way it would work with self::?
The class should be rewritten to:
class Foo{
private $effect = false;
public function doit( $arg ){
if( $this->effect ) return;
$this->check_args( $arg );
$this->effect = true;
}
private function check_args($arg){
#validate and trhow exception if invalid
}
}
self:: is only used for static functions and variables, which you should avoid using in most cases for various reasons.
How about adding to your testing environment a subclass of your class under test, and then using it to manipulate your static variable. In the example below, I modified your code a bit so the value of $effect is easier to play with, and I also had to bump the scope of $effect to protected:
<?php
class Foo{
protected static $effect = "0";
public static function doit( $arg ) {
echo 'I got: ' . $arg . ' and effect is: ' . self::$effect . '<br>';
self::$effect = "1";
}
}
class FooChild extends Foo {
public static function setEffect($newEffect) {
self::$effect = $newEffect;
}
}
Foo::doit('hello');
Foo::doit('world');
FooChild::setEffect('3');
Foo::doit('three');
The output is this:
I got: hello and effect is: 0 (first time through shows the initial value)
I got: world and effect is: 1 (second time through shows the value incremented by Foo in doit()
I got: three and effect is: 3 (third time through, shows that the subclass was able to change the value in the parent)

add function to an array in php

I'm having trouble declaring an array in conjunction to a function. Here is my code, what am I doing wrong?
private function array_list(){
return array('1'=>'one', '2'=>'two');
}
private $arrays= array(
'a'=>array('type'=>'1', 'list'=>$this->array_list())
);
Getting unexpected T_VARIABLE error when I run this code.
You cannot declare arrays like this as property:
private $arrays= array(
'a'=>array('type'=>'1', 'list'=>$this->array_list())
);
You cannot use an array returned from a class method in the property definition.
You should populate it inside a constructor for example. Like this:
private $arrays = array();
public function __construct() {
$this->arrays = array(
'a'=>array('type'=>'1', 'list'=>$this->array_list())
);
}
Do it in a method, for example, the constructor:
class Foo {
function __construct () {
$this->arrays['list'] = $this->array_list ();
}
}

How to initialize static variables

I have this code:
private static $dates = array(
'start' => mktime( 0, 0, 0, 7, 30, 2009), // Start date
'end' => mktime( 0, 0, 0, 8, 2, 2009), // End date
'close' => mktime(23, 59, 59, 7, 20, 2009), // Date when registration closes
'early' => mktime( 0, 0, 0, 3, 19, 2009), // Date when early bird discount ends
);
Which gives me the following error:
Parse error: syntax error, unexpected '(', expecting ')' in /home/user/Sites/site/registration/inc/registration.class.inc on line 19
So, I guess I am doing something wrong... but how can I do this if not like that? If I change the mktime stuff with regular strings, it works. So I know that I can do it sort of like that..
Anyone have some pointers?
PHP can't parse non-trivial expressions in initializers.
I prefer to work around this by adding code right after definition of the class:
class Foo {
static $bar;
}
Foo::$bar = array(…);
or
class Foo {
private static $bar;
static function init()
{
self::$bar = array(…);
}
}
Foo::init();
PHP 5.6 can handle some expressions now.
/* For Abstract classes */
abstract class Foo{
private static function bar(){
static $bar = null;
if ($bar == null)
bar = array(...);
return $bar;
}
/* use where necessary */
self::bar();
}
If you have control over class loading, you can do static initializing from there.
Example:
class MyClass { public static function static_init() { } }
in your class loader, do the following:
include($path . $klass . PHP_EXT);
if(method_exists($klass, 'static_init')) { $klass::staticInit() }
A more heavy weight solution would be to use an interface with ReflectionClass:
interface StaticInit { public static function staticInit() { } }
class MyClass implements StaticInit { public static function staticInit() { } }
in your class loader, do the following:
$rc = new ReflectionClass($klass);
if(in_array('StaticInit', $rc->getInterfaceNames())) { $klass::staticInit() }
Instead of finding a way to get static variables working, I prefer to simply create a getter function. Also helpful if you need arrays belonging to a specific class, and a lot simpler to implement.
class MyClass
{
public static function getTypeList()
{
return array(
"type_a"=>"Type A",
"type_b"=>"Type B",
//... etc.
);
}
}
Wherever you need the list, simply call the getter method. For example:
if (array_key_exists($type, MyClass::getTypeList()) {
// do something important...
}
I use a combination of Tjeerd Visser's and porneL's answer.
class Something
{
private static $foo;
private static getFoo()
{
if ($foo === null)
$foo = [[ complicated initializer ]]
return $foo;
}
public static bar()
{
[[ do something with self::getFoo() ]]
}
}
But an even better solution is to do away with the static methods and use the Singleton pattern. Then you just do the complicated initialization in the constructor. Or make it a "service" and use DI to inject it into any class that needs it.
That's too complex to set in the definition. You can set the definition to null though, and then in the constructor, check it, and if it has not been changed - set it:
private static $dates = null;
public function __construct()
{
if (is_null(self::$dates)) { // OR if (!is_array(self::$date))
self::$dates = array( /* .... */);
}
}
best way is to create an accessor like this:
/**
* #var object $db : map to database connection.
*/
public static $db= null;
/**
* db Function for initializing variable.
* #return object
*/
public static function db(){
if( !isset(static::$db) ){
static::$db= new \Helpers\MySQL( array(
"hostname"=> "localhost",
"username"=> "root",
"password"=> "password",
"database"=> "db_name"
)
);
}
return static::$db;
}
then you can do static::db(); or self::db(); from anywhere.
In PHP 7.0.1, I was able to define this:
public static $kIdsByActions = array(
MyClass1::kAction => 0,
MyClass2::kAction => 1
);
And then use it like this:
MyClass::$kIdsByActions[$this->mAction];
You can't make function calls in this part of the code. If you make an init() type method that gets executed before any other code does then you will be able to populate the variable then.
In my case, I'm using both static and nonstatic class properties, and I might even have main program code referencing the static part of the class before defining the class. Since static portions of classes don't have constructors, just add a manual constructor to initialize any variables requiring nontrivial calculation:
class A
{
static $a; // Initialized by Init()
static function Init()
{
A::$a=nontrivial();
{
}
...
A::Init(); // Initialize static part of class
...
$obj=new A(); // Using initialized class as an object
Here is a hopefully helpful pointer, in a code example. Note how the initializer function is only called once.
Also, if you invert the calls to StaticClass::initializeStStateArr() and $st = new StaticClass() you'll get the same result.
$ cat static.php
<?php
class StaticClass {
public static $stStateArr = NULL;
public function __construct() {
if (!isset(self::$stStateArr)) {
self::initializeStStateArr();
}
}
public static function initializeStStateArr() {
if (!isset(self::$stStateArr)) {
self::$stStateArr = array('CA' => 'California', 'CO' => 'Colorado',);
echo "In " . __FUNCTION__. "\n";
}
}
}
print "Starting...\n";
StaticClass::initializeStStateArr();
$st = new StaticClass();
print_r (StaticClass::$stStateArr);
Which yields :
$ php static.php
Starting...
In initializeStStateArr
Array
(
[CA] => California
[CO] => Colorado
)

How do I dynamically invoke a class method in PHP? [duplicate]

This question already has answers here:
How to use class methods as callbacks
(5 answers)
Closed last year.
How can I dynamically invoke a class method in PHP? The class method is not static. It appears that
call_user_func(...)
only works with static functions?
Thanks.
It works both ways - you need to use the right syntax
// Non static call
call_user_func( array( $obj, 'method' ) );
// Static calls
call_user_func( array( 'ClassName', 'method' ) );
call_user_func( 'ClassName::method' ); // (As of PHP 5.2.3)
Option 1
// invoke an instance method
$instance = new Instance();
$instanceMethod = 'bar';
$instance->$instanceMethod();
// invoke a static method
$class = 'NameOfTheClass';
$staticMethod = 'blah';
$class::$staticMethod();
Option 2
// invoke an instance method
$instance = new Instance();
call_user_func( array( $instance, 'method' ) );
// invoke a static method
$class = 'NameOfTheClass';
call_user_func( array( $class, 'nameOfStaticMethod' ) );
call_user_func( 'NameOfTheClass::nameOfStaticMethod' ); // (As of PHP 5.2.3)
Option 1 is faster than Option 2 so try to use them unless you don't know how many arguments your going to be passing to the method.
Edit: Previous editor did great job of cleaning up my answer but removed mention of call_user_func_array which is different then call_user_func.
PHP has
mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )
http://php.net/manual/en/function.call-user-func.php
AND
mixed call_user_func_array ( callable $callback , array $param_arr )
http://php.net/manual/en/function.call-user-func-array.php
Using call_user_func_array is orders of magnitude slower then using either option listed above.
You mean like this?
<?php
class A {
function test() {
print 'test';
}
}
$function = 'test';
// method 1
A::$function();
// method 2
$a = new A;
$a->$function();
?>
As of PHP7, you use an array-like way:
// Static call only
[TestClass::class, $methodName](...$args);
// Dynamic call, static or non-static doesn't matter
$instance = new TestClass();
[$instance, $methodName](...$args);
Just replace the class name with TestClass, the method name with $methodName and the method arguments with ...$args. Note that, in the later case, it doesn't matter that the method is static or non-static.
One advantage is you can pass the array as a callable to a function.
call_user_func(array($object, 'methodName'));
For more details, see the php callback documentation.
EDIT: I just worked out what you were trying to ask... ah well.. will leave my comments in anyway. You can substitute names of classes and methods with variables if you like..(but you are crazy) - nick
To call a function from within a class you can do it one of two ways...
Either you can create an instance of the class, and then call it.
e.g.:
$bla = new Blahh_class();
$bla->do_something();
or... you can call the function statically.. i.e. with no instance of the class.
e.g.:
Blahh_class::do_something()
of course you do need to declare that your function is static:
class Blahh_class {
public static function do_something(){
echo 'I am doing something';
}
}
If a class is not defined as static, then you must create an instance of the object.. (so the object needs a constructor)
e.g.:
class Blahh_class {
$some_value;
public function __construct($data) {
$this->$some_value = $data;
}
public function do_something() {
echo $this->some_value;
}
}
The important thing to remember is that static class functions can not use $this as there is no instance of the class. (this is one of the reasons why they go much faster.)
This may be useful as a substitute
class ReferenceContainer {
function __construct(CallbackContainer $callbackContainer) {
//Alternatively you can have no parameters in this constructor and create a new instance of CallbackContainer and invoke the callback in the same manner
//var_dump($this->callbackContainer);
$data = 'This is how you parse a class by reference';
$callbackContainer->myCallback($data);
}
}
class CallbackContainer {
function __construct() {}
function myCallback($data) {
echo $data."\n";
}
}
$callbackContainer = new CallbackContainer();
$doItContainer = new ReferenceContainer($callbackContainer);

Categories