Aura.Sql icon indicating copy to clipboard operation
Aura.Sql copied to clipboard

Support for PHP 8.4

Open srjlewis opened this issue 1 year ago • 9 comments

Hi

I am doing some eairly testing with PHP 8.4

I have found a conflict within the library

Fatal error: Cannot make static method PDO::connect() non static in class Aura\Sql\AbstractExtendedPdo in /some-project-dir/vendor/aura/sql/src/AbstractExtendedPdo.php on line 161

It is a result of a accepted PHP RFC https://wiki.php.net/rfc/pdo_driver_specific_subclasses which introduces a static connect method PDO::connect() as shown in the above method.

Here is lavavel's fix within there library https://github.com/laravel/framework/pull/52538/files

It looks like ExtendedPdoInterface::connect() would need renaming to somthing along the lines of ExtendedPdoInterface::autoConnect() and throughout the rest of the library.

I would then think ExtendedPdo:autoConnect() would looke somthing like.

class ExtendedPdo extends AbstractExtendedPdo
{
    public function autoConnect(): void
    {
        if ($this->pdo) {
            return;
        }

        // connect
        $this->profiler->start(__FUNCTION__);
        list($dsn, $username, $password, $options, $queries) = $this->args;
        if(version_compare(phpversion(), '8.4.0', '<')) {
            $this->pdo = new PDO($dsn, $username, $password, $options);
        } else {
             $this->pdo = PDO::connect($dsn, $username, $password, $options);
        }
        $this->profiler->finish();

        // connection-time queries
        foreach ($queries as $query) {
            $this->exec($query);
        }
    }
}

This would need to be a new majer version due to the big BC break ExtendedPdoInterface::connect() being public.

I can create a MR for this if needed.

srjlewis avatar Oct 03 '24 10:10 srjlewis

It seems reasonable and appropriate to me. cc/ @pmjones @harikt @frederikbosch

koriym avatar Oct 04 '24 03:10 koriym

Boooom.. @srjlewis Thank you for bringing this up and also nice pointers. We can make this next major version, but I am not sure what should we call this. I don't like the name autoConnect because it is not connecting automatically. Also this is a manual call. Any other alternatives you have in mind ?

Some of the methods came to my mind are createConnection, makeConnection, connectToDb.

harikt avatar Oct 04 '24 07:10 harikt

PR https://github.com/auraphp/Aura.Sql/pull/229

harikt avatar Oct 04 '24 07:10 harikt

@harikt I do agree with the naming, I used autoConnect as an example, and can easily be changed, apart from this issue I have not run into any other issues with PHP 8.4

srjlewis avatar Oct 04 '24 07:10 srjlewis

With driver specific subclasses in PHP, I think we should re-evaluate more than just the new PDO::connect method. Driver-specific classes are available in this library since #138 was merged. To what extend is that superseded in PHP 8.4? Should we not drop that if it is included in PHP itself?

If so, would it then not be better to drop < 8.4 for a new version 6.0? And just use override the connect method in order to support lazy connections? And, then maybe use an option for direct connections?


<?php

class ExtendedPdo
{
    public const CONNECT_IMMEDIATELY = 'immediate';

    protected array $args = [];
    protected bool $driverSpecific = false;

    public function __construct(
        string $dsn,
        ?string $username = null,
        ?string $password = null,
        array $options = [],
        array $queries = [],
        ?ProfilerInterface $profiler = null
    ) {
      // if no error mode is specified, use exceptions
        if (! isset($options[PDO::ATTR_ERRMODE])) {
            $options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
        }

        // retain the arguments for later
        $this->args = [
            $dsn,
            $username,
            $password,
            $options,
            $queries
        ];

        if ($options[self::CONNECT_IMMEDIATELY]) {
             $this->establishConnection();
        }

        // keep the rest of the constructor
    }

