php log4php dynamically set file:line column - php

using the apache log4php and its being called by a helper method in my class:
class MyClass{
function log($msg, $level='info'){
$log = #Logger::getLogger("MyLogger");
$log->$level($msg);
}
}
The issue with the above code is the file:line column in logger will always report the line num for the help method. I can get the original line num and file that calls the helper method using php's debug_backtrace():
$bt = debug_backtrace();
$caller = array_shift($bt);
So my question is, is there a way within my helper method to set the file:line column? I'm thinking that I might need to overwrite a Logger::method or something?

My solution is a hack to the source code, which should be avoided. There is an answer by #Sven that should cover most scenarios but for me my log calls have to go through a helper method.
In the LoggerLoggingEvent.php class file add the method:
/**
* Set the event location info
* #param LoggerLocationInfo $locationInfo
*/
public function setLocationInformation(LoggerLocationInfo $locationInfo) {
$this->locationInfo = $locationInfo;
}
Then in your log class method use:
/**
* Log an INFO message
* #param string $msg The message to log
* #return none
*/
public function log($msg, $level='info'){
// Manually construct a logging event
$level = LoggerLevel::toLevel($level);
$logger = Logger::getLogger(__CLASS__);
$event = new LoggerLoggingEvent(__CLASS__, $logger, $level, $msg);
// Override the location info
$bt = debug_backtrace();
$caller = array_shift($bt);
$location = new LoggerLocationInfo($caller);
$event->setLocationInformation($location);
// Log it
$logger->logEvent($event);
}

The solution is not to have a log function on your own, but to use the logger right there where you now make the call to your own log function.
class MyClass {
public function __construct() {
$this->logger = Logger::getLogger("MyLogger");
}
public function anywhere() {
// $this->log("Foo"); // Don't do this,
$this->logger->info("Foo"); // do this.
}
}
A generic logging framework cannot know how many layers of indirection your log call really took, and strip these from the backtrace. You also loose the ability to pass exceptions to the logger.

Related

Codeigniter 4 - get instance in helper function

how can i get in ci4 instance into helper function? $CI =&get_instance(); <- that was in version 3, how does this go in version Codeigniter 4?
I made a gist for it. here is a way to do it.
Create a helper file, you can name it whatever you want.
Add the following codes to the helper you just created.
Content of the helper file: i.e: utility_helper.php
<?php
$CI_INSTANCE = []; # It keeps a ref to global CI instance
function register_ci_instance(\App\Controllers\BaseController &$_ci)
{
global $CI_INSTANCE;
$CI_INSTANCE[0] = &$_ci;
}
function &get_instance(): \App\Controllers\BaseController
{
global $CI_INSTANCE;
return $CI_INSTANCE[0];
}
Call register_ci_instance($this) at the very beginning of your base controller
Now you may use get_instance() where ever you want just like CI3: $ci = &get_instance()
There are three notes I want to mention
I tested on PHP8 only.
I'm not sure if & is needed when calling get_instance. however, you have the option to use any form you want. so calling $ci = get_instance() is ok too, but you may do $ci = &get_instance() as you wish.
Make sure you change \App\Controllers\BaseController to something appropiate(i.e: your base controller). type hinting is great since IDEs understand them.
Create a common helper to keep the controller instance. Suppose here it is common_helper.php
$CI4 = new \App\Controllers\BaseController;
function register_CI4(&$_ci)
{
global $CI4;
$CI4 = $_ci;
}
In your BaseController
public $user_id;
protected $helpers = ['form', 'url', 'common']; // Loading Helper
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
{
parent::initController($request, $response, $logger);
register_CI4($this); // Registering controller instance for helpers;
$this->user_id = 4;
}
Now you will get the controller instance in all other helpers. Suppose a new heper user_helper as
function show_user_id()
{
global $CI4;
echo $CI4->user_id; // Showing 4
}
I tested it in PHP Version: 8.0.6
For me, this is the best solution for me:
if (!function_exists('getSegment'))
{
/**
* Returns segment value for given segment number or false.
*
* #param int $number The segment number for which we want to return the value of
*
* #return string|false
*/
function getSegment(int $number)
{
$request = \Config\Services::request();
if ($request->uri->getTotalSegments() >= $number && $request->uri->getSegment($number))
{
return $request->uri->getSegment($number);
}
else
{
return false;
}
}
}
source: https://forum.codeigniter.com/thread-74755.html
I get it (using CodeIgniter4):
in controller:
helper('login');
$isLog=isLogged($this,"user","passwd");
if($isLog)
....
else
....
in helper:
use App\Models\Loginmodel as Loginmodel;
function isLogged($ci,$e,$c){
$ci->login = new Loginmodel;
$ci->login->Like('paassword',$c);
$id=$ci->login->select('id')->where('username',$e)->findAll();
return $id;
}
I hope this helps.

