I need help understanding type hinting with objects. I tried searching stackoverflow but cannot find anything that has another user explain its use. If you find one let me know.
First let me explain what I do understand.
When using a type hint of array the user must type in a parameter that is an array otherwise its going to throw an error.
<?php
function something(array $myval)
{
return print_r($myval);
}
When i try it with an object i get an error. I might be writing it wrong, but please help me understand how to write it.
<?php
class Person
{
function name($name)
{
return $name;
}
}
$foo = new Person();
function doSomething(Person $lname)
{
return $lname->name;
}
doSomething('smith');
From what I understand when a function is type hinted of object Person (in this example) the parameter variable will have access to the objects methods just like when you instantiate an object and echo out its methods. I can be wrong but please correct me. My other question is that if this is true where a Person parameter has access to the Person methods what makes this any different from just instantiating the Person class and manually echoing out the methods.
Using your example:
$foo = new Person;
$foo->name = 'smith';
$something = doSomething($foo);
echo $something;
Type hinting means that whatever you pass must be an instance of (the same type as) the type you're hinting.
So, if you hint to Person only objects of that type will be accepted.
In the example you gave, you tried to pass a string instead of an object.
Update
"Type hinting" forces you to only pass objects of a particular type. This prevents you from passing incompatible values, and creates a standard if you're working with a team etc.
So, let's say you have a function sing(). You want to be sure that it will only accept objects of type Song.
Let's create our class Song:
class Song{
public $title;
public $lyrics;
}
and our function sing(). We will type hint to Song to ensure that no other type of params can be passed to it:
function sing(Song $song){
echo "Singing the song called " .$song->title;
echo "<p>" . $song->lyrics . "</p>";
}
Now, again, the function can ONLY accept objects of type Song because that's what we hinted to in the declaration (Song $song).
Let's create a Song and pass it:
$hit = new Song;
$hit->title = "Beat it!";
$hit->lyrics = "It doesn't matter who's wrong or right... just beat it!";
then we call:
sing($hit);
Which will work just fine.
Now, let's say we have a class Poem:
class Poem{
public $title;
public $lyrics;
}
$poem = new Poem;
$poem->title = "Look at the sea";
$poem->lyrics = "How blue, blue like the sky, in which we fly..."
If we try to call it using our function 'sing';
sing($poem)
we will get an error because $poem is not the type of object we've hinted to when creating the function sing().
Related
I'm trying to write a code where I could run a function based on it's previous variables. I don't know how to explain any better but a sample will do. I'm trying to do something like this:
<?php
$agric = new Agriculture;
$newplant = $agric-> setClass('plant');
$newanimal = $agric->setClass('animal');
$agric->getAll(); // returns null
$newplant->setProperties($plant1_data); //uses plant
$newanimal->setProperties($animal1_data); // uses animal
$newplant->setProperties($plant2_data); //uses plant
$newanimal->setProperties($animal2_data); // uses animal
$newplant->getAll(); // returns all plants array
$newanimal->getAll(); // returns all animals array
$agric->getAll(); // returns both plants array and animals
?>
So, In one form, the new variables calls the setClass in order to work and everytime it is is called they use their setClass method to know which type of argument they should use to run their code. I know I could do this differently but I seem to love this approach. Any help will do. Thanks in advance
Thanks #everyone.. I finally got the answer to that. So the reason why I'm giving it to #Dragos is because phone factory seems to be the best approach to it. So, all I did was to create two classes. One is Agriculture which has a method to get the properties of what was called using "getAll()" and the Other is HandleAgric which also has "getAll()". The HandleAgric has its own setClass which is static and every time it is called, it instantiates a new Agriculture with its default parameter. So, Both Classes will have setClass() and getAll() as method. Something like this
<?php
class HandleAgric{
private static $Agriculture;
public function __construct(){
self::setClass();
}
function __call($method,$args){
$self = new self;
if(method_exists($self::$Agriculture, $method)){
$call = call_user_func(array($self::$Agriculture, $method));
return $call;
}
return trigger_error("Error Found!!! Aborting");
}
public static function setClass($class=null){
self::$Agriculture = new Agriculture;
$call = self::$Agriculture->setClass($class);
return $call;
}
}
//Example of usage
$Agric = new HandleAgric();
$Plant = $Agric::setClass("plant");
$Animal = $Agric::setClass("animal");
$Plant->setProperties($arrayList);
$Animal->setProperties($arrayList);
# $Plant->getAll() //return plants properties in array;
# $Animal->getAll() //return Animal properties in array;
# $Agric->getAll() //return Agric (both plant and animal) properties in array;
?>
I believe this to be much better.
For your example to work, the method setClass from Agriculture must work like a factory: it instantiates a class based on the parameter and returns the object.
Agriculture class must also keep all the objects instantiated inside setClass method in its own internal array, so when getAll method is called, it iterates through each object and executes their own getAll methods.
As I got it, in the first example an object is created, and in the second one I don't see an object created. I am trying to understand, what is the difference between the two ways of method calling :
<?php
class Animal{
public $voice;
public function speak($sound){
echo $this->voice = $sound;
}
}
// Example 1
$tiger = new Animal();
$tiger->speak('Roar');
// Example 2
(new Animal)->speak("Cloak Cloak");
Whenever you use "new", you're creating an instance of an object (it can be temporary). The difference in your code is that in the first example, you're storing the instance in "$tiger", so it'll persist, but in the second example you're only instantiating a temporary object to call a method.
In the first example, you are assigning the variable $tiger as a new Object, by which you can then call the functions and variables associated with that object, by referencing $tiger.
i.e. as Tiger now equals an Object of Class Animal, it can speak.
However in the second example, you are still creating a new Object of class Animal, and as such it can speak - but you have not assigned it to a variable. So you cannot reference that same Object any longer.
So in the first example, if we wanted to name our $tiger, we could have the Class look something like this.
class Animal{
public $voice;
public $name = "I have no name.";
public function speak($sound){
echo $this->voice = $sound;
}
public function sayYourName(){
echo $this->name;
}
}
Now if we say,
$tiger = new Animal();
$tiger->speak('Roar');
$tiger->name = "Rory";
$tiger->sayYourName(); // This will echo "Rory"
However, if you try your second example instead :
(new Animal)->sayYourName(); // This will echo "I have no name."
So if you say :
(new Animal)->name = "Rory";
(new Animal)->sayYourName(); // This will still echo "I have no name".
This is because we haven't assigned a reference to the new animal, so while we can access methods of the function, and even predefined variables, we can't then reference them again later on.
To do that, we should stick to the first method (i.e. referencing)
$tiger = new Animal();
$tiger->name = "Rory";
$tiger->sayYourName();
In conclusion, use referencing to refer to an Object later on. i.e to get the animals attention, you have to call it by its name.
I have the following class with several properties and a method in PHP (This is simplified code).
class Member{
public $Name;
public $Family;
public function Fetch_Name(){
for($i=0;$i<10;$i++){
$this[$i]->$Name = I find the name using RegExp and return the value to be stored here;
$this[$i]->Family = I find the family using RegExp and return the value to be stored here;
}
}//function
}//class
In the function Fetch_Name(), I want to find all the names and families that is in a text file using RegExp and store them as properties of object in the form of an array. But I don't know how should I define an array of the Member. Is it logical or I should define StdClass or 2-dimension array instead of class?
I found slightly similar discussion here, but a 2 dimensional array is used instead of storing data in the object using class properties.
I think my problem is in defining the following lines of code.
$Member = new Member();
$Member->Fetch_name();
The member that I have defined is not an array. If I do define it array, still it does not work. I did this
$Member[]= new Member();
But it gives error
Fatal error: Call to a member function Fetch_name() on a non-object in
if I give $Member[0]= new Member() then I don't know how to make $Member1 or Member[2] or so forth in the Fetch_Name function. I hope my question is not complex and illogical.
Many thanks in advance
A Member object represents one member. You're trying to overload it to represent or handle many members, which doesn't really make sense. In the end you'll want to end up with an array that holds many Member instances, not the other way around:
$members = array();
for (...) {
$members[] = new Member($name, $family);
}
Most likely you don't really need your Member class to do anything really; the extraction logic should reside outside of the Member class, perhaps in an Extractor class or something similar. From the outside, your code should likely look like this:
$parser = new TextFileParser('my_file.txt');
$members = $parser->extractMembers();
I think you should have two classes :
The first one, Fetcher (or call it as you like), with your function.
The second one, Member, with the properties Name and Family.
It is not the job of a Member to fetch in your text, that's why I would make another class.
In your function, do your job, and in the loop, do this :
for($i = 0; $i < 10; ++$i){
$member = new Member();
$member->setName($name);
$member->setFamily($family);
// The following is an example, do what you want with the generated Member
$this->members[$i] = $member;
}
The problem here is that you are not using the object of type Member as array correctly. The correct format of your code would be:
class Member{
public $Name;
public $Family;
public function Fetch_Name(){
for($i=0;$i<10;$i++){
$this->Name[$i] = 'I find the name using RegExp and return the value to be stored here';
$this->Family[$i] = 'I find the family using RegExp and return the value to be stored here';
}
}
}
First, $this->Name not $this->$Name because Name is already declared as a member variable and $this->Name[$i] is the correct syntax because $this reference to the current object, it cannot be converted to array, as itself. The array must be contained in the member variable.
L.E: I might add that You are not writing your code according to PHP naming standards. This does not affect your functionality, but it is good practice to write your code in the standard way. After all, there is a purpose of having a standard.
Here you have a guide on how to do that.
And I would write your code like this:
class Member{
public $name;
public $family;
public function fetchName(){
for($i=0;$i<10;$i++){
$this->name[$i] = 'I find the name using RegExp and return the value to be stored here';
$this->family[$i] = 'I find the family using RegExp and return the value to be stored here';
}
}
}
L.E2: Seeing what you comented above, I will modify my answer like this:
So you are saying that you have an object of which values must be stored into an array, after the call. Well, after is the key word here:
Initialize your object var:
$member = new Memeber();
$memebr->fechNames();
Initialize and array in foreach
$Member = new Member();
foreach ($Member->Name as $member_name){
$array['names'][] = $member_name;
}
foreach ($Member->Family as $member_family) {
$array['family'][] = $member_family;
}
var_dump($array);
Is this more of what you wanted?
Hope it helps!
Keep on coding!
Ares.
I'm working through this tutorial:
http://www.killerphp.com/tutorials/object-oriented-php/php-objects-page-3.php
At first he has you create a setter and getter method in the class:
<?php
class person{
var $name;
function set_name($new_name){
$this->name=$new_name;
}
function get_name(){
return $this->name;
}
}
php?>
And then you create the object and echo the results:
<?php
$stefan = new person();
$jimmy = new person();
$stefan ->set_name("Stefan Mischook");
$jimmy ->set_name("Nick Waddles");
echo "The first Object name is: ".$stefan->get_name();
echo "The second Object name is: ".$jimmy->get_name();
?>
Works as expected, and I understand.
Then he introduces constructors:
class person{
var $name;
function __construct($persons_name) {
$this->name = $persons_name;
}
function set_name($new_name){
$this->name=$new_name;
}
function get_name(){
return $this->name;
}
}
And returns like so:
<?php
$joel = new person("Joel");
echo "The third Object name is: ".$joel->get_name();
?>
This is all fine and makes sense.
Then I tried to combine the two and got an error, so I'm curious-is a constructor always taking the place of a "get" function? If you have a constructor, do you always need to include an argument when creating an object?
Gives errors:
<?php
$stefan = new person();
$jimmy = new person();
$joel = new person("Joel Laviolette");
$stefan ->set_name("Stefan Mischook");
$jimmy ->set_name("Nick Waddles");
echo "The first Object name is: ".$stefan->get_name();
echo "The second Object name is: ".$jimmy->get_name();
echo "The third Object name is: ".$joel->get_name();
?>
It's giving you errors because the constructor has required parameters. To make a parameter optional give it a default value like this
function __construct($persons_name=null) {
if ($persons_name) {
$this->set_name($persons_name);//use the setter in the constructor.
}
}
this will now work
$stefan = new person();
$stefan ->set_name("Stefan Mischook");
$joel = new person("Joel Laviolette");
echo "The first Object name is: ".$stefan->get_name();
echo "The second Object name is: ".$joel->get_name();
A constructor is used to initialize an object. The expectation in object-oriented programming is that an object shouldn't exist unless it's in a valid state. For example, a Person without a first and last name might not be considered a valid entity, so when the object is first created it should be initialized with a first and last name in the constructor.
The reason you got an error is because the constructor has a required parameter, so you must pass an argument to it.
P.S. I really hate explanations of object-oriented programming that try to use analogies like "Dog is-a Mammal". You should probably stay away from those examples. They really give no helpful information in real world programming and sometimes even give students the illusion they understand how to use what they're being taught.
If you're looking for a practical application of using a constructor to create an object in valid state, imagine a blog post that uses a database for persistence.
For example, there would be no point writing a long post and then calling $BlogPost->save(); if the blog wasn't first initialized with a reference to the database. The application of using a constructor in this case would perhaps be
$BlogPost = new BlogPost($Database);
It would make no sense to have to write:
$BlogPost->setDatabase($Database);
every time you wanted to do anything with it. Maybe you'd forget to write it once and you'd be wondering where the post you spent 30 minutes writing disappeared to. That's an example of an invalid state.
The idea is that you're including anything the class is dependent upon when it is first initialized, instead of risking the possibility of the object being in an invalid state.
Edit: Corrected 'two parameters' to one.
Rails relies on some of the neat aspects of Ruby. One of those is the ability to respond to an undefined method.
Consider a relationship between Dog and Owner. Owner has_many :dogs and Dog belongs_to :owner.
If you go into script/console, get a dog object with fido = Dog.find(1), and look at that object, you won't see a method or attribute called Owner.
What you will see is an owner_id. And if you ask for fido.owner, the object will do something like this (at least, this is how it appears to me):
I'm being asked for my .owner attribute. I don't have one of those!
Before I throw a NoMethodError, do I have a rule about how to deal with this?
Yes, I do: I should check and see if I have an owner_id.
I do! OK, then I'll do a join and return that owner object.
PHP's documentation is - ahem - a bit lacking sometimes, so I wonder if anyone here knows the answer to this:
Can I define similar behavior for objects in PHP?
If not, do you know of a workaround for flexible model joins like these?
You can implement the __call() method in PHP which is a catch all for calling an otherwise inaccessible method.
class MyObj {
public function __call($name, $args) {
$list = $args ? '"' . implode('", "', $args) . '"' : '';
echo "Call $name($list)\n";
}
}
$m = new MyObj;
$m->method1(1, 2, 3);
$m->method2();
Some languages (eg Javascript) also have what are called first-class functions. This essentially allows you to add or remove methods from objects (or classes) on the fly. PHP syntax (as of 5.3) sort of supports this but it isn't really usable.
$obj = new stdClass;
$obj->foo = function() {
echo "hello\n";
};
print_r($obj);
Output:
stdClass Object
(
[foo] => Closure Object
(
)
)
But try:
$obj->foo();
and you get:
Fatal error: Call to undefined method stdClass::foo() in C:\xampp\htdocs\test.php on line 8
However:
$f = $obj->foo;
$f();
correctly outputs hello.
Overloading to the rescue!
After some further research, it appears that what I really wanted was PHP's overloading methods. The code at the link Gordon gave, and especially the downloadable example they offer, was very illuminating.
(Despite my question title, what I was really after was to have an object respond to undefined attributes.)
So, __get() and __set() let you specify methods to use for getting and setting object attributes, and within those methods, you can tell the object what to do if no such attribute exists. Let's just look at __get() for now.
Going back to my Dog example, you could use it like this:
class Dog{
// Private attributes, not accessible except through the __get method
private $bark_volume = 'loud';
private $owner_id = '5';
public function __get($name){
// If there's a property by that name, return it
if (isset($this->$name)){
return $this->$name;
}
// If not, let's see if there's an id with a related name;
// if you ask for $this->owner, we'll check for $this->owner_id
$join_id = $name . "_id";
if(isset($this->$join_id)){
// This is pretty useless, but the id could be used
// to do a join query instead
return $this->$join_id;
}
}
}
$Fido = new Dog;
echo $Fido->bark_volume; //outputs 'loud'
echo '<br/>';
echo $Fido->owner; //outputs '5'