23

I have a trait that is using another trait, and now I'm getting errors about functions that don't exist in classes.

I have simplified the code:

settings.php

<?php

trait settings {
    // read setting from config.ini
    protected function getSetting($type, $setting){
        try {
            $configFile = dirname(__FILE__)."/../config.ini";
            if (!file_exists($configFile) || !is_file($configFile))
                throw new Exception("Config file was not found. ");

            $configContents = parse_ini_file($configFile,true);
            if (is_array($configContents) && array_key_exists($type, $configContents) && is_array($configContents[$type]) && array_key_exists($setting, $configContents[$type]))
                return $configContents[$type][$setting];
            else
                throw new Exception("Setting ".$setting." could not be found in ".$type.".");
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
}

database.php

<?php
trait database {
    use settings, session;

    private $pdo;

    // connect to database
    protected function connect() {
        try {
            $this->pdo = new PDO(
                "mysql:host=".$this->getSetting("db", "host").";dbname=".$this->getSetting("db", "database"),
                $this->getSetting("db", "user"),
                $this->getSetting("db","password")
            );

            $this->init();
        } catch (PDOException $e) {
            throw new Exception($e->getMessage());
        }
    }
}

users.php

<?php

class users {
    use database;

    public function __construct() {
        try {
            $this->connect();
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    public function __destruct() {
        unset($this);
    }

    public function isAdmin() {
        try {
            if ($this->loginStatus() === true) {
            } else
                return false;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    public function loginStatus() {
        if (!$this->getSession("tysus") || !$this->getSession("tyspw"))
            // user is not logged in because we couldn't find session with username and/or password
            return false;

        if (!$this->userExists($this->getSession("tysus"), $this->getSession("tyspw")))
            // user is unknown to database
            return false;

        // other checks failed, user must be logged in
        return true;
    }
}

And now I'm getting this error:

Fatal error: Call to undefined method users::readSetting() in /home/deb2371/domains/nonamenohistory.com/public_html/include/classes/class.database.php on line 18

What I thought would happen was something like this: Class users uses database trait and that trait would use settings and session traits.

If that were the case, I wouldn't be getting any errors, but unfortunately, this isn't the case.

Does someone know how to fix this problem?

5
  • Why should I not use traits? Please explain. Commented Dec 23, 2013 at 20:41
  • Because, besides making huge mess regarding violations of SOLID principles, they also are actually implemented (under the hood) as interpreter-assisted copy-paste. Commented Dec 23, 2013 at 21:09
  • I don't understand why that's the case. Commented Dec 26, 2013 at 10:40
  • 3
    Furthermore, why catch an exception just to throw a new exception with the same message? Commented Jul 26, 2014 at 17:37
  • 1
    Can you please update your question that it IS possible so we do not waste time on reading about a syntax error? ;) Commented Mar 25, 2019 at 9:57

2 Answers 2

45

Code reuse is one of the most important aspects of object-oriented programming.

A simple example of Multiple Traits and Composing Multiple Traits by which you can easily analyze your situation.

  1. Using multiple traits

A class can use multiple traits. The following example demonstrates how to use multiple traits in the IDE class.

It simulates the C compilation model in PHP for the sake of demonstration.

<?php

trait Preprocessor {
    function preprocess() {
        echo 'Preprocess...done' . '<br/>';
    }
}

trait Compiler {
    function compile() {
        echo 'Compile code... done' . '<br/>';
    }
}

trait Assembler {
    function createObjCode() {
        echo 'Create the object code files... done.' . '<br/>';
    }
}

trait Linker {
    function createExec() {
        echo 'Create the executable file...done' . '<br/>';
    }
}

class IDE {
    use Preprocessor, Compiler, Assembler, Linker;

    function run() {
        $this->preprocess();
        $this->compile();
        $this->createObjCode();
        $this->createExec();

        echo 'Execute the file...done' . '<br/>';
    }
}

$ide = new IDE();
$ide->run();
  1. Composing multiple traits

A trait can be composed of other traits by using the use statement in the trait’s declaration. See the following example:

<?php

trait Reader {
    public function read($source) {
        echo sprintf("Read from %s <br/>", $source);
    }
}

trait Writer {
    public function write($destination) {
        echo sprintf("Write to %s <br/>", $destination);
    }
}

trait Copier {
    use Reader, Writer;

    public function copy($source, $destination) {
        $this->read($source);
        $this->write($destination);
    }
}

class FileUtil {
    use Copier;

    public function copyFile($source, $destination) {
        $this->copy($source, $destination);
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Clearly, considering the generic title on this question, this answer will be the correct one for 99% of visitors. Thanks for the nice detailed examples! (in my case, I only wound up here because of a "syntax error" of my own, but your examples clarified I had my traits configured right, and I found the problem, as predicted)
Is class FileUtil { use Reader; use Copier; … } (with Copier already containing Reader) an error, or what is happening then?
@luckydonald - Can you put down the error in your case here.
I was asking if it works. From what I've seen it actually isn't an error, and it works to include the same trait twice.
23

Your issue may be due to you using readSetting but it is actually called getSetting

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.