Call function before each function

Is it possible to create a function that going be automatically called when each function are called?
I want that the result do this:
before_functions()
function_1()
before_functions()
function_2()
before_functions()
function_3()
But i want a file wit the function:
function before_functions(){} -> call before each function
and another file where I call functions:
function_1()
function_2()
function_3()
But I won't call the before_functions in each function..
There are different approaches to solve your problem. Some of them are mentioned in the comments already. Let us take the simplest approaches for solving your issue.
The magic method __call()
As lovelace said in the comments, there is already a simple solution for your problem stated in another stack overflow article. It uses PHPs own magic method __call(). Let 's have a look at a simple example.
class Foo
{
protected function before() : void
{
echo "before";
}
public function after() : void
{
echo "after";
}
public function __call($method, $arguments)
{
if (method_exists($this, $method)) {
$this->before();
return call_user_func_array(array($this, $method), $arguments);
}
}
}
// Testing
$class = new Foo();
$class->after(); // echoes "before->after"
As you can see the magic method __call provides proper handling for your purpose. First it checks, if the called method exists and after that it executes the before method before the called method is executed. The before method is called automatically, when you call a class method, that exists.
The callback approach
As also mentioned in the comments a callback function could be a possible solution without handling class instances. Let 's have a look at the callback example.
$callback = function()
{
echo "before->";
}
function foo(callable $callback, $bla)
{
$callback();
echo $bla;
}
// example code
foo($callback, 'go and make some coffee');
// output: "before->go and make some coffee"
This approach is even simpler as using the __call method, because you need just a callable function as parameter for your functions. Easy, hm?
The observer pattern
The observer pattern came with the standard php library in php5 and is more complex. I guess way too complex for your use case. To keep it complete, here 's a short example, how the observer pattern could be a usable solution to your issue.
class Group implements SplSubject
{
/**
* persons in this group
* #var \SplObjectStorage
*/
protected $persons;
/**
* observer active in this group
* #var \SplObjectStorage
*/
protected $observers;
/**
* the person, which actually speaks
* #var Person
*/
protected $speaker;
/**
* Initializes our class members and sets an observer for this group
*/
public function __construct()
{
$this->persons = new \SplObjectStorage();
$this->observers = new \SplObjectStorage();
$onSpeakObserver = new OnSpeakObserver($who, $what);
$this->attach($onSpeakObserver);
}
public function add(Person $person) {
$this->persons->attach($person);
}
public function speak(Person $who, $what) {
echo $who . " says: " . $what . "<br>";
$this->speaker = $who;
$this->notify();
}
public function getSpeaker() {
return $this->speaker;
}
public function getGroup() {
return $this->persons;
}
public function attach(\SplObserver $observer) {
$this->observers->attach($observer);
}
public function detach(\SplObserver $observer) {
$this->observers->attach($observer);
}
public function notify() {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
}
This is our basic class called group, which should be observed. A class, which should be observed, is always called the "subject". A subject takes one ore more observers, which are called by the notify method of the subject. A group consists of several people and a speaker. There is always one speaker and the other persons are listeners, which can react, when the speaker says something. For the reaction of the listeners we need an observer. This observer listens, if the speaker says something. The observer is added directly in the constructor of the group.
This class implements the \SplSubject interface, which brings us the methods attach, detach and notify for handling the observer, we attach to the group. Next we need the classes for a person and the observer itself.
class Person
{
protected $name = '';
public function __construct(string $name) : void
{
$this->name = $name;
}
public function __toString() : string
{
return $this->name;
}
}
A simple person with a name.
class OnSpeakObserver implements \SplObserver
{
public function update(\SplSubject $subject)
{
foreach ($subject->getGroup() as $person) {
if ($person !== $subject->getSpeaker()) {
echo $person . " says: me!<br>";
}
}
}
}
This is our observer, which implements the native \SplObserver interface, which forces us to use the update method. This method is called every time, when a person in the group speaks.
With this the classes, we have a simple observer pattern. In this simple example the observer forces a reaction every time a person in a group says something.
// open a group (the subject, which is observed)
$friends = new Group();
// add some persons to our group
$sarah = new Person('Sarah');
$friends->add($sarah);
$nicole = new Person('Nicole');
$friends->add($nicole);
$marcel = new Person('Marcel');
$friends->add($marcel);
$steffen = new Person('Steffen');
$friends->add($steffen);
// Marcel says ...
$friends->speak($marcel, 'Who likes the observer pattern?');
// result:
// Marcel says: Who likes the observer pattern?
// Sarah says: me!
// Nicole says: me!
// Steffen says: me!
You could transfer this little example to solve your problem. An observer could listen on the execution of your functions and every time one of your functions is called, the observer could execute another function before. As shown in this example, the observer does nothing more than executing, after a person in a group has said something. Same goes for your issue. It all depends on when the notify method of the subject is called.
If you have any questions feel free to ask.

Php Calling the functions of same class one after another

I want to write something like (laravel uses):
View::make('FooBarView')->with('foo', $foo)
->with('bar', $bar);
My knowledge and imagination made me to use new self instances. But I don't think that this is the best idea around and I could not handle it.
Google couldn't help me because of my bad keywords I think. I don't want to make you write code for me for sure but what is the name of this design pattern or whatever?
In laravel's source, with function uses
return $this;
But how to use it after make?
By the way, in this example; with method helps you to set variables for view's render.
To call what the function returns, the function will have to return something that is possible to call.
In this case, you could for example return "this":
class View {
/**
* #returns View
*/
public static function make($foo) {
/* do stuff, return new View instance */
return new View();
}
/**
* #returns View
*/
public function with($foo, $bar){
/* do stuff */
return $this;
}
}
That way, whenever you call with you will get the class instance back, which in turn will be callable:
View::make("foo")->with("foo")->with("bar");
// Will be same as:
$v = View::make("foo");
$v = $v->with("foo");
$v = $v->with("bar");

How to check if all declared variables are set?

I am rather new to the whole OOP paradigm in PHP, but I'm really loving it so far. I am currently writing a EventSender class, which should gather some information, and then fire the event to a EventHandler as well as writing the event to a eventlog.
When I came to the "firing" part, it struck me that I really would love a simple solution to validating that all my declared variables had been set. Is there an easy way to do so, or maybe even a in-built function in PHP?
Also, the code pasted below is the actual code for my class so far, so if you have any other remarks feel free to elaborate your thought :-)
class Event extends Base {
private $eventKey;
private $userID;
private $value;
private function __construct($eventKey){
$sql = Dbc::getInstance();
//Set and escape the EVENT_KEY.
$this->eventKey = $sql->real_escape_string($eventKey);
$sql->select_db('my_event_db');
$result = $sql->query('SELECT idx FROM event_types WHERE event_key = $this->$eventKey');
//Verify that the event key given is correct and usable.
//If failed throw exception and die.
if($result->num_rows != 1){
$err = 'ERROR: Illegal EVENT_KEY sent.';
throw new Exception($err);
}
}
public function setUserID($userID) {
$this->userID = $userID;
if(is_numeric($this->userID) != TRUE){
$err = 'ERROR: Value passed as userID is not numeric.';
throw new Exception($err);
}
}
public function setValue($value) {
$this->value = $value;
//The isJson function comes from a Trait that my Base class uses.
//The method will also throw a exception if it doesn't pass the test.
self::isJson($this->value);
}
public function fire () {
/* Here I want some code that swiftly checks if all declared vars have been set, which makes my method "ready to fire".. */
}
Best regards,
André V.
Based on Lajos Veres answer, I managed to build a class which can be added to a Trait (what is what I did in this case) and did exactly what I wrote in my initial question. I just wanted to share it, if anyone wanted to reuse it :-)
protected function allPropertiesSet($self){
/*
* This class is dependent on the ReflectionClass.
* This class can be called with self::allPropertiesSet(get_class());
* The class can be called in any class who uses this trait, even if it is inherited. It's function is to validate if all your defined variables are set.
* It will return true if all variables are set, otherwise it will return false.
* Some credit goes to Lajos Veres from Stackoverflow for pointing me in the right direction.
* Author: André Valentin
* Created: 30-10-2013
*/
$class = new $self;
$reflect = new ReflectionClass($class);
$props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE | ReflectionProperty::IS_STATIC);
$prop_array = array();
foreach($props AS $prop){
$var_name = $prop->getName();
$class_name = $prop->class;
if($class_name == $self){
$prop_array[] = $var_name;
}
}
foreach($prop_array AS $value){
$var_name = $value;
if(!isset($this->$var_name)){
return false;
}
}
return true;
}
Using Reflection you can list the properties of a class
http://www.php.net/manual/en/reflectionclass.getproperties.php
But I think this is overkill...
For sanity's sake, clearly define the rules governing the state of your instance before you call fire(), and move that to a separate function. So your fire() becomes
function fire() {
if ($this->validate_state()) {
/// do whatever
...
} else {
/// report issues
}
}
Your validator simply checks everything that needs to be in-place, i.e.
function validate_state() {
if ( isset($this->some_property) )
....
}
Alternatively, if your state checking just wants to ensure that default values are set, make sure this is done in your __construct constructor. That way you know which values are reasonably expected to defined.

