Req #74425 [NEW]: Behaviour Change: Non-static method should not be called statically

From: Date: Thu, 13 Apr 2017 01:28:18 +0000
Subject: Req #74425 [NEW]: Behaviour Change: Non-static method should not be called statically
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-208503@lists.php.net to get a copy of this message
From:             adrianrb93 at gmail dot com
Operating system: MACOS 10.12.14
PHP version:      7.0.17
Package:          Class/Object related
Bug Type:         Feature/Change Request
Bug description:Behaviour Change: Non-static method should not be called statically

Description:
------------
I added to my class a non-static method intentionally expecting that if
it is called statically, it will direct to the __callStatic() magic
method.

Use Case:

I am using Laravel. On its model classes:

* Model: calling a non-existing static method is handled by
__callStatic(), for this example lets pretend its ->admin()
* Model: callStatic creates a query builder class and attempts to run
the method on that
* Query Builder: The query builder does not have this method so it is
handled by __call()
* Query Builder: __call() will try to call the method on the Model
prepended with 'scope': `$this->getModel()->scopeAdmin($this,
...$parameters)`
* Model: The scopeAdmin() method exists

In my test script example, I will be using a scopeOpened() and an
opened() method, both which are non-static methods.


Test script:
---------------
<?php

/**
 *
----------------------------------------------------------------------------
 * Instructions:
 *
----------------------------------------------------------------------------
 * Install a new Laravel 5.4 app and place this test script in:
app/Shop.php
 *
 * In the Laravel project run composer install. The `php artisan
tinker`
 * command will give you the same test environment to test the
behaviour.
 */

/**
 *
----------------------------------------------------------------------------
 * Expected Result:
 *
----------------------------------------------------------------------------
 * $ php artisan tinker
 * Psy Shell v0.8.3 (PHP 7.0.17 — cli) by Justin Hileman
 * >>> App\Shop::opened('2017-04-13')
 * => App\Shop {#970
 *      owner_id: null,
 *      day_of_the_week: 4,
 *      start_date: "2017-04-13",
 *      end_date: "2017-04-13",
 *    }
 */

/**
 *
----------------------------------------------------------------------------
 * Actual Result:
 *
----------------------------------------------------------------------------
 * $ php artisan tinker
 * Psy Shell v0.8.3 (PHP 7.0.17 — cli) by Justin Hileman
 * >>> App\Shop::opened('2017-04-13')
 * ErrorException with message 'Non-static method App\Shop::opened()
should not be called statically'
 */

namespace App;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model as BaseModel;

class Shop extends BaseModel
{
    /**
     * This is what is expected to be run when Shop::opened() is
called.
     *
     * -  Shop::opened()
     * -> Model::__callStatic()
     * -> $builder->opened()
     * -> $builder->__call()
     * -> $model->scopeOpened()
     * -> $model->opened()
     *
     * The above will not work because within a static context, PHP
attempts
     * to call the non-static method opened() instead of directing the
call
     * to __callStatic().
     *
     * @param  Illuminate\Database\Eloquent\Builder $query
     * @return $this    Usually you return the query builder, but my
intent is
     *                  to have data pulled from the query builder,
then
     *                  $model->opened() be called.
     */
    public function scopeOpened($query, $date)
    {
        return $query->getModel()->fill([
            //
----------------------------------------------------------------
            // This is an example, the query builder could have added
            // where owner_id = '1'. This scope method is here to
direct to
            // the opened() method on the query builders model instance
with
            // the owner_id provided if given in the query.
            //
            // This is to show I have an actual need for the behaviour
I'm
            // requesting.
            //
----------------------------------------------------------------
            'owner_id' => self::extractWhereValueFromQuery($query,
'owner_id'),
        ])->opened($date);
    }

    /**
     * This is intended to only be called in a non-static context. The
     * behaviour I'm requesting is for PHP to see this isn't a static
method
     * and instead direct to the magic __callStatic() method.
     *
     * @param  Carbon\Carbon|string $date
     * @return $this
     */
    public function opened($date)
    {
        $date = self::resolveDate($date);
        return $this->fill([
            'day_of_week' => $date->dayOfWeek,
            'start_date'  => $date->toDateString(),
            'finish_date' => $date->toDateString(),
        ]);
    }

    /**
     * Extract column value from query builders where clause.
     *
     * @param  Illuminate\Database\Eloquent\Builder $query
     * @param  string $column
     * @return mixed
     */
    public static function extractWhereValueFromQuery($query, $column)
    {
        return collect( $query->getQuery()->wheres )
            ->filter(function ($where) use ($column) {
                return $where['column'] === $column;
            })
            ->pluck('value')
            ->first();
    }

    /**
     * Resolve a variable to a Carbon instance.
     *
     * @param  mixed $date
     * @return Carbon\Carbon
     */
    public static function resolveDate($date)
    {
        if (is_string($date)) {
            $date = Carbon::parse($date);
        }

        if ($date instanceof Carbon === false) {
            return Carbon::now();
        }

        return $date;
    }
}


Expected result:
----------------
$ php artisan tinker
Psy Shell v0.8.3 (PHP 7.0.17 — cli) by Justin Hileman
>>> App\Shop::opened('2017-04-13')
=> App\Shop {#970
     owner_id: null,
     day_of_the_week: 4,
     start_date: "2017-04-13",
     end_date: "2017-04-13",
   }


Actual result:
--------------
$ php artisan tinker
Psy Shell v0.8.3 (PHP 7.0.17 — cli) by Justin Hileman
>>> App\Shop::opened('2017-04-13')
ErrorException with message 'Non-static method App\Shop::opened() should
not be called statically'


-- 
Edit bug report at https://bugs.php.net/bug.php?id=74425&edit=1
-- 
Try a snapshot (PHP 5.4):   https://bugs.php.net/fix.php?id=74425&r=trysnapshot54
Try a snapshot (PHP 5.5):   https://bugs.php.net/fix.php?id=74425&r=trysnapshot55
Try a snapshot (trunk):     https://bugs.php.net/fix.php?id=74425&r=trysnapshottrunk
Fixed in SVN:               https://bugs.php.net/fix.php?id=74425&r=fixed
Fixed in release:           https://bugs.php.net/fix.php?id=74425&r=alreadyfixed
Need backtrace:             https://bugs.php.net/fix.php?id=74425&r=needtrace
Need Reproduce Script:      https://bugs.php.net/fix.php?id=74425&r=needscript
Try newer version:          https://bugs.php.net/fix.php?id=74425&r=oldversion
Not developer issue:        https://bugs.php.net/fix.php?id=74425&r=support
Expected behavior:          https://bugs.php.net/fix.php?id=74425&r=notwrong
Not enough info:            https://bugs.php.net/fix.php?id=74425&r=notenoughinfo
Submitted twice:            https://bugs.php.net/fix.php?id=74425&r=submittedtwice
register_globals:           https://bugs.php.net/fix.php?id=74425&r=globals
PHP 4 support discontinued: https://bugs.php.net/fix.php?id=74425&r=php4
Daylight Savings:           https://bugs.php.net/fix.php?id=74425&r=dst
IIS Stability:              https://bugs.php.net/fix.php?id=74425&r=isapi
Install GNU Sed:            https://bugs.php.net/fix.php?id=74425&r=gnused
Floating point limitations: https://bugs.php.net/fix.php?id=74425&r=float
No Zend Extensions:         https://bugs.php.net/fix.php?id=74425&r=nozend
MySQL Configuration Error:  https://bugs.php.net/fix.php?id=74425&r=mysqlcfg



Thread (3 messages)

« previous php.bugs (#208503) next »