    #[\Override]
    public static function connect(string $dsn, string $username = null, string $password = null, array $options = null) {
    {
          $pdo = new self($dsn, $username, $password, $options);
          $pdo->driverSpecific = true;
          return $pdo;
    }

    // call this method from query, perform, execute etc
    private function establishConnection(): void
    {
       if ($this->pdo) {
            return;
       }

       if ($this->driverSpecific) {
            $this->pdo = PDO::connect($dsn, $username, $password, $options);$password, $options);
        } else {
            $this->pdo = new PDO($dsn, $username, 
        }
    }
}

frederikbosch avatar Oct 04 '24 07:10 frederikbosch

@frederikbosch we could approch the issue has you mention, but I see a few issues in your approach.

  1. it would make the libaray PHP 8.4 only, I was trying to keep compatability with prior versions of php
  2. You have removed the compatability of initilizing the object with a profiler in the connect method

but by all means keep the ideas flowing

srjlewis avatar Oct 04 '24 08:10 srjlewis

  1. Why keep the compatibility if you go to a version 6? People can use v5 for compatibility?
  2. That's because it's an example, not a PR. I would leave that in for sure.

frederikbosch avatar Oct 04 '24 08:10 frederikbosch

It will help with a smooth migration/update path

We can release v6.0 and users can implement it before the release of PHP 8.4

This would also allow users to run PHP 8.x in production and do testing with PHP 8.4 before its release.

This is what I am doing at the moment, yes I know users can use some composer magic but that makes it more complicated for the users.

I also think it is a good idea to keep compatability with all supported version of PHP and not make users switch between versions of the libaray.

srjlewis avatar Oct 04 '24 09:10 srjlewis

I disagree with that.

  1. Why rush to release before 8.4?
  2. There is no composer magic needed: if your are on PHP < 8.3, you use version 5 of AuraSQL, if you are going to use PHP 8.4, you use version 6 of AuraSQL. I don't see problem with that. That's no magic, that's how version management works.

Moreover, that is why there is semantic versioning: it's a contract how to deal with backward compatibility. When, you change the major version, you are going to break things. Now, with PHP 8.4 compatibility will be broken anyhow since the new connect method will force us to break things. You can better re-evaluate, and put the library in a mode that is going to proof itself again in the future. Hence, I suggest we slow down, and evaluate whether we need the driver specific parsers included in this library now PHP provides them in the core of PDO. And then see what is the best syntactic solution for immediate or manual connections.

frederikbosch avatar Oct 04 '24 09:10 frederikbosch

Hi

Could we move forward with any ideas for the support for PHP 8.4 as there is now only 10 days to the GA release of PHP 8.4

I dont mind if you want to implement the 8.4 support in a different way, I just think time is catching up quickly.

srjlewis avatar Nov 11 '24 15:11 srjlewis

Hi

Thank you all for your insightful discussions. I can see valid points in both approaches.

The clean v6 design proposed by @frederikbosch is ideal for applications that depend on a single PHP version. However, when libraries need to support both PHP 8.3 and PHP 8.4, some abstraction is necessary. In such cases, I believe implementing this abstraction at Aura.Sql's layer would be more appropriate than pushing it up to higher layers.

May I suggest the following approach:

For v5: Introduce a new internal method establishConnection() that handles version-specific PDO instantiation while keeping the existing connect() method for backward compatibility.

For v6: Since we can break BC, we can simply use the native PDO implementation targeting PHP 8.4+ only, without any abstraction.

I chose the name establishConnection as it clearly represents the intent and is a commonly used term in database contexts.

koriym avatar Nov 15 '24 04:11 koriym

@koriym I don´t get your suggested approach completely. Could you elaborate what you want to do in v5? What do we gain with this new establishConnection() method?

As stated before, I am all for targeting 8.4 only for v6. It is not us that is breaking BC, it is PHP that is doing that (and rightfully so). Do I understand correctly that is what you are suggesting?

frederikbosch avatar Nov 15 '24 09:11 frederikbosch

v5:

