How to reuse code with Utility Classes

by Kevin Pfeifer

About Kevin Pfeifer

  • Web-Developer at in Austria - www.sunlime.at
  • Mainly working with Wordpress and Drupal
  • Started using CakePHP 3 in 2017 and love it ❤️

What is coming?

  • Why we need PHP Namespaces
  • How are PHP Namespaces set up in CakePHP
  • How do I use PHP Namespaces
  • Common issues with PHP Namespaces

Why do we need PHP Namespaces?

Example

Lets say we have the following PHP classes


          // Books/MyUtility.php
          class MyUtility {
            public static function export(): void {
              echo "export function" . PHP_EOL;
            }
          }
        

          // Authors/MyUtility.php
          class MyUtility {
            public static function exportAll(): void {
              echo "exportAll function" . PHP_EOL;
            }
          }
        

Lets use them both

Lets require them both in our start php file and see what happens


          // start.php
          require('Books/MyUtility.php');
          require('Authors/MyUtility.php');

          MyUtility::export(); // from Books/MyUtility.php
          MyUtility::exportAll(); // from Authors/MyUtility.php
        

Well dang...


          -> % php start.php
          PHP Fatal error:  Cannot declare class MyUtility, because
          the name is already in use in
          /Users/kevin/cakefest/Authors/MyUtility.php on line 3
        

What does this tell us?

PHP class names need to be unique?

What does this tell us?

PHP class names need to be unique?

NO!

What do we have to do?

We need to add Namespaces to our classes

Old Code


          // Books/MyUtility.php
          class MyUtility {
            public static function export(): void {
              echo "export function" . PHP_EOL;
            }
          }
        

Add Namespace


          // Books/MyUtility.php
          namespace Books;
          class MyUtility {
            public static function export(): void {
              echo "export function" . PHP_EOL;
            }
          }
        

Same with other class


          // Authors/MyUtility.php
          namespace Authors;
          class MyUtility {
            public static function exportAll(): void {
              echo "exportAll function" . PHP_EOL;
            }
          }
        

Lets try again


          -> % php start.php
          PHP Fatal error:  Uncaught Error: Class 'MyUtility'
          not found in /Users/kevin/cakefest/start.php:6
          Stack trace:
          #0 {main}
            thrown in /Users/kevin/cakefest/start.php on line 6
        

Well, another error 🙈

Whats the problem?

PHP doesn't know which Class to use

We have to tell it which to use

Old Code


          // start.php
          require('Books/MyUtility.php');
          require('Authors/MyUtility.php');

          MyUtility::export(); // from Books/MyUtility.php
          MyUtility::exportAll(); // from Authors/MyUtility.php
        

New Code


          // start.php
          require('Books/MyUtility.php');
          require('Authors/MyUtility.php');

          Books\MyUtility::export(); // from Books/MyUtility.php
          Authors\MyUtility::exportAll(); // from Authors/MyUtility.php
        

Result


          -> % php start.php
          export function
          exportAll function
        

success 🙌

To sum up

  • PHP class names need to be unique in their namespace
  • We use namespaces to group classes together
  • If we want to use a class we need to specify in which namespace it is living in

Namespaces in CakePHP

Lets just look into the src/Controller/AppController.php


          namespace App\Controller;

          use Cake\Controller\Controller;

          class AppController extends Controller
          {
            ....
          }
        

We can see, that

  • This class live in the namespace App\Controller
  • It uses the Controller class from Cake\Controller (which is the CakePHP cores namespace)

But why...

do we start with an App namespace and not with a src namespace?

composer.json


          "autoload": {
            "psr-4": {
              "App\\": "src/"
            }
          },
        

With this composer automatically loads all classes in the folder src in the namespace App

Same for CakePHP Core


          "autoload": {
            "psr-4": {
              "Cake\\": "src/"
            }
          },
        
https://github.com/cakephp/cakephp/blob/master/composer.json

And CakePHP Plugins


          "autoload": {
            "psr-4": {
              "DebugKit\\": "src/"
            }
          },
        
https://github.com/cakephp/debug_kit/blob/master/composer.json

How do I use that now?

Lets continue in the editor

IDE Support

But what about errors?

But what about errors?


          // Missing one of these
          use App\Utility\CoinSync;
          $coinSync = new \App\Utility\CoinSync();
        

Don't put use statements inside of functions


        function sync() {
          use App\Utility\CoinSync;
          $coinSync = new CoinSync();
        }
        Parse error: syntax error, unexpected token "use" in
        src/Controller/CoinsController.php on line 110
        

The 'use' keyword must be declared in the outermost scope of a file (the global scope) or inside namespace declarations

Watch the folder structure

Watch the folder structure

namespace App\Utility requires a folder structure of src/Utility

When should I not use a utility class (in CakePHP)?

Duplicate code used only in

  • Controllers => Component
  • Models => Behaviour
  • View/Templates => Elements