Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing an entire Class as a parameter within another Class

Tags:

methods

oop

php

So far I feel like I've understood the concept and the advantages of OOP programming, and I've not really had any difficulties with understanding how to work with classes in PHP.

However, this has left me just a little confused. I think I may understand it, but I'm still uncertain.

I've been following a set of video tutorials (not sure on the rules on linking to outside resources, but I found them on youtube), and they're pretty self explanatory. Except, frustratingly, when the tutor decided to pass one class as a parameter within another class. At least I think that's what's happening;

Class Game
{

    public function __construct()
    {
        echo 'Game Started.<br />';
    }
    public function createPlayer($name)
    {
        $this->player= New Player($this, $name);
    }
}


Class Player
{

    private $_name;

    public function __construct(Game $g, $name)
    {
        $this->_name = $name;
        echo "Player {$this->_name} was created.<br />";
    }
}

Then I'm instantiating an object of the Game class and calling its method;

$game = new Game();
$game-> createPlayer('new player');

Rather frustratingly, the tutor doesn't really explain why he has done this, and hasn't displayed, as far as I can see, any calls in the code that would justify it.

Is the magic method constructor in Player passing in the Game class as a reference? Does this mean that the entire class is accessible within the Player class by reference? When referencing $this without pointing to any particular method or property, are you referencing the entire class?

If this is what is happening, then why would I want to do it? If I've created a Player inside my Game Class, then surely I can just access my Player Properties and Methods within the Game Class, right? Why would I want my Game Class inside my Player class as well? Could I then, for example, call createPlayer() within the Player class?

I apologise if my explanation has been at all confusing.

I guess my question boils down to; what is it that I'm passing as a parameter exactly, and why would I want to do it in every day OOP programming?

like image 974
Martyn Shutt Avatar asked Oct 29 '12 00:10

Martyn Shutt


3 Answers

It's called type hinting and he's not passing the entire class as a parameter but ratter hinting the Class Player about the type of the first parameter

PHP 5 introduces type hinting. Functions are now able to force parameters to be objects (by specifying the name of the class in the function prototype), interfaces, arrays (since PHP 5.1) or callable (since PHP 5.4). However, if NULL is used as the default parameter value, it will be allowed as an argument for any later call.

(Extracted from php manual)

Does this mean that the entire class is accessible within the Player class by reference?

Not the entire class, but you can access the an instance of the class you are passing as a parameter

like image 72
Bruno Vieira Avatar answered Oct 21 '22 09:10

Bruno Vieira


The method DOES expect to get the an object which is an instance of Game anything else, and it will error out.

You are passing an instance of Game just here: New Player($this, $name); The key word $this refers to the object instance you are in.

And last....I (and nobody else for that matter) has any idea why the author did it, as he is not using the Game instance after he passes it.

Why would u pass a Instance of a class?
Imagine a Class that accepts Users as input and according to some logic does something with them. (No comments in code, as function name and class name are supposed to be self-explanatory)

class User{
  protected $name,$emp_type,$emp_id;
  protected $department;

  public function __construct($name,$emp_type,$emp_id){
     $this->name = $name;
     $this->emp_type = $emp_type;
     $this->emp_id = $emp_id;
  }

  public function getEmpType(){
     return $this->emp_type;
  }

  public function setDep($dep){
    $this->department = $dep;
  }
}


class UserHub{

  public function putUserInRightDepartment(User $MyUser){
      switch($MyUser->getEmpType()){
           case('tech'):
                $MyUser->setDep('tech control');
                break;
           case('recept'):
                $MyUser->setDep('clercks');
                break;
           default:
                $MyUser->setDep('waiting HR');
                break;
      }
  }
}

$many_users = array(
    0=>new User('bobo','tech',2847),    
    1=>new User('baba','recept',4443), many more
}

$Hub = new UserHub;
foreach($many_users as $AUser){
   $Hub->putUserInRightDepartment($AUser);
}
like image 40
Itay Moav -Malimovka Avatar answered Oct 21 '22 09:10

Itay Moav -Malimovka