    /** This method is added, and nothing else changes. */
    public function establishConnection(): void
    {
        if ($this->pdo) {
            return;
        }

        // connect
        $this->profiler->start(__FUNCTION__);
        list($dsn, $username, $password, $options, $queries) = $this->args;
        if(version_compare(phpversion(), '8.4.0', '<')) {
             $this->pdo = new PDO($dsn, $username, $password, $options);
        } else {
             $this->pdo = PDO::connect($dsn, $username, $password, $options);
        }
        $this->profiler->finish();

        // connection-time queries
        foreach ($queries as $query) {
            $this->exec($query);
        }
    }

What we can gain from this method:

Libraries that must support both PHP 8.3 and PHP 8.4 can be made compatible with both PHP versions by modifying them to call this method.

v6:

targeting 8.4 only for v6

Yes.

Hope this makes things clear.

koriym avatar Nov 15 '24 09:11 koriym

I dont think it is worth changing v5, as the connect() signature is totally different

v5

public function connect(): void

v6 and pdo in php 8.4 onwards

public static function connect(string $dsn [, string $username [, string $password [, array $options ]]])

so just including establishConnection() would not work and would not benfit v5 and v5 cant even run on PHP 8.4 with it's current connect() signature.

What I was trying to do in version v6 was change the connect() method to match PDO's new signuture and logic, move the connect() logic to establishConnection() method, whist making v6 compatable with all supported versions of PHP.

So all version of PHP would be able to use v6 and PHP <= 8.3 would be able to use the new PDO connect syntax due to the changed connect() method

    public static function connect(
        string $dsn,
        ?string $username = null,
        ?string $password = null,
        ?array $options = [],
        array $queries = [],
        ?ProfilerInterface $profiler = null
    ): static {
        $pdo = new static($dsn, $username, $password, $options ?? [], $queries, $profiler);
        $pdo->establishConnection();
        return $pdo;
    }

I dont mind making v6 compatable with PHP 8.4 above I would only need to change establishConnection() to

    public function establishConnection(): void
    {
        if ($this->pdo) {
            return;
        }

        // connect
        $this->profiler->start(__FUNCTION__);
        list($dsn, $username, $password, $options, $queries) = $this->args;
        $this->pdo = PDO::connect($dsn, $username, $password, $options);
        $this->profiler->finish();

        // connection-time queries
        foreach ($queries as $query) {
            $this->exec($query);
        }
    }

I have updated the git actions to include PHP 8.4 in testing and all versions of PHP pass

srjlewis avatar Nov 15 '24 10:11 srjlewis

I would do the latter and on-top I really believe we need to investigate how our parsers. What use do they still have?

frederikbosch avatar Nov 15 '24 10:11 frederikbosch

Look at this commit in php-src, that is pretty much what we are offering with our parsers.

frederikbosch avatar Nov 15 '24 10:11 frederikbosch

Why don't we merge the proposed code (after review) of @srjlewis, tag v6 beta 1, let people use that if they want to use PHP 8.4 from today? Investigate the parsers, change the code where necessary and then move towards v6-rc.1 and finally v6.0?

frederikbosch avatar Nov 15 '24 10:11 frederikbosch

@frederikbosch I do aggree they need looking at, I was/am aming for compatability first of all and as we move forward, and the userbase move to newer versions of PHP I totally agree, with reviewing and removing the parsers.

srjlewis avatar Nov 15 '24 10:11 srjlewis

@frederikbosch I have done the update requested, and added a test for the connect() method and update composer

srjlewis avatar Nov 15 '24 11:11 srjlewis

I apologize for my earlier confusion. You are right - this is about the method name conflict between Aura.Sql's connect() and PHP 8.4's PDO::connect(). That's why we need v6 for PHP 8.4 support.

Thank you for the clarification.

koriym avatar Nov 15 '24 12:11 koriym

@frederikbosch @harikt

I was thinking of @harikt point of putting a establishConnection() method into v5, but as a alias of the connect() like

public function establishConnection(): void
{
     $this->connect();
}

This would allow users to switch to the establishConnection() mthod and be able to use the same api on PHP <=8.3 or >=8.4, they would only need to change calls from connect() to establishConnection() in there code

srjlewis avatar Nov 15 '24 12:11 srjlewis

@srjlewis We can add a method as you suggested and promote it in 5x, so people using older versions can use it. That is just a ux I believe, so easier to switch from 5.x to 6.x.

harikt avatar Nov 16 '24 07:11 harikt

I have merged https://github.com/auraphp/Aura.Sql/pull/229 to 6.x .

harikt avatar Nov 16 '24 10:11 harikt