23

I have a trait which 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{
    protected function getSetting($type, $setting){ // read setting from config.ini
        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;
    protected function connect(){ // connect to database
        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"))return false;// user is not logged in because we couldn't find session with username and/or password
        if(!$this->userExists($this->getSession("tysus"),$this->getSession("tyspw")))return false;// user is unknown to database
        return true;// other checks failed, user must be logged in
    }
}
?>

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 trait database and trait database would use trait settings and trait session.

If that was the case, I wouldn't get any errors, but unfortunately this isn't the case.

Does someone know how to fix this problem?

Funk Forty Niner
  • 74,450
  • 15
  • 68
  • 141
  • Why should I not use traits? Please explain. –  Dec 23 '13 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. – tereško Dec 23 '13 at 21:09
  • I don't understand why that's the case. –  Dec 26 '13 at 10:40
  • 3
    Furthermore, why catch an exception just to throw a new exception with the same message? – Justin E Jul 26 '14 at 17:37
  • Can you please update your question that it IS possible so we do not waste time on reading about a syntax error? ;) – Daniel Mar 25 '19 at 09:57

2 Answers2

40

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 analyse you 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);
 }
}
Mohd Belal
  • 1,119
  • 11
  • 23
  • 1
    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) – jerclarke Feb 14 '22 at 22:02
  • Is `class FileUtil { use Reader; use Copier; … }` (with `Copier` already containing `Reader`) an error, or what is happening then? – luckydonald Dec 08 '22 at 12:31
  • @luckydonald - Can you put down the error in your case here. – Mohd Belal Dec 11 '22 at 09:17
  • 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. – luckydonald Jan 04 '23 at 16:17
21

Maybe because readSetting is actually called getSetting ?

Noino
  • 593
  • 4
  • 13