I'm sorry if my question already asked before. I don't know what the search term that match my problem. I've searched 'OOP child', 'PHP object child', etc but I got no clue at all.
So, here we go. I wanted to make something like this:
$school = new School;
var_dump($school);
And resulting something like this:
object(School)#1 (2) {
["name"]=>
string(14) "All Way School"
["object"]=>
object(School)#2 (2) {
["art"]=>
object(School)#3 (1) {
["student"]=>
int(25)
}
["law"]=>
object(School)#4 (1) {
["student"]=>
int(30)
}
}
}
See the output. object has 2 child: art and law, each has a child student. This way, I can access law's total students by $school->object->law->student. But, I don't know what to write in my class. All I know I can only make $school->name in my School class like this:
class School {
$name = "All Way School";
}
I don't know how to make $object and its child.
Note: I'm just guessing the output. I'm really don't understand how to get it. I'm inspired by SimpleXMLElement.
Edit: I'm replacing $class with $object to prevent confusion.
The OOP 'way' would be to have a different object for each School, Subject (you can't use Class, as it's a reserved keyword) and Student. Define them as normal, with specific properties:
Class School {
// methods, vars etc
}
Class Subject {
// ...
}
Then, you can create new instances of Class within School and assign them to a variable:
Class School {
var $subjects;
function __construct() {
$this->subjects = new Array();
}
}
$mySchool = new School();
$mySchool->subjects[] = new Subject();
Then you can get the subjects using:
$mySchool->subjects[1]->name;
Hope that helps
You can make a child the same way you assigned a name:
class School {
$name = "All Way School";
public function __construct() { // executed when creating a new School object
$this->classes = array(
'art' => new SchoolClass('art'),
'law' => new SchoolClass('law')
);
}
}
You can do the same for the SchoolClass class, making it contain all the students;
class SchoolClass {
$name;
public function __construct( $name ) {
$this->name = $name; // law, art, etc
$this->students = array(
new SchoolStudent( 2 ),
new SchoolStudent( 5 )
);
}
}
(Note that you would usually not put the students hard-coded in the constructor like this, but rather load them from a data source.)
Then count the students for instance:
$School = new School();
echo count( $School->classes['law']->students );
class School {
public $name, $class;
public function __construct() {
$this->class = new SchoolClass();
$this->name = "All Way School";
}
}
class SchoolClass {
public $art, $law;
public function __construct() {
$this->art = new SchoolStudent(25);
$this->law = new SchoolStudent(30);
}
}
class SchoolStudent {
public $student;
public function __construct($student) {
$this->student = $student;
}
}
$school = new School();
var_dump($school);
var_dump($school->class->law->student);
Will give you exactly this:
object(School)#1 (2) {
["name"]=>
string(14) "All Way School"
["class"]=>
object(SchoolClass)#2 (2) {
["art"]=>
object(SchoolStudent)#3 (1) {
["student"]=>
int(25)
}
["law"]=>
object(SchoolStudent)#4 (1) {
["student"]=>
int(30)
}
}
}
int(30)
If you don't want to type every class by hand, read about overloading in PHP and beautiful __set()/__get() methods.
Typecasting can be other solution that fits:
$school = (object) array(
"name" => "All Way School",
"object" => (object) array(
"art" => (object) array("student" => 25),
"law" => (object) array("student" => 30),
),
);
var_dump($school);
var_dump($school->object->law->student);
Which gives you
object(stdClass)#4 (2) {
["name"]=>
string(14) "All Way School"
["object"]=>
object(stdClass)#3 (2) {
["art"]=>
object(stdClass)#1 (1) {
["student"]=>
int(25)
}
["law"]=>
object(stdClass)#2 (1) {
["student"]=>
int(30)
}
}
}
int(30)
I understand that all this is not what you're looking for, but I hope that these examples will help you understand your own needs better and ask the right question. Your current question is vague and unclear: even if I gave you literally what you've asked, you ain't going to accept my answer.
You need a seperate class for each object, they can;t all be instances of School. For example, Your class School would have two properties, name and subjects. Then you would have an class called subject which contains a name and a numbver of students. Note that I have used the word 'subject' instead of 'class' - this is because 'class' is a reserved word in PHP and even if it weren't, it would get very confusing if you had a class called 'class'.
So you might do something like this:
<?php
class School {
public $name;
public $subjects = array();
function __construct ($name) {
$this->name = $name;
}
function addSubject ($subject) {
// Adds a new subject object
if (is_string($subject)) $subject = new Subject($subject);
$this->subjects[$subject->name] = $subject;
}
function getSubjectByName ($name) {
// returns a Subject object or FALSE on failure
return (isset($this->subjects[$name])) ? $this->subjects[$name] : FALSE;
}
function getStudentsBySubjectName ($name) {
// returns number of students for a subject or FALSE on failure
return (isset($this->subjects[$name])) ? $this->subjects[$name]->numberOfStudents : FALSE;
}
// ... more methods
}
class Subject {
public $name;
public $numberOfStudents;
function __construct ($name, $students = 0) {
$this->name = $name;
$this->numberOfStudents = $students;
}
// ... more methods
}
$school = new School('Useless City High');
$school->addSubject(new Subject('Math', 2));
$school->addSubject('Stupidity Studies');
$school->subjects['Stupidity Studies']->numberOfStudents = 65;
$school->addSubject(new Subject('Law'));
var_dump($school);
$math = $school->getSubjectByName('Math');
echo $math->numberOfStudents; // outputs '2'
echo $school->getStudentsBySubjectName('Math'); // outputs '2'
$stupidity = $school->subjects['Stupidity Studies'];
echo $stupidity->numberOfStudents; // outputs '65'
echo $school->getStudentsBySubjectName('Stupidity Studies'); // outputs '65'
echo $school->getStudentsBySubjectName('Law'); // outputs '0'
echo $school->subjects['Law']->numberOfStudents; // outputs '0'
?>
While you could write this in a single class that contains more instances, it doesn't make semantic sense to do so in this situation.
A SimpleXML element is one object that contain children that are instances of itself. This is because that is how XML works - any element can contain any other element (within reason). E.g. you can have <div><div><div>Some Data</div></div></div> but equally you could just have <div>Some Data</div>. All those divs are XML elements - they are all the same type of object. They can potentially contain more of themselves, but this situation is rare.
You are dealing with 2 distinct types of object - a School and a Subject. Think of it like the real world - a School won't contain another School, a Suject wont contain another Subject, and a Subject will not exist outside a School.
You could take the above example a step further, and create a class called Student since this is another distict type. A Student might be contained by a School or a Subject, but it will never contain either of them. Equally, say you had a Book class, a Student could have one, a Subject could have one, or a School could have one. But a Book would never contain a whole School - it just doesn't make sense.
OOP is all about objects. Think about it in terms of the real world, it will help you make sense of what classes you need, and how they can interact with each other.
Related
all. I am working on a custom-build API built in PHP. I have an array (see below) that has been returned from my database access object. When I use the json_encode function, the int value assigned to phone1Type gets encoded incorrectly. I have tried this with multiple records, and the value in the encoded json object always matches the phone2Type. Any ideas as to what might be going on? (I've included two sample arrays below in addition to their corresponding json object.)
The code that I'm using to check the array and json values is the following:
$responseObject = $userCtrl->selectPersonForUserId($userId);
var_dump($responseObject);
var_dump(json_encode($responseObject));
One example array to encode is as follows. (The phone1Type and phone2Type keys are at the very end, but include the full array here for completeness. Also, as a side note, the other int values in the array are encoding fine.)
object(Adult)#13 (8) {
["person":protected]=>
object(Person)#14 (4) {
["id":protected]=>
int(3)
["firstName":protected]=>
string(7) "William"
["lastName":protected]=>
string(3) "Smith"
["hasVerified":protected]=>
bool(false)
}
["address":protected]=>
object(Address)#17 (4) {
["id":protected]=>
int(2)
["address1":protected]=>
string(15) "520 Hilbert Dr."
["address2":protected]=>
string(0) ""
["city":protected]=>
object(City)#18 (3) {
["zip":protected]=>
string(5) "71342"
["city":protected]=>
string(11) "West Monroe"
["state":protected]=>
string(2) "AL"
}
}
["email":protected]=>
string(14) "wmrmay#spam.com"
["phone1":protected]=>
string(10) "6195080000"
["phone1Type":protected]=>
int(1)
["phone2":protected]=>
string(10) "3188126574"
["phone2Type":protected]=>
int(0)
["teacher":protected]=>
NULL
}
This encodes to the following json object:
{"person":{"id":3,"firstName":"William","lastName":"Smith","hasVerified":false},"address":{"id":2,"address1":"520 Hilbert Dr.","address2":"","city":{"zip":"71342","city":"West Monroe","state":"AL"}},"email":"wmrmay#spam.com","phone1":"6195080000","phone1Type":0,"phone2":"3188126574","phone2Type":0,"teacher":null}
For brevity, here's the last few lines of another array followed by its json counterpart:
["email":protected]=>
string(20) "wltrallen2#gmail.com"
["phone1":protected]=>
string(10) "6192047586"
["phone1Type":protected]=>
int(1)
["phone2":protected]=>
NULL
["phone2Type":protected]=>
NULL
["teacher":protected]=>
NULL
"email":"wltrallen2#gmail.com","phone1":"6192047586","phone1Type":null,"phone2":null,"phone2Type":null,"teacher":null}
Edited to add original Adult.php model class:
class Adult implements JsonSerializable {
protected $person; // Person object
protected $address; // Address object
protected $email;
protected $phone1;
protected $phone1Type; // PhoneType object
protected $phone2;
protected $phone2Type; // PhoneType object
protected $teacher; // Teacher object
public function __construct($person, $address, $email, $phone1, $phone1Type, $phone2, $phone2Type, $teacher)
{
$this->person = $person;
$this->address = $address;
$this->email = $email;
$this->phone1 = $phone1;
$this->phone1Type = $phone1Type;
$this->phone2 = $phone2;
$this->phone2Type = $phone2Type;
$this->teacher = $teacher;
}
... // Getters and Setters removed for brevity
private function getPhoneType($type) {
if(PhoneTypes::isValid($type)) {
return PhoneTypes::StringDict[$type];
}
return '';
}
function jsonSerialize() {
$array = [
'person' => $this->person,
'address' => $this->address,
'email' => $this->email,
'phone1' => $this->phone1,
'phone1Type' => $this->phone2Type,
'phone2' => $this->phone2,
'phone2Type' => $this->phone2Type,
'teacher' => $this->teacher
];
return $array;
}
}
So, embarrassingly, this was just caused by an initial typo and then forgetfulness on my part.
In the Adult.php model class, I has implemented the JsonSerializable interface to make sure that an Adult object could be encoded in Json. When doing so, I made a typographical error when building the array:
'phone1Type' => $this->phone2Type,
which should have been, of course...
'phone1Type' => $this->phone1Type,
This typo was the source of my issue. Ugh!
However, as I've been deeply mired in this project and it had been some time since I originally built those model classes, I had completely forgot that for an object to be encodable by JSON that it had to implement the JsonSerializable interface. So, as I was debugging, it never occurred to me to look back in my model at the very end of the file to examine the jsonSerialize() function. (Insert face palm here.)
Thank you for your responses. This is the first time that I've actually gotten to post a question on stackOverflow, and I appreciate you all taking a look. Sorry that it wasn't an exciting question and merely a silly novice programmer moment.
I just found something "kinda strange" about PHP 7.4 and I am not sure if it is just me missing something or maybe if it is an actual bug. Mostly I am interested in your opinion/confirmation.
So in PHP, you can iterate over objects properties like this:
class DragonBallClass
{
public $goku;
public $bulma = 'Bulma';
public $vegeta = 'Vegeta';
}
$dragonBall = new DragonBallClass();
foreach ($dragonBall as $character) {
var_dump($character);
}
RESULT
NULL
string(5) "Bulma"
string(6) "Vegeta"
Now if we start using strongly typed properties like that:
class DragonBallClass
{
public string $goku;
public string $bulma = 'Bulma';
public string $vegeta = 'Vegeta';
}
$dragonBall = new DragonBallClass();
foreach ($dragonBall as $character) {
var_dump($character);
}
We will get a different result:
string(5) "Bulma"
string(6) "Vegeta"
Now what is different:
When you DO NOT assign a default value to strongly typed property it will be of Uninitialized type. Which of course makes sense. The problem is though that if they end up like this you cannot loop over them they will simply be omitted - no error, no anything as you can see in the second example. So I just lose access to them.
It makes sense but just imagine that you have a custom Request/Data class like this:
namespace App\Request\Request\Post;
use App\Request\Request\Request;
class GetPostsRequest extends Request
{
public string $title = '';
}
Do you see that ugly string assignment? If I want to make my properties on the class iterable then I have to either:
drop types
assign dummy values
I might want to have an object with typed properties without any values in them to loop over them and populate them if that makes sense.
Is there any better way of doing this? Is there any option to keep types and keep em iterable without having to do this dummy value abomination?
If you want to allow a typed attribute to be nullable you can simply add a ? before the type and give NULL as default value like that:
class DragonBallClass
{
public ?string $goku = NULL;
public string $bulma = 'Bulma';
public string $vegeta = 'Vegeta';
}
In this case NULL is a perfectly legitimate value (and not a dummy value).
demo
Also without using ?, you can always merge the class properties with the object properties lists:
class DragonBallClass
{
public string $goku;
public string $bulma = 'Bulma';
public string $vegeta = 'Vegeta';
}
$dragonBall = new DragonBallClass();
$classProperties = get_class_vars(get_class($dragonBall));
$objectProperties = get_object_vars($dragonBall);
var_dump(array_merge($classProperties, $objectProperties));
// array(3) {
// ["goku"]=>
// NULL
// ["bulma"]=>
// string(5) "Bulma"
// ["vegeta"]=>
// string(6) "Vegeta"
// }
Before we start - I think that the answer accepted by me and provided by Casimir is better and more correct than what I came up with(that also goes for the comments).
I just wanted to share my thoughts and since this is a working solution to some degree at least we can call it an answer.
This is what I came up with for my specific needs and just for fun. I was curious about what I can do to make it more the way I want it to be so don't freak out about it ;P I think that this is a quite clean workaround - I know it's not perfect though.
class MyRequest
{
public function __construct()
{
$reflectedProperties = (new \ReflectionClass($this))->getProperties();
foreach ($reflectedProperties as $property) {
!$property->isInitialized($this) ??
$property->getType()->allowsNull() ? $property->setValue($this, null) : null;
}
}
}
class PostRequest extends MyRequest
{
public ?string $title;
}
$postRequest = new PostRequest();
// works fine - I have access here!
foreach($postRequest as $property) {
var_dump($property);
}
The downfall of this solution is that you always have to make types nullable in your class. However for me and my specific needs that is totally ok. I don't mind, they would end up as nullables anyway and it might be a nice workaround for a short deadline situation if someone is in a hurry.
It still keeps the original PHP not initialized error though when the type is not nullable. I think that is actually kinda cool now. You get to keep all the things: Slim and lean classes, PHP error indicating the true nature of the problem and possibility to loop over typed properties if you agree to keep them nullable. All governed by native PHP 7 nullable operator.
Of course, this can be changed or extended to be more type-specific if that makes any sense.
Update: this answer may be obsolete, but the comments contain an interesting discussion.
#Robert's workaround is buggy; in this part:
foreach ($reflectedProperties as $property) {
!$property->isInitialized($this) ??
$property->getType()->allowsNull() ? $property->setValue($this, null) : null;
}
the ?? must be corrected to &&.
Moreover that's a misuse of the ternary conditional; just use a classic if:
foreach ($reflectedProperties as $property) {
if (!$property->isInitialized($this)
&& $property->getType()->allowsNull()
) {
$property->setValue($this, null);
}
}
or:
foreach ($reflectedProperties as $property) {
if (!$property->isInitialized($this) && $property->getType()->allowsNull()) {
$property->setValue($this, null);
}
}
Probably not what you want, but you could use reflection:
<?php
class DragonBallClass
{
public string $goku;
public string $bulma = 'Bulma';
public string $vegeta = 'Vegeta';
}
$ob = new DragonBallClass;
$reflector = new ReflectionClass($ob);
foreach($reflector->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
echo $prop->name, ':', ($ob->{$prop->name} ?? 'NOT INITIALISED'), "\n";
}
Output:
goku:NOT INITIALISED
bulma:Bulma
vegeta:Vegeta
What i have learned about objects in php is that the hash with a number (#n) points to the instantiation times for example :
if we have something like this object(Index)#5 (1) means that we have 5 instances of the Index object.
However in my case i'm working on a custom PHP MVC i have only instantiated the class once (i'm sure only once. a model class directly within the controller ) but i'm getting an object like so
object(Timino\App\Models\Index)#5 (1)
so why is this happening ?
do namespaces affect this ! ?
does this have an affection to the performance ?!
Namespaces should not affect this. For performance issues, the number of objects during script runtime will only have an impact, if the script is getting close or over the max memory limit. FYI, here are some considerations about performance.
A simple example to show/explain the "object counter":
class TestClass {
public $number = 2;
}
class ClassInner {
protected $number = 5;
protected $innerObject;
public function __construct() {
$this->innerObject = new \stdClass();
}
}
$testInstance = new TestClass();
$classInner = new ClassInner();
$classInner2 = new ClassInner();
$testInstance2 = new TestClass();
$classInner3 = $classInner2;
echo '<pre>';
var_dump($testInstance);
var_dump($classInner);
var_dump($classInner2);
var_dump($testInstance2);
var_dump($classInner3);
echo '</pre>';
Should result in this output. Pleas have a look at the order of instances and count:
object(TestClass)#1 (1) {
["number"]=>
int(2)
}
object(ClassInner)#2 (2) {
["number":protected]=>
int(5)
["innerObject":protected]=>
object(stdClass)#3 (0) {
}
}
object(ClassInner)#4 (2) {
["number":protected]=>
int(5)
["innerObject":protected]=>
object(stdClass)#5 (0) {
}
}
object(TestClass)#6 (1) {
["number"]=>
int(2)
}
object(ClassInner)#4 (2) {
["number":protected]=>
int(5)
["innerObject":protected]=>
object(stdClass)#5 (0) {
}
}
I'm trying to get a multi-dimensional array from an Entity.
Symfony Serializer can already convert to XML, JSON, YAML etc. but not to an array.
I need to convert because I want have a clean var_dump. I now have entity with few connections and is totally unreadable.
How can I achieve this?
You can actually convert doctrine entities into an array using the built in serializer. I actually just wrote a blog post about this today:
https://skylar.tech/detect-doctrine-entity-changes-without/
You basically call the normalize function and it will give you what you want:
$entityAsArray = $this->serializer->normalize($entity, null);
I recommend checking my post for more information about some of the quirks but this should do exactly what you want without any additional dependencies or dealing with private/protected fields.
Apparently, it is possible to cast objects to arrays like following:
<?php
class Foo
{
public $bar = 'barValue';
}
$foo = new Foo();
$arrayFoo = (array) $foo;
var_dump($arrayFoo);
This will produce something like:
array(1) {
["bar"]=> string(8) "barValue"
}
If you have got private and protected attributes see this link : https://ocramius.github.io/blog/fast-php-object-to-array-conversion/
Get entity in array format from repository query
In your EntityRepository you can select your entity and specify you want an array with getArrayResult() method.
For more informations see Doctrine query result formats documentation.
public function findByIdThenReturnArray($id){
$query = $this->getEntityManager()
->createQuery("SELECT e FROM YourOwnBundle:Entity e WHERE e.id = :id")
->setParameter('id', $id);
return $query->getArrayResult();
}
If all that doesn't fit you should go see the PHP documentation about ArrayAccess interface.
It retrieves the attributes this way : echo $entity['Attribute'];
PHP 8 allows us to cast object to array:
$var = (array)$someObj;
It is important for object to have only public properties otherwise you will get weird array keys:
<?php
class bag {
function __construct(
public ?bag $par0 = null,
public string $par1 = '',
protected string $par2 = '',
private string $par3 = '')
{
}
}
// Create myBag object
$myBag = new bag(new bag(), "Mobile", "Charger", "Cable");
echo "Before conversion : \n";
var_dump($myBag);
// Converting object to an array
$myBagArray = (array)$myBag;
echo "After conversion : \n";
var_dump($myBagArray);
?>
The output is following:
Before conversion :
object(bag)#1 (4) {
["par0"]=> object(bag)#2 (4) {
["par0"]=> NULL
["par1"]=> string(0) ""
["par2":protected]=> string(0) ""
["par3":"bag":private]=> string(0) ""
}
["par1"]=> string(6) "Mobile"
["par2":protected]=> string(7) "Charger"
["par3":"bag":private]=> string(5) "Cable"
}
After conversion :
array(4) {
["par0"]=> object(bag)#2 (4) {
["par0"]=> NULL
["par1"]=> string(0) ""
["par2":protected]=> string(0) ""
["par3":"bag":private]=> string(0) ""
}
["par1"]=> string(6) "Mobile"
["�*�par2"]=> string(7) "Charger"
["�bag�par3"]=> string(5) "Cable"
}
This method has a benefit comparing to Serialiser normalizing -- this way you can convert Object to array of objects, not array of arrays.
I had the same issue and tried the 2 other answers. Both did not work very smoothly.
The $object = (array) $object; added alot of extra text in my key
names.
The serializer didn't use my active property because it did not have is in front of it and is a boolean. It also changed the sequence of my data and the data itself.
So I created a new function in my entity:
/**
* Converts and returns current user object to an array.
*
* #param $ignores | requires to be an array with string values matching the user object its private property names.
*/
public function convertToArray(array $ignores = [])
{
$user = [
'id' => $this->id,
'username' => $this->username,
'roles' => $this->roles,
'password' => $this->password,
'email' => $this->email,
'amount_of_contracts' => $this->amount_of_contracts,
'contract_start_date' => $this->contract_start_date,
'contract_end_date' => $this->contract_end_date,
'contract_hours' => $this->contract_hours,
'holiday_hours' => $this->holiday_hours,
'created_at' => $this->created_at,
'created_by' => $this->created_by,
'active' => $this->active,
];
// Remove key/value if its in the ignores list.
for ($i = 0; $i < count($ignores); $i++) {
if (array_key_exists($ignores[$i], $user)) {
unset($user[$ignores[$i]]);
}
}
return $user;
}
I basicly added all my properties to the new $user array and made an extra $ignores variable that makes sure properties can be ignored (in case you don't want all of them).
You can use this in your controller as following:
$user = new User();
// Set user data...
// ID and password are being ignored.
$user = $user->convertToArray(["id", "password"]);
To clarify:
I'm building a Logger class that allows me to easily log messages:
lib.Logger.php:
<?php
class Logger {
private $handle;
public function __construct($log_name, $log_path) {
if ( ! is_dir($log_path))
throw new Exception('Log path does not exist.');
if ( ! in_array(strtolower(substr($log_name, 0, -4)), array('.log', '.txt')))
$log_name = "{$log_name}.log";
$this->handle = fopen("{$log_path}/{$log_name}", 'a');
$this->log('------------- Initializing ------------- '.get_parent_class($this));
}
// --------------------------------------------------------------------
public function __destruct() {
fclose($this->handle);
}
// --------------------------------------------------------------------
public function log($message) {
$time = date(DATE_RFC822);
$log = "[{$time}] {$message}\n";
fwrite($this->handle, $log);
}
}
?>
And I call this using:
MyController.php:
<?php
class MyController extends Controller {
$logger = new Logger('testlog','/path/to/logs/');
$logger->log('Logs are fun!');
}
?>
When I initialize the object:
$this->log('------------- Initializing ------------- '.get_parent_class($this));
I want to log the name of the object (or file) that is calling log() -- in this case, either MyController or /path/to/MyController.php.
I tried using get_parent_class(), but of course this doesn't work because Logger does not have a parent class per se.
Any ideas? thank you so much for the help!
Alex B
I suppose a solution could be to use debug_backtrace.
The given example gets a backtrace like this :
array(2) {
[0]=>
array(4) {
["file"] => string(10) "/tmp/a.php"
["line"] => int(10)
["function"] => string(6) "a_test"
["args"]=>
array(1) {
[0] => &string(6) "friend"
}
}
[1]=>
array(4) {
["file"] => string(10) "/tmp/b.php"
["line"] => int(2)
["args"] =>
array(1) {
[0] => string(10) "/tmp/a.php"
}
["function"] => string(12) "include_once"
}
}
So, should include what you want ;-)
Still, the solution that seems the best, in my opinion, would be to pass the information needed when calling the log method...
(Just feels more... natural ^^ But that's not a very rational opinion, I suppose ^^ )
You willl need to make use of the function debug_backtrace(). But note that the function is not very consistent in its return value (not every array key is always present, even if it should be - the index file can be missing, for example, indicating that the file is either unknown (eval'd code) or the same as the previous stack frame). The last function that was called before this one should be available at index 0:
$trace = debug_backtrace();
$calling_function = $trace[0]['function'];
The only way to handle this implicitly is to capture the output from debug_backtrace as the others have suggested.
If you are interested in an explicit approach, maybe this could be it
in Logger
public function initialize( $context )
{
$logMessage = '------------- Initializing ------------- ';
if ( is_object( $context ) )
{
$logMessage .= get_class( $context );
} else {
// do something else?
}
$this->log( $logMessage );
}
and then
class MyController extends Controller
{
public function someFunc()
{
$logger = new Logger('testlog','/path/to/logs/');
$logger->initialize( $this );
$logger->log('Logs are fun!');
}
}
I ran into (almost) this exact problem last night. I'm using debug_backtrace() (as many others have already mentioned) to solve it. I needed to know the module that contains the controller that was calling a certain function. Because my controllers contain the module name within the class name, I simply grabbed the return from debug_backtrace() and explode()ed it to fetch the module name.
Soulmerge makes a good point about the return not always being consistent, but if you structure your code a certain way you can definitely get an accurate enough return.