Writing specs for a class that behaves differently depending upon constructor arguments

If you have a class that responds differently depending upon constructor arguments, how do you go about writing a spec for that class?
class Route
{
function __construct($url, array $methods = array())
{
// stores methods and url in private member variables
// creates a regex to match $url against incoming request URLs
}
public function isMatch($url)
{
// checks if the incoming request url matches against this url
}
}
Example use:
$a = new Route('/users/:id');
$a->isMatch('/users/1') // returns true;
$b = new Route('/users');
$b->isMatch('/users') // returns true
If I set up my spec for this class using the let function from phpspec:
class Route extends ObjectBehaviour
{
function let()
{
$this->beConstructedWith('/users/:id')
}
}
My spec can only check if the behaviour of this class works in one of the cases.
I've contemplated adding setter methods to allow me to test around this, but it seems like I'd be breaking encapsulation for the purpose of testing.
I'm struggling to find anything that touches upon this, so I'm started to think that maybe this is bad code smell situation.
beConstructedWith() doesn't always need to be called from the let() method. You can call it from the specs as well.
In my opinion there's nothing wrong in setting up an object in more than one way. However, you should avoid doing too much work in the constructor.
Constructor should be used only to obtain variables that will be set to a member properties here. No further logic should be done here...
Following the idea from point 1 there should be another logic that determines what happens next (e.g. if Object->hasProperty(X) then do x(), etc.)
Then a comment would be plain and straight forward.
Example:
class Route
{
private $url;
private $methods = array();
/**
* Constructor method, sets the attributes to private member variables
* #param string $url URL pattern
* #param array $methods Methods that should be used with given URL
*/
function __construct($url, $methods = array())
{
$this->url = $url;
$this->methods = $methods;
}
// ...
}

Categories