I have a class:
class Test
{
public $AppCount;
public $Apps;
// When $AppCount is accessed I want to return count( $this->Apps )
}
When I access property $AppCount, I want to return count( $this->Apps ).
Rather than having to declare an exposing function for this property and making it private, can I use a getter function like C# and Java have?
Obviously the __get is not what i want in this case as the property does already exist.
For the comments
I have this and it does not run the function when i try and access the property:
class ProjectSettingsViewModel
{
public $ProjectAppCount = 0;
public $ProjectApps = array();
public function __get( $property )
{
switch( $property )
{
case "ProjectAppCount":
return count( $this->ProjectApps );
break;
}
}
}
If the code seems okay, it must be something else going wrong.
Unfortunately, PHP does not have the getter and setter syntax you are referring to. It has been proposed, but it didn't make it into PHP (yet).
First of all, __get() is only executed if you are trying to access a property that can't be accessed (because it is protected or private, or because it does not exist). If you are calling a property that is public, __get() will never be executed.
However, I would not suggest using PHP's magic getters and setters unless you really have no other choice. They are slower, tend to become a long if/elseif/else mess very quickly, and code completion (using a smart IDE) will not work. Normal methods are a lot simpler and easier to understand. You should also read this answer.
I have used the __get() and __set() methods for a while (instead of manually created getFoo() and setFoo() methods), because it seemed like a good idea, but I've changed my mind after a short time.
So, in your case, I recommend writing normal methods:
<?php
class Test
{
/**
* #var App[]
*/
private $apps;
/**
* Returns an array containing all apps
*
* #return App[]
*/
public function getApps()
{
return $this->apps;
}
/**
* Returns the number of apps
*
* #return integer
*/
public function getAppsCount() //or call it countApps()
{
return count($this->apps);
}
}
Untested.. Should work though. Using more magic methodology.
class ProjectSettingsViewModel
{
protected $ProjectAppCount = 0;
public $ProjectApps = array();
public function __get( $property )
{
switch( $property )
{
case "ProjectAppCount":
return count( $this->ProjectApps );
break;
}
}
}
Related
PHP 7 added support for anonymous classes, however I can't seem to find any information regarding associated scoping issues. I know I can use the use keyword with callables/closures to access outer scoped variables (like function() use ($outer) { // do work with $outer }), is there any way to do that with an anonymous class?
I would like to be able to do so without relying on the anonymous class constructor arguments, and without doing things like adding a setter method or public property to store the value after the instantiation.
Here's an example:
$outer = 'something';
$instance = new class {
public function testing() {
var_dump($outer); // would like this to dump the string 'something'
}
};
another solution could be
$outer = 'something';
$instance = new class($outer) {
private $outer;
public function __construct($outer) {
$this->outer = $outer
}
public function testing() {
var_dump($this->outer);
}
};
The unique way to access outside variable in this case is use $ _GLOBAL (I don't recommend). If you do not want to use constructor or setter method, my suggestion is to use a STATIC variable inside the anonymous class and set the value after the attribuition to the variable that contains the instance of anonymous class (Its not possible to define the static value before, because the class is anonymous..). Doing this, you have a better control and a static variable, but in certain way this is not very usual, every time when you create a new anonymous class the instance and it values belongs to the VARIABLE that receives the "new object", maybe is better for you to create a real class.. But follow a example with a static value and a anonymous class:
$i = new class {
public static $foo;
};
var_dump($i::$foo); //No value
$i::$foo = "Some value";
var_dump($i::$foo); //Has value
http://php.net/manual/en/language.variables.scope.php
There are some instructions in the php variable scope documentation.
This script will not produce any output because the echo statement refers to a local version of the $a variable, and it has not been assigned a value within this scope. You may notice that this is a little bit different from the C language in that global variables in C are automatically available to functions unless specifically overridden by a local definition. This can cause some problems in that people may inadvertently change a global variable. In PHP global variables must be declared global inside a function if they are going to be used in that function.
In php, the scope that a method inside a class can access is restricted to the inside of the entire class and cannot be accessed up to other scopes. So I think that the effect you want is not implemented in php, at least until the PHP GROUP decides to change the default behavior of PHP.
Of course, you can still use it by declaring variables as global.
Even though the OP did state that they would like to avoid public properties and anonymous class constructor arguments, the accepted answer is exactly that, so here is an example using a public property, which can be improved with a private property and a setter to maintain encapsulation:
class Foo {
public function executionMethod() {
return "Internal Logic";
}
}
$foo = new Foo();
var_dump("Foo's execution method returns: " . $foo->executionMethod());
$bar = new class extends Foo {
public $barVal;
public function executionMethod() {
return $this->barVal;
}
};
$bar->barVal = "External Logic";
var_dump("Bar's execution method returns: " . $bar->executionMethod());
I find this useful if you are unable to override the inherited class's constructor.
This will output:
string(46) "Foo's execution method returns: Internal Logic"
string(46) "Bar's execution method returns: External Logic"
If you want your anonymous class to have access to outer properties and methods that are protected or private, you could take advantage of the fact that closures inherit the scope they're defined in, and tie them into some magic methods for seamless behavior.
This is untested, but I'm pretty sure it'd work:
$class = new class {
/**
* #var \Closure
*/
public static $outerScopeCall;
/**
* #var \Closure
*/
public static $outerScopeGet;
/**
* #param string $name
* #param array $arguments
* #return mixed
*/
public function __call(string $name, array $arguments = [])
{
$closure = static::$outerScopeCall;
return $closure($name, $arguments);
}
/**
* #param string $name
* #param array $arguments
* #return mixed
*/
public function __get(string $name)
{
$closure = static::$outerScopeGet;
return $closure($name);
}
};
$class::$outerScopeCall = function (string $name, array $arguments = []) {
return $this->$name(...$arguments);
};
$class::$outerScopeGet = function (string $name) {
return $this->$name;
};
The $call closure will have access to the definition of $this from where it's defined as opposed to where
I'm starting to work with classes in PHP.
I have been reading and I noticed PHP is all about arrays.
So I was wondering if it would be a good practice to use the class properties inside array and naming them after keys.
Like this:
private $prefix;
private $name;
public function setPrefix($p)
{
$this->prefix = $p;
}
public function getPrefix()
{
return $this->prefix;
}
public function setName($n)
{
$this->name = $n;
}
public function getName()
{
return $this->name;
}
That's the common way of doing this.
But instead do it like this:
private $data = array();
public function setData($property, $value)
{
$this->data[$property] = $value;
}
public function getData($property)
{
return $this->data[$property];
}
Would this be better than the common way? I believe that would be a generic class structure for any database table.
Would this be better than the common way?
NO. And in fact it have drawbacks.
It removes the public, protected and private encapsulation of your properties (which is in the essence of oop).
Adds a layer over every variable access. I don't really know the internals of php, but I really don't think it could be faster than native properties. (although the difference is probably absolutely irrelevant to any script)
IDE's won't be able to complete your code when accessing properties.
It can have it's uses, if your class is a container which needs to have an array of internal data, in which case you would class container implements ArrayAccess and use it like an array, instead of global get/set methods. Here the documentation for ArrayAccess()
$obj = new container();
$obj['key'] = "value";
echo $obj['key'];
Bottom line
Why try and reinvent the wheel? A property is a property. There is no logical or semantical improvement in wrapping every property inside another property. It's obsfucating everything. It won't be faster, it won't be clearer, it removes the oop concepts from your properties and it's just going against the current of using objects in the first place.
About easier database management
If you really want to easily pass an array to a prepared statement, you can get the properties of an object with get_object_vars($obj), no need to put them in an array before for this very purpose. Moreover, as noted by Cypher, you won't be able to use the built-in fetchObject() method, which completely nullify the time you will not have gained by having an easier time querying the database.
This will make it easy to automate DB Operations.
But will make it hard for to use the object by humans.
Yii(2) uses this setup as part of there ActiveRecords but extend it by
defining the properties as a comment
/**
* #property int $id
* #property string $name
*/
class SomeClass extends AbstractModel
And also implements magic methods: __get(), __set()` so you can easily set and get properties like this:
class AbstractModel{
public function __get($name){
if(isset($this->data[$name])){
return $this->data[$name];
}else{
throw new Exception("Undefined or property '$name'");
}
}
public function __set($name, $value){
if(isset($this->data[$name])){
return $this->data[$name] = $value;
}else{
throw new Exception("Undefined or property '$name'");
}
}
I've been reading / watching a lot of recommended material, most recently this - MVC for advanced PHP developers. One thing that comes up is Singletons are bad, they create dependency between classes, and Dependency Injection is good as it allows for unit testing and decoupling.
That's all well and good until I'm writing my program. Let's take a Product page in a eshop as an example. First of all I have my page:
class Page {
public $html;
public function __construct() {
}
public function createPage() {
// do something to generate the page
}
public function showPage() {
echo $this->html;
}
}
All fine so far, but the page needs a product, so let's pass one in:
class Page {
public $html;
private $product;
public function __construct(Product $product) {
$this->product = $product;
}
public function createPage() {
// do something to generate the page
}
public function showPage() {
echo $this->html;
}
}
I've used dependency injection to avoid making my page class dependent on a product. But what if page had several public variables and whilst debugging I wanted to see what was in those. No problem, I just var_dump() the page instance. It gives me all the variables in page, including the product object, so I also get all the variables in product.
But product doesn't just have all the variables containing all the details of the product instantiated, it also had a database connection to get those product details. So now my var_dump() also has the database object in it as well. Now it's starting to get a bit longer and more difficult to read, even in <pre> tags.
Also a product belongs to one or more categories. For arguments sake let's say it belongs to two categories. They are loaded in the constructor and stored in a class variable containing an array. So now not only do I have all the variables in product and the database connection, but also two instances of the category class. And of course the category information also had to be loaded in from the database, so each category instance also has a database private variable.
So now when I var_dump() my page I have all the page variables, all the product variables, multiples of the category variables in an array, and 3 copies of the database variables (one from the products instance and one from each of the category instances). My output is now huge and difficult to read.
Now how about with singletons? Let's look at my page class using singletons.
class Page {
public $html;
public function __construct() {
}
public function createPage() {
$prodId = Url::getProdId();
$productInfo = Product::instance($prodId)->info();
// do something to generate the page
}
public function showPage() {
echo $this->html;
}
}
And I use similar singletons inside the Product class as well. Now when I var_dump() my Page instance I only get the variables I wanted, those belonging to the page and nothing else.
But of course this has created dependencies between my classes. And in unit testing there's no way to not call the product class, making unit testing difficult.
How can I get all the benefits of dependency injection but still make it easy to debug my classes using var_dump()? How can I avoid storing all these instances as variables in my classes?
I'll try to write about several things here.
About the var_dump():
I'm using Symfony2 as a default framework, and sometimes, var_dump() is the best option for a quick debug. However, it can output so much information, that there is no way you're going to read all of it, right? Like, dumping Symfony's AppKernel.php, or, which is more close to your case, some service with an EntityManager dependency. IMHO, var_dump() is nice when you debugging small bits of code, but large and complex product make var_dump() ineffective. Alternative for me is to use a "real" debugger, integrated with your IDE. With xDebug under PhpStorm I have no real need of var_dump() anymore.
Useful link about "Why?" and "How-to?" is here.
About the DI Container:
Big fan of it. It's simple and makes code more stable; it's common in modern applications. But I agree with you, there is a real problem behind: nested dependencies. This is over-abstraction, and it will add complexity by adding sometimes unnecessary layers.
Masking the pain by using a dependency injection container is making
your application more complex.
If you want to remove DIC from your application, and you actually can do it, then you don't need DIC at all. If you want alternative to DIC, well... Singletons are considered bad practice for not testable code and a huge state space of you application. Service locator to me has no benefits at all. So looks like there is the only way, to learn using DI right.
About your examples:
I see one thing immediately - injecting via construct(). It's cool, but I prefer optional passing dependency to the method that requires it, for example via setters in services config.yml.
class Page
{
public $html;
protected $em;
protected $product;
public function __construct(EntityManager $em) {
$this->em = $em;
}
//I suppose it's not from DB, because in this case EM handles this for you
protected function setProduct(Product $product)
{
$this->product = $product;
}
public function createPage()
{
//$this->product can be used here ONLY when you really need it
// do something to generate the page
}
public function showPage()
{
echo $this->html;
}
}
I think it gives needed flexibility when you need only some objects during execution, and at the given moment you can see inside your class only properties you need.
Conclusion
Excuse me for my broad and somewhat shallow answer. I really think that there is no direct answer to your question, and any solution would be opinion based. I just hope that you might find that DIC is really the best solution with limited downside, as well as integrated debuggers instead of dumping the whole class (constructor, service, etc...).
I exactly know that it's possible to reach result what you wish, and don't use extreme solutions.
I am not sure that my example is good enough for you, but it has: di and it easy to cover by unit test and var_dump will be show exactly what you wish, and i think it encourage SRP.
<?php
class Url
{
public static function getProdId()
{
return 'Category1';
}
}
class Product
{
public static $name = 'Car';
public static function instance($prodId)
{
if ($prodId === 'Category1') {
return new Category1();
}
}
}
class Category1 extends Product
{
public $model = 'DB9';
public function info()
{
return 'Aston Martin DB9 v12';
}
}
class Page
{
public $html;
public function createPage(Product $product)
{
// Here you can do something more to generate the page.
$this->html = $product->info() . PHP_EOL;
}
public function showPage()
{
echo $this->html;
}
}
$page = new Page();
$page->createPage(Product::instance(Url::getProdId()));
$page->showPage();
var_export($page);
Result:
Aston Martin DB9 v12
Page::__set_state(array(
'html' => 'Aston Martin DB9 v12
',
))
Maybe this will help you:
class Potatoe {
public $skin;
protected $meat;
private $roots;
function __construct ( $s, $m, $r ) {
$this->skin = $s;
$this->meat = $m;
$this->roots = $r;
}
}
$Obj = new Potatoe ( 1, 2, 3 );
echo "<pre>\n";
echo "Using get_object_vars:\n";
$vars = get_object_vars ( $Obj );
print_r ( $vars );
echo "\n\nUsing array cast:\n";
$Arr = (array)$Obj;
print_r ( $Arr );
This will returns:
Using get_object_vars:
Array
(
[skin] => 1
)
Using array cast:
Array
(
[skin] => 1
[ * meat] => 2
[ Potatoe roots] => 3
)
See the rest here http://php.net/manual/en/function.get-object-vars.php
The short answer is, yes you can avoid many private variables and using dependency injection. But (and this is a big but) you have to use something like an ServiceContainer or the principle of it.
The short answer:
class A
{
protected $services = array();
public function setService($name, $instance)
{
$this->services[$name] = $instance;
}
public function getService($name)
{
if (array_key_exists($name, $this->services)) {
return $this->services[$name];
}
return null;
}
private function log($message, $logLevel)
{
if (null === $this->getService('logger')) {
// Default behaviour is to log to php error log if $logLevel is critical
if ('critical' === $logLevel) {
error_log($message);
}
return;
}
$this->getService('logger')->log($message, $logLevel);
}
public function actionOne()
{
echo 'Action on was called';
$this->log('Action on was called', 0);
}
}
$a = new A();
// Logs to error log
$a->actionOne();
$a->setService('logger', new Logger());
// using the logger service
$a->actionOne();
With that class, you have just one protected variable and you are able to add any functionality to the class just by adding a service.
A more complexer example with an ServiceContainer can be somthing like that
<?php
/**
* Class ServiceContainer
* Manage our services
*/
class ServiceContainer
{
private $serviceDefinition = array();
private $services = array();
public function addService($name, $class)
{
$this->serviceDefinition[$name] = $class;
}
public function getService($name)
{
if (!array_key_exists($name, $this->services)) {
if (!array_key_exists($name, $this->serviceDefinition)) {
throw new \RuntimeException(
sprintf(
'Unkown service "%s". Known services are %s.',
$name,
implode(', ', array_keys($this->serviceDefinition))
)
);
}
$this->services[$name] = new $this->serviceDefinition[$name];
}
return $this->services[$name];
}
}
/**
* Class Product
* Part of the Model. Nothing too complex
*/
class Product
{
public $id;
public $info;
/**
* Get info
*
* #return mixed
*/
public function getInfo()
{
return $this->info;
}
}
/**
* Class ProductManager
*
*/
class ProductManager
{
public function find($id)
{
$p = new Product();
$p->id = $id;
$p->info = 'Product info of product with id ' . $id;
return $p;
}
}
class UnusedBadService
{
public function _construct()
{
ThisWillProduceAnErrorOnExecution();
}
}
/**
* Class Page
* Handle this request.
*/
class Page
{
protected $container;
/**
* Set container
*
* #param ServiceContainer $container
*
* #return ContainerAware
*/
public function setContainer(ServiceContainer $container)
{
$this->container = $container;
return $this;
}
public function get($name)
{
return $this->container->getService($name);
}
public function createPage($productId)
{
$pm = $this->get('product_manager');
$productInfo = $pm->find($productId)->getInfo();
// do something to generate the page
return sprintf('<html><head></head><body><h1>%s</h1></body></html>', $productInfo);
}
}
$serviceContainer = new ServiceContainer();
// Add some services
$serviceContainer->addService('product_manager', 'ProductManager');
$serviceContainer->addService('unused_bad_service', 'UnusedBadService');
$page = new Page();
$page->setContainer($serviceContainer);
echo $page->createPage(1);
var_dump($page);
You can see, if you look at the var_dump output, that just the services, you called are in the output.
So this is small, fast and sexy ;)
I have a class with 23 different properties of various types, but I'll only show three in this example. A typical way to construct this class would be:
class Item
{
public $name;
public $price;
public $date;
// Getters and setters
public function get_name() {return $name;}
public function get_price() {return $price;}
public function get_date() {return $date;}
public function set_name($val) {$name = $val;}
public function set_price($val) {$price = $val;}
public function set_date($val) {$date = $val;}
}
With 23 different properties I would need 46 different set/get functions, and my code would have to call all these different functions by specific name, so it's difficult to create loops.
Can I do this instead:
class Item
{
public $props = array (
'name' => NULL,
'price' => NULL,
'date' => NULL
);
// Getters and setters
public function get( $key ) { return $props[$key]; }
public function set( $key, $val ) { $props[$key] = $val; }
public function getKeys() { return array_keys( $props ); }
}
To get a property such as $name I'd simply use Item->get('name');
rather than the specific getter function Item->get_name();
This way, even if I have 23 different properties of different types, I still use only one getter and one setter for all of them. Plus, it's easy to loop through all (or a subset) of the properties by doing a foreach on the getKeys() array.
It seems so convenient I would think it would be a pretty standard format for PHP classes, yet in all the tutorials and examples of PHP classes that I've seen I have never seen this construct used. Is there a problem with it?
This is fine if you don't care what type your properties are or you don't care if they should be restricted to a certain set of known properties (ie, you don't care about encapsulation).
If however you want to use specific types, you will need some control.
class Foo {
/**
* #var Bar
*/
private $bar;
public function setBar(Bar $bar) {
$this->bar = $bar;
}
/**
* #return Bar
*/
public function getBar() {
return $this->bar;
}
}
The other real benefit to setters in particular is the ability to perform transformations on input data. For example
class Foo {
/**
* #var DateTime
*/
private $date;
public function setDate($date) {
if (!$date instanceof DateTime) {
$date = new DateTime((string) $date);
}
$this->date = $date;
}
}
it's difficult to create loops.
What meaningful loop would you make over name, price and date?
The reason individual getters and setters are used is encapsulation: prohibiting things outside the class insight into its insides. For example, say you have age property. Then you extend your class with birth_date property. Then you think it would be a good idea to automatically calculate age instead of having to assign both values. If you have a getter for age, it is trivial: just have it stop returning $props["age"], and start returning the difference between the birth date and now. But if you're not using the age getter but a direct access, you would have to change every single place in your codebase where you're accessing age.
Or say that you want to issue a log statement every time a property changes. Trivial to put it inside a setter, but you're screwed if you have a public property that anyone is allowed to mess with at will.
I have an helper class with some static functions. All the functions in the class require a ‘heavy’ initialization function to run once (as if it were a constructor).
Is there a good practice for achieving this?
The only thing I thought of was calling an init function, and breaking its flow if it has already run once (using a static $initialized var). The problem is that I need to call it on every one of the class’s functions.
Sounds like you'd be better served by a singleton rather than a bunch of static methods
class Singleton
{
/**
*
* #var Singleton
*/
private static $instance;
private function __construct()
{
// Your "heavy" initialization stuff here
}
public static function getInstance()
{
if ( is_null( self::$instance ) )
{
self::$instance = new self();
}
return self::$instance;
}
public function someMethod1()
{
// whatever
}
public function someMethod2()
{
// whatever
}
}
And then, in usage
// As opposed to this
Singleton::someMethod1();
// You'd do this
Singleton::getInstance()->someMethod1();
// file Foo.php
class Foo
{
static function init() { /* ... */ }
}
Foo::init();
This way, the initialization happens when the class file is included. You can make sure this only happens when necessary (and only once) by using autoloading.
Actually, I use a public static method __init__() on my static classes that require initialization (or at least need to execute some code). Then, in my autoloader, when it loads a class it checks is_callable($class, '__init__'). If it is, it calls that method. Quick, simple and effective...
NOTE: This is exactly what OP said they did. (But didn't show code for.) I show the details here, so that you can compare it to the accepted answer. My point is that OP's original instinct was, IMHO, better than the answer he accepted.
Given how highly upvoted the accepted answer is, I'd like to point out the "naive" answer to one-time initialization of static methods, is hardly more code than that implementation of Singleton -- and has an essential advantage.
final class MyClass {
public static function someMethod1() {
MyClass::init();
// whatever
}
public static function someMethod2() {
MyClass::init();
// whatever
}
private static $didInit = false;
private static function init() {
if (!self::$didInit) {
self::$didInit = true;
// one-time init code.
}
}
// private, so can't create an instance.
private function __construct() {
// Nothing to do - there are no instances.
}
}
The advantage of this approach, is that you get to call with the straightforward static function syntax:
MyClass::someMethod1();
Contrast it to the calls required by the accepted answer:
MyClass::getInstance->someMethod1();
As a general principle, it is best to pay the coding price once, when you code a class, to keep callers simpler.
If you are NOT using PHP 7.4's opcode.cache, then use Victor Nicollet's answer. Simple. No extra coding required. No "advanced" coding to understand. (I recommend including FrancescoMM's comment, to make sure "init" will never execute twice.) See Szczepan's explanation of why Victor's technique won't work with opcode.cache.
If you ARE using opcode.cache, then AFAIK my answer is as clean as you can get. The cost is simply adding the line MyClass::init(); at start of every public method. NOTE: If you want public properties, code them as a get / set pair of methods, so that you have a place to add that init call.
(Private members do NOT need that init call, as they are not reachable from the outside - so some public method has already been called, by the time execution reaches the private member.)
There is a way to call the init() method once and forbid it's usage, you can turn the function into private initializer and ivoke it after class declaration like this:
class Example {
private static function init() {
// do whatever needed for class initialization
}
}
(static function () {
static::init();
})->bindTo(null, Example::class)();
I am posting this as an answer because this is very important as of PHP 7.4.
The opcache.preload mechanism of PHP 7.4 makes it possible to preload opcodes for classes. If you use it to preload a file that contains a class definition and some side effects, then classes defined in that file will "exist" for all subsequent scripts executed by this FPM server and its workers, but the side effects will not be in effect, and the autoloader will not require the file containing them because the class already "exists". This completely defeats any and all static initialization techniques that rely on executing top-level code in the file that contains the class definition.
If you don't like public static initializer, reflection can be a workaround.
<?php
class LanguageUtility
{
public static function initializeClass($class)
{
try
{
// Get a static method named 'initialize'. If not found,
// ReflectionMethod() will throw a ReflectionException.
$ref = new \ReflectionMethod($class, 'initialize');
// The 'initialize' method is probably 'private'.
// Make it accessible before calling 'invoke'.
// Note that 'setAccessible' is not available
// before PHP version 5.3.2.
$ref->setAccessible(true);
// Execute the 'initialize' method.
$ref->invoke(null);
}
catch (Exception $e)
{
}
}
}
class MyClass
{
private static function initialize()
{
}
}
LanguageUtility::initializeClass('MyClass');
?>
Some tests of assigning static public properties :
settings.json :
{
"HOST": "website.com",
"NB_FOR_PAGINA": 8,
"DEF_ARR_SIZES": {
"min": 600,
"max": 1200
},
"TOKEN_TIME": 3600,
"WEBSITE_TITLE": "My website title"
}
now we want to add settings public static properties to our class
class test {
/** prepare an array to store datas */
public static $datas = array();
/**
* test::init();
*/
public static function init(){
// get json file to init.
$get_json_settings =
file_get_contents(dirname(__DIR__).'/API/settings.json');
$SETTINGS = json_decode($get_json_settings, true);
foreach( $SETTINGS as $key => $value ){
// set public static properties
self::$datas[$key] = $value;
}
}
/**
*
*/
/**
* test::get_static_properties($class_name);
*
* #param {type} $class_name
* #return {log} return all static properties of API object
*/
public static function get_static_properties($class_name) {
$class = new ReflectionClass($class_name);
echo '<b>infos Class : '.$class->name.'</b><br>';
$staticMembers = $class->getStaticProperties();
foreach( $staticMembers as $key => $value ){
echo '<pre>';
echo $key. ' -> ';
if( is_array($value) ){
var_export($value);
}
else if( is_bool($value) ){
var_export($value);
}
else{
echo $value;
}
echo '</pre>';
}
// end foreach
}
/**
* END test::get_static_properties();
*/
}
// end class test
ok now we test this code :
// consider we have the class test in API folder
spl_autoload_register(function ($class){
// call path to API folder after
$path_API = dirname(__DIR__).'/API/' . $class . '.php';
if( file_exists($path_API) ) require $path_API;
});
// end SPL auto registrer
// init class test with dynamics static properties
test::init();
test::get_static_properties('test');
var_dump(test::$HOST);
var_dump(test::$datas['HOST']);
this return :
infos Class : test
datas -> array (
'HOST' => 'website.com',
'NB_FOR_PAGINA' => 8,
'DEF_ARR_SIZES' =>
array (
'min' => 600,
'max' => 1200,
),
'TOKEN_TIME' => 3600,
'WEBSITE_TITLE' => 'My website title'
)
// var_dump(test::$HOST);
Uncaught Error: Access to undeclared static property:
test::$HOST
// var_dump(test::$datas['HOST']);
website.com
Then if we modify the class test like this :
class test {
/** Determine empty public static properties */
public static $HOST;
public static $NB_FOR_PAGINA;
public static $DEF_ARR_SIZES;
public static $TOKEN_TIME;
public static $WEBSITE_TITLE;
/**
* test::init();
*/
public static function init(){
// get json file to init.
$get_json_settings =
file_get_contents(dirname(__DIR__).'/API/settings.json');
$SETTINGS = json_decode($get_json_settings, true);
foreach( $SETTINGS as $key => $value ){
// set public static properties
self::${$key} = $value;
}
}
/**
*
*/
...
}
// end class test
// init class test with dynamics static properties
test::init();
test::get_static_properties('test');
var_dump(test::$HOST);
this return :
infos Class : test
HOST -> website.com
NB_FOR_PAGINA -> 8
DEF_ARR_SIZES -> array (
'min' => 600,
'max' => 1200,
)
TOKEN_TIME -> 3600
WEBSITE_TITLE -> My website title
// var_dump(test::$HOST);
website.com
I actually need to initialize an object with public static properties that I will reuse in many other classes, which I think is supposed to, I don't want to do new api() in every method where I would need, for example to check the host of the site or indicate it. Also I would like to make things more dynamic so that I can add as many settings as I want to my API, without having to declare them in my initialization class.
All other methods I've seen no longer work under php > 7.4
I keep looking for a solution for this problem.
Note - the RFC proposing this is still in the draft state.
class Singleton
{
private static function __static()
{
//...
}
//...
}
proposed for PHP 7.x (see https://wiki.php.net/rfc/static_class_constructor )