6

Here is my example:

trait FileConfig {
    public static function getPathForUploads() {
        $paths = static::getPaths();
        //etc.
    }

    abstract public static function getPaths(); //doesn't work. Error: "Static function SharedDefaultConfig::getPaths() should not be abstract"

    abstract public function getPaths(); //OK
    public static function getPaths() {} //OK
}

Class:

class AppConfig {
    use FileConfig;

    public static function getPaths() {
        return array(...);  
    }
}

Call:

AppConfig::getPathForUploads();

It's nessessary to make it static and abstract (to force classes using FileConfig to implement getPaths).

I wonder how is it possible to implement method changing it's static property? Is it a good practice or there are better solutions? Will it one day become illegal?

Thank you

VladRia
  • 1,475
  • 3
  • 20
  • 32
  • I'm seeing `abstract public function` in AWS's PHP SDK, but I don't understand why not just use interfaces and/or abstract classes? – The Onin Sep 24 '17 at 15:05

3 Answers3

12

This is fixed in php 7, so the following code works:

<?php

error_reporting(-1);

trait FileConfig {
    public static function getPathForUploads() {
        echo static::getPaths();
    }

    abstract static function getPaths();
}

class AppConfig {
    use FileConfig;

    protected static function getPaths() {
        return "hello world";
    }
}

AppConfig::getPathForUploads();

http://sandbox.onlinephpfunctions.com/code/610f3140b056f3c3e8defb84e6b57ae61fbafbc9

But it does not actually check if the method in AppConfig is static or not during compilation. You will only get a warning when you try to call the non-static method statically: http://sandbox.onlinephpfunctions.com/code/1252f81af34f71e901994af2531104d70024a685

user1431317
  • 2,674
  • 1
  • 21
  • 18
3

You do not need to make the method static to force classes using it to implement the method. You can simply use interfaces alongside.

trait FileUploadConfig {
    public static function getPathForUploads() {
        $paths = static::getPaths();
        //etc.
    }
}

The trait was left as is. I just took away the functions for the interface.

interface PathConfiguration {
    public static function getPaths();
}

The interface forces the class to implement the function. I left the static in there to correspond with the trait's specification.

class AppConfig implements PathConfiguration {
    use FileUploadConfig;
    public static function getPaths() {
        return [];
    }
}
Igbanam
  • 5,904
  • 5
  • 44
  • 68
  • 3
    The point of abstract methods in traits is to force users of the trait to implement a method. If you have a separate interface, nothing guarantees that a user will also implement that interface (and the method required by the trait). – user1431317 Jul 13 '16 at 12:30
  • It's also useful for IDE. When you use a static method from an interface in the trait, basically the trait is not aware of this static method and the ide will pop an error. Instead if you declared it as an abstract static method inside the trait then the IDE knows about the method – gsouf Feb 04 '17 at 23:15
  • @SoufianeGhzal, if you only want to get rid of the IDE popups you can use the `@mixin` annotation to declare that the trait knows the interface – fragmentedreality Nov 29 '18 at 06:39
  • Didn't know about that ``@mixin`` annotation, thank you @fragmentedreality – gsouf Nov 29 '18 at 20:28
2

To force classes using FileConfig to implement getPaths it's not nessessary to make abstract function static. Static means that it belongs to the class that declared it. Make it protected static, add code from trait and then you could change behaviour by inheritance from your AppConfig class.

StalkAlex
  • 743
  • 7
  • 16