Asked  2 Years ago    Answers:  5   Viewed   60 times

Why can't I do this in PHP? Where Database is a singleton class and getInstance() returns a PDO object.

<?php

class AnExample
{
    protected static $db = Database::getInstance();

    public static function doSomeQuery()
    {
        $stmt = static::$db->query("SELECT * FROM blah");
        return $stmt->fetch();
    }
}

Like any other PHP static variable, static properties may only be initialized using a literal or constant; expressions are not allowed. So while you may initialize a static property to an integer or array (for instance), you may not initialize it to another variable, to a function return value, or to an object.

http://php.net/manual/en/language.oop5.static.php

Why?!

 Answers

2

http://php.net/language.oop5.properties

Class member variables are called "properties". You may also see them referred to using other terms such as "attributes" or "fields", but for the purposes of this reference we will use "properties". They are defined by using one of the keywords public, protected, or private, followed by a normal variable declaration. This declaration may include an initialization, but this initialization must be a constant value--that is, it must be able to be evaluated at compile time and must not depend on run-time information in order to be evaluated.

The important part is

that is, it must be able to be evaluated at compile time

Expressions were evaluated at runtime, so it isn't possible to use expression to initialize properties: They are simply not evaluatable yet.

Wednesday, October 5, 2022
1

Will I have to upgrade to >=5.4 to have that option?

Yes. You need to upgrade to PHP 5.4 to do that.

That was a new feature introduced on PHP 5.4 actually.. Class member access on instantiation has been added, e.g. (new Foo)->bar().


If you try doing that on PHP versions less than 5.4 , you will run into this error.

Parse error: syntax error, unexpected T_OBJECT_OPERATOR, expecting ',' or ';'

Tuesday, December 13, 2022
 
2

In response to the "comment-discussion" that this question (and perhaps answer) has ignited, a recap of my opinion, and the general consensus (don't skimp on equipment/server load, when that means doubling the man-hours and maintainability is more important than speed, save for a few specific cases).

You say you hate frameworks and that devs who use frameworks they didn't develop themselves sell maintainance contracts. How exactly does that work? Code using an open source, documented FW can be maintained by anyone who knows the FW. Your code can be maintained by you.
Besides, PHP frameworks are (well, the main ones at least) OPEN SOURCE. I know that, coming from a VB.NET background, you might suspect there is a catch to this, but there really isn't. They all have github repo's, the source is freely available to copy, and edit (in accordance to the license, which, on the whole is quite liberal anyway). Basically, you have full control.
And don't go thinking it's all second-grade code: Apple (including major parts of their OSX kernel), Microsoft, Google... they are all using open-source software and frameworks. Not only because it's easy, and faster to develop, but also because of the notion of proven technology.

Besides: performance is your goal, and -given that you loathe frameworks-, why not consider learning assembly? That way, you can avoid having an idiotic compiler compile your code to something that isn't as efficient as, perhaps, it could have been.
And of course, you don't use an IDE for this, you'd either go for Emacs or butterflies, right?


(source: andreas-wilm.com)

An alternative approach would be to use a framework that offers advanced caching featrures...

Maintainability should be at the most important thing to you, while writing code:

'Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.' — Brian Kernighan

Servers are zombie-slaves: they don't complain, and it's not illegal to whip them. Developers, though some project-managers seem to forget this, are neither zombies or slaves: they can't work 2 weeks on end, and whipping devs is illegal, still. Even Steve Ballmer knows that... Developers, Developers, Developers. Microsoft's Server OS's are resource intensive, as is MSSQL, and pretty much all their software, but look into their server OS. There are many features that only serve to reduce the maintainance hours your system requires. I'm no MS fan-boy, but I fully understand that descision. They can't beat Linux on price ('cause it's free), but they can lure in companies by selling a system, that will cut their sys-admin costs in half. (though, ATM, they haven't acchieved this, and IMO, they never will)


This question is one of many dupes, a bad fit for SO, and will be closed. However, before it gets closed/deleted, I'd like to point a couple of things out to you:

First things first:

As I'm currently working on an e-commerce website, I'd like to know if it's really useful to use a singleton pattern on my PDO connections

No, it never is.

PHP is not capable of persisting an objects' state in between requests. The server receives a request. The script is parsed, the DB connection is established, queries are performed, the response is sent back and the connection is closed. Over and over again.
Connections can't be serialized, they can't be stored in any way, shape or form, so:
By coding well, you can use injection and at the same time ensure that you only connect to the DB once per request.
Statics are globals in drag, now: would you use globals if you might as well pass an argument? Giving you the benefit of the doubt, I assume your answer is no.
The few remaining benefits of singletons that remain, then, are not worth the effort it takes to write decent, working, unit-tests for your singleton-using code.

Why:

my architecture is made in a way that each script only needs one connection.

Then your code isn't very scalable. I know this is a blanket statement, and a terrible cliché, but it just is the case here. Just like your presumtion that each script only needs one connection might be true right now, but who's to say that will always be the case?
Refactoring the DB, adding support for another adapter, testing (as mentioned before) become increasingly difficult, as you'll write code and, without noticing it, that new code will be adapted to maintain the singleton.
This means that you'll write code to preserve what was, because you're working around its problems, a bad design decision.

