i create a Depot class. when i create object from this class i use find method for find a Special item with id.
after that i cant call any other method.
I do not use Laravel
// index.php file
$depot = new Depot();
$depot = $depot->find(2);
var_dump($depot->hi());
Fatal error: Uncaught Error: Call to undefined method stdClass::hi()
hi method is for test.
// model.php file
class Model {
// ...
public function find(int $id)
{
$statement = $this->pdo->prepare("select * from {$this->table} where id = :id");
$statement->execute(compact('id'));
$obj = $statement->fetch(PDO::FETCH_OBJ);
return $obj;
}
}
class Depot extends Model {
//...
public function hi()
{
echo "hi";
}
}
With this line:
$depot = $depot->find(2);
you're overwriting the variable $depot, representing your object, with the result of your query. The object returned (unsurprisingly) doesn't contain a function called hi().
I don't know if this was just a typo, but if not, it's generally a sign of poor code quality if you re-use the same variable to contain two completely different things. It leads to maintenance and readability issues, and often causes errors further down the line, such as this one, where you mistakenly assume the variable still has its original content. Weakly-typed languages such as PHP are especially vulnerable to this kind of mistake. The easiest thing is to just make a rule never to do it.
Assigning the result to a different variable, e.g.
$depot = new Depot();
$findResult = $depot->find(2);
$depot->hi();
will fix the issue.
(Also the var_dump() was unnecessary since hi() already contains an echo.)
try this
$depot = new Depot();
$depotDb = $depot->find(2);
var_dump($depot->hi());
Related
I've been busy trying to create my own framework (to become more experienced in this area), and stumbled on an error I couldn't fix by searching google... wow...
I want to get data from a database, placed in an object / class. I've done it before, in a different way I learned at school, but I wanted to tweak it and make it more dynamic so I could use it in my framework.
The problem I stumbled on is the following:
SQLSTATE[HY000]: General error: could not call class constructor on line 96
This is the function in my database class:
public function getObject($query, $classRootPath)
{
try {
//Check if slashes are double already and make them if not
if(!strpos($classRootPath, "\\\\")) {
$classRootPath = str_replace("\\","\\\\",$classRootPath);
}
$statement = $this->pdo->prepare($query);
$statement->execute(\PDO::FETCH_CLASS, "Campers\\Camper"); // I want this path to be $classRootPath once it is working with this dummy data
return $statement->fetchAll();
// return $this->pdo->query($query)->fetchAll(\PDO::FETCH_CLASS, "Campers\\Camper");
} catch (\PDOException $e) {
throw new \Exception("DB receive object failed: " . $e->getMessage());
}
}
This function is nested in Database and the class is called Database / Database.php
The following class is nested in Campers and is called Camper.php
class Camper {
public $ID, $date, $camperID;
public function __construct($ID, $date, $camperID)
{
$this->ID = $ID;
$this->date = $date;
$this->camperID = $camperID;
}
}
The only reason I can think of this is not working, is that the call "Campers\\Camper" is calling on top of Database, but I don't know how to escape that. I tried with ..\ but I got errors back, and this is the closest I can get. Here it can find the class though, but it can't find the constructor of Camper...
I've tested if my db class / connection works, so that's not the fault.
The structure of my table matches my Campers class constructor.
From the PSR-4 spec:
The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name.
You likely can't instantiate that Camper class as-is anyway. PSR-4 expects your filename to match the class. It should be located in framework/Campers/Camper.php.
This error implies more than been unable to call the constructor, it is also used to indicate than an error occurred while calling it.
In my case, an Exception was been thrown inside de constructor. If you don't print/log the stacktrace, you could easily miss it.
Enjoy!
:)
I had the same issue in at least 3 cases.
Case 1: You select something from the database that can contain a NULL value.
SELECT name FROM tableX;
In that case I do the select in that way:
SELECT IFNULL(name,'') AS name FROM tableX;
where name is a field in your class.
Case 2: You select something that is not a field in your class
class Example {
public string $name = '';
}
Then the following query will fail as id is not declared in your class
SELECT id, name FROM tableX;
case3:
your field in the class isn't initialised
class Example {
public string $name;
}
SELECT name FROM tableX;
can be solved by either initialise the field
class Example {
public string $name = '';
}
or using a constructor to declare it
BR
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.
Assume this class code:
class Foo {
function method() {
echo 'works';
}
}
Is there any way to store a reference to the method method of a Foo instance?
I'm just experimenting and fiddling around, my goal is checking whether PHP allows to call $FooInstance->method() without writing $FooInstance-> every time. I know I could write a function wrapper for this, but I'm more interested in getting a reference to the instance method.
For example, this pseudo-code would theoretically store $foo->method in the $method variable:
$foo = new Foo();
$method = $foo->method; //Undefined property: Foo::$method
$method();
Apparently, as method is a method and I'm not calling it with () the interpreter thinks I'm looking for a property thus this doesn't work.
I've read through Returning References but the examples only show how to return references to variables, not methods.
Therefore, I've adapted my code to store an anonymous function in a variable and return it:
class Foo {
function &method() {
$fn = function() {
echo 'works';
};
return $fn;
}
}
$foo = new Foo();
$method = &$foo->method();
$method();
This works, but is rather ugly. Also, there's no neat way to call it a single time, as this seems to require storing the returned function in a variable prior to calling it: $foo->method()(); and ($foo->method())(); are syntax errors.
Also, I've tried returning the anonymous function directly without storing it in a variable, but then I get the following notice:
Notice: Only variable references should be returned by reference
Does this mean that returning/storing a reference to a class instance method is impossible/discouraged or am I overlooking something?
Update: I don't mind adding a getter if necessary, the goal is just getting a reference to the method. I've even tried:
class Foo {
var $fn = function() {
echo 'works';
};
function &method() {
return $this->fn;
}
}
But from the unexpected 'function' (T_FUNCTION) error I'd believe that PHP wisely doesn't allow properties to store functions.
I'm starting to believe that my goal isn't easily achievable without the use of ugly hacks as eval().
It is. You have to use an array, with two values: the class instance (or string of the class name if you are calling a static method) and the method name as a string. This is documented on the Callbacks Man page:
A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
Demo (Codepad):
<?php
class Something {
public function abc() {
echo 'called';
}
}
$some = new Something;
$meth = array($some, 'abc');
$meth(); // 'called'
Note this is also works with the built-ins that require callbacks (Codepad):
class Filter {
public function doFilter($value) {
return $value !== 3;
}
}
$filter = new Filter;
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array($filter, 'doFilter'))); // 'array(1,2,4,5)'
And for static methods -- note the 'Filter' instead of an instance of a class as the first element in the array (Codepad):
class Filter {
public static function doFilter($value) {
return $value !== 3;
}
}
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array('Filter', 'doFilter'))); // 'array(1,2,4,5)'
// -------- or -----------
var_dump(array_filter($test, 'Filter::doFilter')); // As of PHP 5.2.3
Yes, you can. PHP has a "callable" pseudo-type, which is, in fact, either just a string or an array. Several functions (usort comes to mind) accept a parameter of the "callback" type: in fact, they just want a function name, or an object-method pair.
That's right, strings are callable:
$fn = "strlen";
$fn("string"); // returns 6
As mentioned, it's possible to use an array as a callback, too. In that case, the first element has to be an object, and the second argument must be a method name:
$obj = new Foo();
$fn = array($obj, "method");
$fn(); // calls $obj->method()
Previously, you had to use call_user_func to call them, but syntax sugar in recent versions make it possible to perform the call straight on variables.
You can read more on the "callable" documentation page.
No, as far as I know it's not possible to store a reference to a method in PHP. Storing object / class name and a method name in an array works, but it's just an array without any special meaning. You can play with the array as you please, for example:
$ref = [new My_Class(), "x"];
// all is fine here ...
$ref();
// but this also valid, now the 'reference' points to My_Other_Class::x()
// do you expect real reference to behave like this?
$ref[0] = new My_Other_Class();
$ref();
// this is also valid syntax, but it throws fatal error
$ref[0] = 1;
$ref();
// let's assume My_Class::y() is a protected method, this won't work outside My_Class
$ref = [new My_Class(), 'y'];
$ref();
this is prone to error as you loose syntax checking due to storing the method name as string.
you can't pass reliably a reference to a private or a protected method this way (unless you call the reference from a context that already has proper access to the method).
Personally I prefer to use lambdas:
$ref = function() use($my_object) { $my_object->x(); }
If you do this from inside $my_object it gets less clunky thanks to access to $this:
$ref = function() { $this->x(); }
this works with protected / private methods
syntax checking works in IDE (less bugs)
unfortunately it's less concise
I've used php enough to be quite comfortable with it, but recently I've been looking through some MVC frameworks to try and understand how they work, and I've come across a syntax and data structure which I haven't encountered before:
function view($id)
{
$this->Note->id = $id;
}
What is the ->id section of this code? Is this a sub-method based off it's parent method? If so, how do I go about writing code to create such a structure? (ie. creating the structure from scratch, not using an existing framework like the above example from cakephp).
The following code demonstrates how one could arrive at the structure you described.
<?php
class Note
{
public $id = 42;
}
class MyClass
{
public function __construct() {
// instance of 'Note' as a property of 'MyClass'
$this->Note = new Note();
}
public function test() {
printf("The \$id property in our instance of 'Note' is: %d\n",
$this->Note->id);
}
}
$mc = new MyClass();
$mc->test();
?>
Note is a property of $this and it's (current) value is an object with a property named id which gets assigned the value of $id.
If id was a method of the Note object, the line would read $this->Note->id($id);.
Another way to think about the construct is considering
$this->Note->id = $id;
similar to
$this["Note"]["id"] = $id;
Which would actually be equivalent if both objects ($this and subobject Note) were based on ArrayAccess.
I'm not quite sure how to correctly put this question. I want to dynamically call functions that are contained in classes (I think this means they are called 'methods').
Here is an example of my code which I hope helps explain what I am trying to achieve.
In this instance $result returns all the different modules that are loaded. This then checks if the module's PHP file has been included with it's class, then if that class exists - trys to call the class directly.
foreach ($results as $result) {
$moduleclass_name = 'TestClassName_' . $result->module_name . '::FunctionToCall';
if (method_exists($moduleclass_name, 'FunctionToCall'))
$VariableToRetrieve = $modulefunction_name($Parameter1, $Parameter2);
}
This returns an error
"Call to undefined function
TestClassName_modulename::FunctionToCall()"
although the 'TestClassName' has been declared correctly.
Can someone tell me what I'm doing wrong?
What you want is probably call_user_func_array().
The code would look similar to this:
call_user_func_array(array($classNameOrInstance, $functionName), array($arg1, $arg2, $arg3));
EDIT Also, in your example you seem to have included the function name in the class parameter for method_exists, too...
You may use call_user_func() as such to achieve what you are trying to do. Also, it is better to use is_callable() instead of method_exists() to validate if the method is callable (method may exist but its visibility may prevent it from being callable.
foreach ($results as $result) {
$module_callback = array('TestClassName_' . $result->module_name,'FunctionToCall');
if (is_callable($module_callback))
$VariableToRetrieve = call_user_func($module_callback, $Parameter1, $Parameter2);
}
I think it doesn't work because your syntax may not support "static method calls".
I suggest you give a try to Franz's method call_user_func().
I did a similar thing on a former project.
It was designed to call a class which implemented an interface, so method names where known.
I don't think it's difficult to modify this code in order to make it match with yours.
class CDispatcher {
public static function GetDispatcher( $module = 'core' ) {
$class_name = $module . 'Dispatcher';
try {
// looks for the file associated with the class
// if the file is not found an exception is raised
search_class( $class_name );
} catch ( exception $e ){
throw new UnkwownModuleException($module);
}
return new $class_name();
}
}
// Then, you call this class :
$new_instance = CDispatcher::GetDispatcher( $my_module );