/**
* Game class.
*/
class Game implements Countable {
    /**
    * Collect players here.
    * 
    * @var array
    */
    private $players = array();

    /**
    * Signal Game start.
    * 
    */
    public function __construct(){
        echo 'Game Started.<br />';
    }

    /**
    * Allow count($this) to work on the Game object.
    * 
    * @return integer
    */
    public function Count(){
        return count($this->players);
    }

    /**
    * Create a player named $name.
    * $name must be a non-empty trimmed string.
    * 
    * @param string $name
    * @return Player
    */
    public function CreatePlayer($name){
        // Validate here too, to prevent creation if $name is not valid
        if(!is_string($name) or !strlen($name = trim($name))){
            trigger_error('$name must be a non-empty trimmed string.', E_USER_WARNING);
            return false;
        }
        // Number $name is also not valid
        if(is_numeric($name)){
            trigger_error('$name must not be a number.', E_USER_WARNING);
            return false;
        }
        // Check if player already exists by $name (and return it, why create a new one?)
        if(isset($this->players[$name])){
            trigger_error("Player named '{$Name}' already exists.", E_USER_NOTICE);
            return $this->players[$name];
        }
        // Try to create... this should not except but it's educational
        try {
            return $this->players[$name] = new Player($this, $name);
        } catch(Exception $Exception){
            // Signal exception
            trigger_error($Exception->getMessage(), E_USER_WARNING);
        }
        // Return explicit null here to show things went awry
        return null;
    }

    /**
    * List Players in this game.
    * 
    * @return array
    */
    public function GetPlayers(){
        return $this->players;
    }

    /**
    * List Players in this game.
    * 
    * @return array
    */
    public function GetPlayerNames(){
        return array_keys($this->players);
    }
} // class Game;


/**
* Player class.
*/
class Player{
    /**
    * Stores the Player's name.
    * 
    * @var string
    */
    private $name = null;

    /**
    * Stores the related Game object.
    * This allows players to point to Games.
    * And Games can point to Players using the Game->players array().
    * 
    * @var Game
    */
    private $game = null;

    /**
    * Instantiate a Player assigned to a Game bearing a $name.
    * $game argument is type-hinted and PHP makes sure, at compile time, that you provide a proper object.
    * This is compile time argument validation, compared to run-time validations I do in the code.
    * 
    * @param Game $game
    * @param string $name
    * @return Player
    */
    public function __construct(Game $game, $name){
        // Prevent object creation in case $name is not a string or is empty
        if(!is_string($name) or !strlen($name = trim($name))){
            throw new InvalidArgumentException('$name must be a non-empty trimmed string.');
        }
        // Prevent object creation in case $name is a number
        if(is_numeric($name)){
            throw new InvalidArgumentException('$name must not be a number.');
        }
        // Attach internal variables that store the name and Game
        $this->name = $name;
        $this->game = $game;
        // Signal success
        echo "Player '{$this->name}' was created.<br />";
    }

    /**
    * Allow strval($this) to return the Player name.
    * 
    * @return string
    */
    public function __toString(){
        return $this->name;
    }

    /**
    * Reference back to Game.
    * 
    * @return Game
    */
    public function GetGame(){
        return $this->game;
    }

    /**
    * Allow easy access to private variable $name.
    * 
    * @return string
    */
    public function GetName(){
        return $this->name;
    }
} // class Player;

// Testing (follow main comment to understand this)
$game = new Game();
$player1 = $game->CreatePlayer('player 1');
$player2 = $game->CreatePlayer('player 2');
var_dump(count($game)); // number of players
var_dump($game->GetPlayerNames()); // names of players

Rewrote your code in a nicer way and added some missing variables that made that code pointless:

  • In player class you don't store the game.
  • In game class, you support only one player.
  • No error checking... anywhere.

Fixed all those plus:

  • Added exceptions (to prevent object creation)
  • Try{} catch(...){} Exception handling any OOP dev should know
  • Implemented Countable interface to allow count($game) players
  • Some more tricks that will give you a good read

Follow the comments and I hope your code will make more sense after you read it.

like image 29
CodeAngry Avatar answered Oct 21 '22 08:10

CodeAngry