Think of it like this:
You build a house, and decide to build a concrete pilar into the corridor, because that's where you'll hang your coat-hangers anyway, and you'll never change that. But Singleton implies that you're writing OO code. The whole point is that you can re-use code. Now: do you honestly think that you'll be able to sell your house just as easily if it had an immovable coat-hanger, as it would be if that thing wouldn't be there? of course not!

And I'm quite sure that it's only useful when you have to call an instance twice or more.

If that's so: use another pattern, or take a leaf out of the Symfony book, and make the connection available to whatever object needs it using a container (dependency injection component is worth a google search).

Testing:

It's all been explained before, but testing singleton code is a nightmare. Learn to use injection.

Monday, November 21, 2022
5

While @Luceos answer is technically correct (the best kind of correct), it does not answer the question you asked, namely is doing this better, or do includes happen regardless of function calls?

I tested this in the most basic way (OP, why didn't you?):

<?php
echo "Testing...";

function doThing() {
    include nonExistantFile.php;
}
//doThing();
echo "Done testing.";

Results:

if I call doThing(); I get a file not found warning.

If I comment out doThing();... there is no error! So you can save file load time by doing this.

Monday, September 5, 2022
 
5

Using the singleton-pattern (or antipattern) is considered bad practice because it makes testing your code very hard and the depencies very convoluted until the project becomes hard to manage at some point. You can only have one fixed instance of your object per php-process. When writing automated unit-tests for your code you need to be able to replace the object the code you want to test uses with a test-double that behaves in a prdictable manner. When the code you want to test uses a singleton, then you cannot replace that with a test double.

The best way (to my knowlege) to organize the interaction between objects (like your Database-Object and other objects using the database) would be to reverse the direction of the depencies. That means that your code is not requesting the object it needs from an external source (in most cases a global one like the static 'get_instance' method from your code) but instead gets its depency-object (the one it needs) served from outside before it needs it. Normally you would use a Depency-Injection Manager/Container like this one from the symfony project to compose your objects.

Objects that use the database-object would get it injected upon construction. It can be injected either by a setter method or in the constructor. In most cases (not all) is it better to inject the depency (your db-object) in the constructor because that way the object that uses the db-object will never be in an invalid state.

Example:

interface DatabaseInterface
{
    function query($statement, array $parameters = array());
}

interface UserLoaderInterface
{
    public function loadUser($userId);
}

class DB extends PDO implements DatabaseInterface
{
    function __construct(
        $dsn = 'mysql:host=localhost;dbname=kida',
        $username = 'root',
        $password = 'root',
    ) {
        try {
            parent::__construct($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'");
            parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch(PDOException $e) {
            echo $e->getMessage();
        }
    }

    function query($statement, array $parameters = array())
    {
        # ...
    }
}

class SomeFileBasedDB implements DatabaseInterface
{
    function __construct($filepath)
    {
        # ...
    }

    function query($statement, array $parameters = array())
    {
        # ...
    }
}

class UserLoader implements UserLoaderInterface
{
    protected $db;

    public function __construct(DatabaseInterface $db)
    {
        $this->db = $db;
    }

    public function loadUser($userId)
    {
        $row = $this->db->query("SELECT name, email FROM users WHERE id=?", [$userId]);

        $user = new User();
        $user->setName($row[0]);
        $user->setEmail($row[1]);

        return $user;
    }
}

# the following would be replaced by whatever DI software you use,
# but a simple array can show the concept.


# load this from a config file
$parameters = array();
$parameters['dsn'] = "mysql:host=my_db_server.com;dbname=kida_production";
$parameters['db_user'] = "mydbuser";
$parameters['db_pass'] = "mydbpassword";
$parameters['file_db_path'] = "/some/path/to/file.db";


# this will be set up in a seperate file to define how the objects are composed
# (in symfony, these are called 'services' and this would be defined in a 'services.xml' file)
$container = array();
$container['db'] = new DB($parameters['dsn'], $parameters['db_user'], $parameters['db_pass']);
$container['fileDb'] = new SomeFileBasedDB($parameters['file_db_path']);

# the same class (UserLoader) can now load it's users from different sources without having to know about it.
$container['userLoader'] = new UserLoader($container['db']);
# or: $container['userLoader'] = new UserLoader($container['fileDb']);

# you can easily change the behaviour of your objects by wrapping them into proxy objects.
# (In symfony this is called 'decorator-pattern')
$container['userLoader'] = new SomeUserLoaderProxy($container['userLoader'], $container['db']);

# here you can choose which user-loader is used by the user-controller
$container['userController'] = new UserController($container['fileUserLoader'], $container['viewRenderer']);

Notice how the different classes no not know about each other. There are no direct depencies between them. This is done by not require the actual class in the constructor, but instead require the interface that provides the methods it needs.

That way you can always write replacements for your classes and just replace them in the depency-injection container. You do not have to check the whole codebase because the replacement just has to implement the same interface that is used by all other classes. You know that everything will continue to work because every component using the old class only knows about the interface and calls only methods known by the interface.

P.S.: Please excuse my constant references to the symfony project, it is just what i am most used to. Other project's like Drupal, Propel or Zend probably also have concepts like this.

Friday, September 9, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 

Browse Other Code Languages