21

I want create a helper class that containing method like cleanArray, split_char, split_word, etc.

The helper class it self will be used with many class. example :

Class A will user Helper, Class B, Class C, D, E also user Helper Class

what the best way to write and use helper class in PHP ?

what i know is basic knowledge of OOP that in every Class that use Helper class must create a helper object.

$helper = new Helper();

It that right or may be some one can give me best way to do that.

I also will create XXX Class that may use Class A, B, C, etc.

UPDATE : ->FIXED my fault in split_word method :D

Based on Saul, Aram Kocharyan and alex answer, i modified my code, but its dont work, i dont know why.

<?php
class Helper {
    static function split_word($text) {
        $array =  mb_split("\s", preg_replace( "/[^\p{L}|\p{Zs}]/u", " ", $text ));
        return $this->clean_array($array);
    }
    static function split_char($text) {
        return preg_split('/(?<!^)(?!$)/u', mb_strtolower(preg_replace( "/[^\p{L}]/u", "", $text )));
    }
}
?>

and i use in other Class

<?php
include "Helper.php";
class LanguageDetection {
    public function detectLanguage($text) {
        $arrayOfChar = Helper::split_char($text);
        $words = Helper::split_word($text);
        return $arrayOfChar;
    }
}
$i = new Detection();
print_r($i->detectLanguage("ab  cd    UEEef   する ح      خهعغ فق  12  34   ٢ ٣  .,}{ + _"));
?>
hakre
  • 193,403
  • 52
  • 435
  • 836
Ahmad
  • 4,224
  • 8
  • 29
  • 40
  • What are the classes A,B,C,D,E doing? What is their responsibility? – Gordon Aug 15 '11 at 13:37
  • I find this link http://net.tutsplus.com/tutorials/php/increase-productivity-by-creating-php-helper-functions/ it's good tutorial, just create a bunch of function, not inside class or static method inside class. What you think ? – Ahmad Aug 17 '11 at 04:23
  • It does not work because you define a class named LanguageDetection and try to instantiate a class named Detection – Semanino Mar 07 '16 at 15:12

5 Answers5

39

Helper classes are usually a sign of lack of knowledge about the Model's problem domain and considered an AntiPattern (or at least a Code Smell) by many. Move methods where they belong, e.g. on the objects on which properties they operate on, instead of collecting remotely related functions in static classes. Use Inheritance for classes that share the same behavior. Use Composition when objects are behaviorally different but need to share some functionality. Or use Traits.

The static Utils class you will often find in PHP is a code smell. People will throw more or less random functions into a class for organizing them. This is fine when you want to do procedural coding with PHP<5.2. As of 5.3 you would group those into a namespace instead. When you want to do OOP, you want to avoid static methods. You want your objects to have High Cohesion and Low Coupling. Static methods achieve the exact opposite. This will also make your code less testable.

Moreover, every Class that use Helper class must create a helper object is a code smell. Your collaborators should not create other collaborators. Move creation of complex object graphs into Factories or Builders instead.

Community
  • 1
  • 1
