-1

I have created an helper class:

abstract class Format{
    public static function format_array_id_value($result){
        $array = [];
        foreach($result as $val){
            extract($val);
            $array[$id] = urldecode($val);
        }
        return $array;
    }

}

I am requiring the containing file at the start of the application. I am attemping to call it from another class method as Format::format_array_id_value($result). I am receiving the error "Fatal error: Class 'Format' not found in Expense.php on line 22".

Am I not thinking about abstract classes correctly? Must I instantiate the class in order to use this helper method?

Thank you. :)

  • 1
    [From docs](https://www.php.net/manual/en/language.oop5.abstract.php): _Classes defined as abstract cannot be instantiated_ that means that you should extend your non-abstract class with it. In your case there is no reason to define it as abstract. Other thing make sure your _requiring_ is proper. – biesior Aug 08 '20 at 13:28
  • Show us how do you require `Format.php` file – biesior Aug 08 '20 at 13:37
  • @biesior It's a static method so there's no need for its class to be instantiated. An abstract class is fine for holding a bunch of static helper methods (it might be a design issue though, but that's another discussion). – Jeto Aug 08 '20 at 13:38
  • @Jeto - from [the documentation](https://www.php.net/manual/en/language.oop5.abstract.php) `"Methods defined as abstract simply declare the method's signature - they cannot define the implementation."` which is what the above is doing – Professor Abronsius Aug 08 '20 at 13:40
  • @DaniellaIsabella Are you using any class autoloading? If not, have you included the file which contains this class' definition? – Jeto Aug 08 '20 at 13:41
  • @ProfessorAbronsius This refers to non-static methods only. What Daniella has posted is [perfectly valid PHP](https://3v4l.org/eo5RZ). – Jeto Aug 08 '20 at 13:41
  • @Jeto I understand it I was rather refering to the _Must I instantiate the class in order to use this helper method?_ subquestion from Daniella. However I wouldn't mix abstract and static things, theye are separate issues. – biesior Aug 08 '20 at 13:42
  • @biesior Oh, right, never mind then. This might have been confusing though, so better clear things up just in case :) Now, again, if you're gonna use a class as a holder for a bunch of helper static functions, making it abstract would make sense. – Jeto Aug 08 '20 at 13:43
  • @Jeto, well, there is another question: if I am not instantiating classes, will the autoloader recognise it & auto load? I required the file manually, just to see if that would solve the problem, which it did not. – Daniella Isabella Aug 08 '20 at 20:57
  • In JavaScript, I am accustom to using object literals (not instantiating the class before using it). Is the same possible in PHP? Is that the correct usage of an abstract PHP class? – Daniella Isabella Aug 08 '20 at 20:59
  • @DaniellaIsabella Yes, it will, no matter whether you're instantiating it or not. See [this thread](https://stackoverflow.com/questions/2044514/does-the-php-autoloader-function-also-work-with-static-method-calls) for a similar question. – Jeto Aug 08 '20 at 21:25

2 Answers2

0

Daniela, there's no reason to use this as an abstract class, it's not its purpose.

As stated in documentation

Classes defined as abstract cannot be instantiated, and any class that contains at least one abstract method must also be abstract. Methods defined as abstract simply declare the method's signature - they cannot define the implementation.

Instead, use a common class

<?php

class FormatHelper
{
    public static function formatArrayIdValue($result, $id)
    {
        $array = [];
        foreach ($result as $val) {
            extract($val);
            $array[$id] = urldecode($val);
        }
        return $array;
    }

}

and make sure you include it in your script:

<?php
require_once('FormatHelper.php');
print_r(
    FormatHelper::formatArrayIdValue(['foo bar baz'], 'foo')
);

// output:
// Array ( [foo] => foo bar baz ) 

And the proper usage of abstract classes is:

<?php

abstract class AbstractFormatHelper
{
    // note that abstract method has no body
    abstract public static function requiredMethodToBeImplementedInChildClass();
}

class FormatHelper extends AbstractFormatHelper
{
    public static function requiredMethodToBeImplementedInChildClass()
    {
        return 'Implemented!';
    }


    public static function formatArrayIdValue($result, $id)
    {
        $array = [];
        foreach ($result as $val) {
            extract($val);
            $array[$id] = urldecode($val);
        }
        return $array;
    }
}

Conclusions & suggestions

Operators

  1. Use :: operator for accessing static methods.
    MyClass::myStaticMethod();
    
  2. Use -> operator for accessing methods of object.
    $obj = new MyClass();
    $obj->myNonStaticMethod();
    

Inheritance with abstract

As a Rule of thumb (ROT) abstract class is defined as abstract and contains at least one abstract method. That means that the main purpose of abstract classes is allowing its inheritance,

Although as Jeto pointed as a side effect if the class is defined as abstract, even if it doesn't contain any non_static methods it cannot be instantiated, however, this is not purpose for using abstract classes. Instead, you should use abstract classes only in case when you want to force developers (maybe yourself) for creating methods in their classes which extends your abstract class.

analyse this sample:

<?php

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

class MyClass
{
    public static function myStaticMethod()
    {
        return ' this is myStaticMethod()';
    }

    public function myNonStaticMethod()
    {
        return 'this is myNonStaticMethod()';
    }
}

abstract class AbstractFormatHelper
{
    abstract public static function requiredMethodToBeImplementedInChildClass();

    abstract protected static function otherProtectedAbstractMethod();

//    final abstract protected static function otherProtectedFinalAbstractMethod(); // you even can't declare it

    public static function foo()
    {
        return "foo() doesn't need to be implemented";
    }

    protected static function bar()
    {
        return "this doesn't need to be implemented but will be not accessible if you won't change it to public";
    }

    final protected static function baz()
    {
        return "baz() is final PROTECTED, you can NOT override it in the child, but you still can access it in child by getter ";
    }

    final private static function boo()
    {
        return 'boo() is final PRIVATE but and you can NOT access it in child by getter ';
    }
}

class FormatHelper extends AbstractFormatHelper
{
    public static function requiredMethodToBeImplementedInChildClass()
    {
        return 'Implemented!';
    }

    public static function otherProtectedAbstractMethod()
    {
        return 'erProtectedAbstractMethod() implemented';
    }

    public static function foo()
    {
        return parent::foo() . " but foo() can be implemented";
    }

    public static function bar()
    {
        return 'method bar() which was protected now can be public OR protected NOT private (see below)';
    }

//    That wouldn't work with private
//    private static function bar()
//    {
//        return parent::bar() . ' I wanted to override it in child as a private but it is impossible';
//    }

    public static function getBaz()
    {
        return self::baz(); // you can still access it in child class, cause is protected
    }

//    public static function getBoo()
//    {
//        return self::boo(); // it won't work ass parent boo is private
//    }


}

echo '<pre> OPERATORS' . PHP_EOL . PHP_EOL;

// operands
echo MyClass::myStaticMethod() . PHP_EOL;
echo MyClass::myNonStaticMethod() . PHP_EOL; // Warning In PHP 7, calling non-static methods statically is deprecated

$obj = new MyClass();
echo $obj->myStaticMethod() . PHP_EOL; // wouldn't use that
echo $obj::myStaticMethod() . PHP_EOL;
echo $obj->myNonStaticMethod() . PHP_EOL;
echo $obj::myNonStaticMethod() . PHP_EOL; // Warning In PHP 7, calling non-static methods statically is deprecated

echo PHP_EOL . PHP_EOL;
echo 'INHERITANCE WITH `abstract`' . PHP_EOL . PHP_EOL;
echo FormatHelper::foo() . PHP_EOL;
echo FormatHelper::bar() . PHP_EOL;
// echo FormatHelper::baz() . PHP_EOL; // you cannot access it as it's protected and final
echo FormatHelper::getBaz() . PHP_EOL;

Namespaces

Although this tip exceeds boundaries of this answer consider using namespaces in the future, especially, when your project will grow, so with proper autoload you will be able to use classes without requiring them each time like.

<?php
require_once('autoloader.php');
print_r(\Your\Namespace\FormatHelper::formatArrayIdValue(['foo bar baz'], 'foo');
print_r(\Other\Namespace\OtherHelper::format('foo');
print_r(\Quite\Other\Something\SomeHelper::someMethod());

Conventions

note, for the convention I renamed the classes and methods names, also added $id param as it was missing.

biesior
  • 55,576
  • 10
  • 125
  • 182
  • Again, I must disagree. Making this class abstract makes total sense, as it has no state at all and just holds static methods (just one here, but probably multiple ones in a real situation). Instantiating it wouldn't do anything, hence why making it abstract is appropriate. – Jeto Aug 08 '20 at 13:53
  • As an additional note, a `final` class with a `private` constructor is probably even better, as it explictly conveys the meaning of a non instantiable, but also non extendable class. In any case, OP's issue is most likely not related to any of this, but just to the appropriate loading of the class itself. – Jeto Aug 08 '20 at 14:03
  • Again, using classes with static-only methods **is not** a reason to make it abstract. Although it's possible and perfectly valid it's not the purpose of using the `abstract` keyword. – biesior Aug 08 '20 at 14:04
  • The first meaning of an abstract class is that it cannot be instantiated. A class that serves as a container of static helper functions shouldn't be instantiated, so it removes that option (even though the `final` + private constructor option is more to the point). Anyway, opinions, I guess. – Jeto Aug 08 '20 at 14:12
  • That's a side effect. The first meaning is that the abstract class can be extended and children class(es) should implement abstract methods. We can exchange with arguments till morning, please check my edit to know how abstract classes are suposed to be used. – biesior Aug 08 '20 at 14:38
  • Yes, that's the most common way of making use of abstract classes. And the most common way of making use of concrete classes is to instantiate them. In both cases, this can be misleading (as we don't want either, here), so if I'd have to choose, I'd still pick the option that actually prevents at least 1/2 of these things from happening. – Jeto Aug 08 '20 at 14:47
  • @Jeto it's actually quite good point for avoiding non-static methods, however I never seen usage of abstract for this even in popular solutions like [Drupal](https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Component%21Utility%21Crypt.php/8.2.x), [TYPO3](https://api.typo3.org/9.5/_general_utility_8php_source.html),etc. And agree with you that classes with static methods only is general against OOP in general. They are rather for structuring functions to avoid names conflicts. – biesior Aug 08 '20 at 15:15
  • @Jeto, exactly. This helper class need not every be instantiated nor reproduced in any way. It is simply a place to hold helper functions. So is that the correct use of an abstract class? Or is there another way of using an uninstantiated object? – Daniella Isabella Aug 08 '20 at 20:39
  • @DaniellaIsabella As you can see by reading this huge comment thread, it's somewhat a matter of opinions. I like the `final class` + `private` constructor best for this usage, as also suggested by [another SO question](https://stackoverflow.com/questions/309553/should-helper-utility-classes-be-abstract). In any case, it shouldn't make a single difference in the error you're getting. – Jeto Aug 08 '20 at 21:29
  • @biesior, so I can use the double-colon notation when accessing a regular class method that is not instantiated? – Daniella Isabella Aug 08 '20 at 23:08
  • And what of one that is instantiated? Can I still access a method in another class? Such as, Client::fetch_client_name? – Daniella Isabella Aug 08 '20 at 23:10
  • @biesior, I see what you mean (final + private constructor). I do like that better, as it does appear more precise. The way I am writing this, I do not intend for this helper class to ever be instantiated nor extended. – Daniella Isabella Aug 08 '20 at 23:26
  • @DaniellaIsabella In general use `::` operator for static methods and `->` for non-static, check my updated sample in Conclusions for more opions. – biesior Aug 09 '20 at 12:18
  • @DaniellaIsabella also keep in mind, that using `final` methods is not always a clue. In some cases, it's better to allow your class to be extended and method(s) overridden. If you assume that your logic ***can not*** be changed in any case, then make it final. – biesior Aug 09 '20 at 12:22
0

In the end, I figured the autoloader was not configured correctly, thus not loading the abstract Format class. Although I am still uncertain why manually requiring this file did not work, fixing the autoloader did solve it. Now onto the next set of errors.

Thank you lads for all of your input. :)