Gordon
  • 312,688
  • 75
  • 539
  • 559
  • you can see this link http://stackoverflow.com/questions/7058411/classoop-extends-problem-in-php – Ahmad Aug 15 '11 at 14:21
  • 6
    @Gordon - Sounds a bit of dogmatic, at least if applied generally. What if the functionality does not map to a specific problem domain? Would [java.util.Arrays](http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html) be an example of an anti-pattern and in effect should be refactored into something that avoids static methods? Or what about [java.lang.Math](http://download.oracle.com/javase/6/docs/api/java/lang/Math.html)? Not to mention the issue of performance with oft-used helper methods. – Saul Aug 15 '11 at 14:27
  • @Saul I'm not any more dogmatic than anyone that says if it doesnt operate on a property, make it static. The only difference is that I dont say "make it static" but "dont put it there". – Gordon Aug 15 '11 at 14:50
  • @Gordon: I suppose the applicability of "dont put it there" varies depending on the situation and who one asks. – Saul Aug 15 '11 at 15:07
  • 2
    Where would he put all those common types of functions at? Just an include in his initial loading? If he has 25 of them, will that slow things down? I'm reading the links you gave, but I'm confused. Are you saying he should put the same functions in every class? – johnny Sep 22 '14 at 14:18
  • The good answer to the OP question could be the Laravel's creator approach. Look up the `vendor\laravel\framework\src\Illuminate\Support\Str.php`. It is a valid example of the static methods used in a helper class and a purely valid practical use case. Not just theorhetical 'no-no'. – Valentine Shi May 11 '18 at 05:12
  • 3
    @bob-12345 that class is a hodgepodge of mostly redundant and/or unrelated wrappers and methods. It has no cohesion and shouldn't be a class at all. It's a prime example of why Helper classes are considered a code smell. For example, why is the UUID stuff in there when it has a dedicated class? Why do I need to reinvent `ucfirst()` with three calls to wrapper calls when PHP has a native version? Why is `slug` in there? Why not separate the case conversions? That entire thing feels like "oh, let's just throw all methods operating on a string somehow in here" But yeah… Laravel… `¯\_(ツ)_/¯` – Gordon May 11 '18 at 11:25
  • Ok. `Str` - 33 (!) pretty convenient static function. There are some more in the same flavour. Look up here `vendor\laravel\framework\src\Illuminate\Support\Arr.php` - 27 static fucntions in a class, quite helpful ones. Let alone there are over a dosen such helpers classes in the Laravel `Support` folder. All they give what OP wants - the container to isolate, group, keep in order misc functions. That keeps code readable & maintainable. What is the alternative advised? None. – Valentine Shi May 13 '18 at 05:15
  • Symfony: same approach. Here just to name a few: `symfony\vendor\doctrine\common\lib\Doctrine\Common\Util\ClassUtils.php, Debug.php.` `symfony\vendor\doctrine\dbal\lib\Doctrine\DBAL\SQLParserUtils.php` and over a couple of dozen examples from Symfony creators. – Valentine Shi May 13 '18 at 06:11
  • @bob-12345 I am not saying there are inconvenient or unhelpful. I am saying they are misplaced by virtue of SOLID and GRASP principles, e.g. two widely accepted sets of good OOP design. If you reread my post and the links carefully and with an open mind instead of rejecting the ideas because "some framework in PHP", you'll not only learn something new but also discover the alternatives advised. – Gordon May 14 '18 at 10:10
  • @bob-12345 As for the particular classes you named: Doctrine's ClassUtils exist because classes are not objects, so it's valid to have a class for doing this with them. Laravel's Str and Arr exist because scalars and arrays are not objects in PHP, so you cannot place methods on them. Nevertheless, both don't need to be classes, but can just be functions in a namespace instead. And if you want them to be classes, you can still break them apart into more dedicated classes. In fact, there already is a Pluralizer in Laravel. It's superfluous to wrap it in the Str class. Segregated interfaces ftw. – Gordon May 14 '18 at 10:15
  • I frequently read many people say `avoid static methods. `. Then why in Laravel do we have static methods for Eloquent Models? – Pathros Nov 02 '21 at 16:13
21

As a rule of thumb, helpers should contain functionality that is common but has no special designation under the overall architecture of the application.

  • Suffix the classname with Helper
  • Use static methods whenever possible

In short:

// Helper sample
//
class ConversionHelper {

   static function helpThis() {
      // code
   }

   static function helpThat() {
      // code
   }
}

// Usage sample
//
class User {

   function createThings() {
      $received = ConversionHelper::helpThis();
   }
}
Saul
  • 17,973
  • 8
  • 64
  • 88
  • Thanks, so is i must include(include "Helper.php";) Helper Class in all Class that use it ? or i must extend ? Thanks – Ahmad Aug 15 '11 at 12:34
  • Extending would be a bad idea unless You want to create a more specialized version of it. I'd recommend `include`. – Saul Aug 15 '11 at 12:40
  • It's work when calling one method, but if i call more then one, it wont work, please see my update, Thanks – Ahmad Aug 15 '11 at 13:00
  • @Ahmad - Be more specific. What is the expected behaviour? What goes wrong? What errors and warnings does it give? – Saul Aug 15 '11 at 13:03
  • I'm sorry, it's my fault in split_word method. Thanks so much It works :) – Ahmad Aug 15 '11 at 13:03
  • the reason I used require was that in case the helper file is not found, you will not get an error with include. So if code that is rarely executed uses a helper function that is unavailable, using include will not give you an error in all situations, in fact, it will stop the script whenever it is used. Require ensures that nothing will run if the file is not found, better for testing. generally use include to separate different parts of a website that don't rely heavily on each other. – Aram Kocharyan Aug 15 '11 at 13:24
  • @Aram Kocharyan - True. `require` guarantees a halt and in some ways is a safer bet. On the other hand, it can encourage sloppier coding - relying on errors for fall-back functionality can turn into a disaster waiting to happen. – Saul Aug 15 '11 at 13:40
  • that's exactly what include would lead to. require will give an error at all times, include will give it IF the function in the missing file is called (and PHP gives an error). – Aram Kocharyan Aug 15 '11 at 13:46
  • @Aram Kocharyan - You misunderstood my comment. What I meant was that `include` forces one to think through what can go wrong. `require` does not. At any rate, a live system which shows a raw error to the end user is considered **a Bad Thing**. Both warnings and errors [should be caught and dealt with](http://php.net/manual/en/function.set-error-handler.php) which makes the halting feature of `require` somewhat redundant. – Saul Aug 15 '11 at 14:03
  • I read that include is better then require if we want a performance, based on benchmark require vc include – Ahmad Aug 15 '11 at 14:08
  • http://stackoverflow.com/questions/596156/what-is-the-difference-between-require-and-include-with-php#596740 and then, http://www.w3schools.com/PHP/php_includes.asp last paragraph, http://www.fayazmiraz.com/php-include-vs-require/ last paragraph. The import/include function found in other languages behaves like the require function in PHP. Also note that a failed include will generate a warning, but you can always (and you should) disable error reporting in PHP for deployed code. – Aram Kocharyan Aug 16 '11 at 02:36
  • @Aram Kohcaryan - When it comes to disabling error reports then my experience has been different. Having `E_ALL` turned on at all times has three benefits: **1)** it forces developers to write cleaner and less ambiguous code; **2)** bugs and potential pitfalls are revealed at an early stage; **3)** logs contain more information which makes retrospective troubleshooting easier. How to make it happen in terms of functionality and user interface is a subject way beyond the scope of this particular question. – Saul Aug 16 '11 at 09:10
  • I agree, you should show errors, warnings and notices when in development, but when deployed, it's best turn them off for the sake of the user. – Aram Kocharyan Aug 16 '11 at 14:46
  • I find this link http://net.tutsplus.com/tutorials/php/increase-productivity-by-creating-php-helper-functions/ it's good tutorial, just create a bunch of function, not inside class or static method inside class. What you think ? – Ahmad Aug 17 '11 at 04:22
  • @Ahmad: That style is called [procedular programming](http://en.wikipedia.org/wiki/Procedural_programming). Which one to use is a matter of preference although OOP certainly has its benefits. If You want to understand what OOP is really about then I suggest You read the chapter **[Object-Oriented Programming Concepts](http://download.oracle.com/javase/tutorial/java/concepts/)** from the official Java tutorial. Ideas presented there apply to PHP also. – Saul Aug 17 '11 at 09:02
  • @Aram Kocharyan: What makes You think that catching every error, warning and notice with `E_ALL` means that the user must see them? – Saul Aug 17 '11 at 09:08
  • not sure what you are asking. basically meant that you should display errors when developing, not deploying. – Aram Kocharyan Aug 17 '11 at 14:47
  • @Aram Kocharyan: I was drawing Your attention to the fact that having errors turned on with `E_ALL` does not necessarily mean they are displayed to the end user as such. – Saul Aug 17 '11 at 15:21
  • @Aram Kocharyan: The standard practice is not to hide or turn off errors but to redirect them from standard output (HTTP) to a log. Under that approach the user sees only a custom-made apology page, if anything at all. See http://davidwalsh.name/custom-error-handling-php – Saul Aug 18 '11 at 12:21
6

Instead of creating static class , you should just write simple functions , and include that file at the index/bootstrap file (you can even use namespaces with it).

Instead of:

class Helper {
    static function split_word($text) { ...
    static function split_char($text) { ...
}

It should be:

namespace Helper;

function split_word($text) { ...
function split_char($text) { ...

There is no point wrapping it all up in a class. Just because you put it in a class doesn't make it object oriented .. actually it does the exact oposite.

tereško
  • 58,060
  • 25
  • 98
  • 150
2

You could create a class with static methods...

class Str {
   public static function split_char($str, $chr) {
      ...
   }
}

You could also namespace a bunch of functions with a namespace, but I think the former is preferred.

alex
  • 479,566
  • 201
  • 878
  • 984
1

Use public static methods in the class as such:

/* Common utility functions mainly for formatting, parsing etc. */
class CrayonUtil {
    /*  Creates an array of integers based on a given range string of format "int - int"
     Eg. range_str('2 - 5'); */
    public static function range_str($str) {
        preg_match('#(\d+)\s*-\s*(\d+)#', $str, $matches);
        if (count($matches) == 3) {
            return range($matches[1], $matches[2]);
        }
        return FALSE;
    }
    // More here ...
}

Then invoke them like this:

CrayonUtil::range_str('5-6');

If in another file, use the following at the top:

require_once 'the_util_file.php';
Aram Kocharyan
  • 20,165
  • 11
  • 81
  • 96
  • Thanks, so is i must include(include "Helper.php";) Helper Class in all Class that use it ? or i must extend ? Thanks – Ahmad Aug 15 '11 at 12:33
  • It's work when calling one method, but if i call more then one, it wont work, please see my update, Thanks – Ahmad Aug 15 '11 at 13:00
  • 1
    I'm sorry, it's my fault in split_word method. Thanks so much It works :) – Ahmad Aug 15 '11 at 13:03