Automatically use __LINE__ and __FILE__ relative to where function is called - php

I have a function log($text)
all this does is write $text to a database.
I would like to include __LINE__ and __FILE__, but don't want to include it as a parameter each time as I currently do:
function log($text,$file = null, $line = null){
// write $text to db using the three paramters
}
If I change $file = null to $file = __FILE__ it uses the file name that the function lives in, rather than where the function is being called.
Is there any way around this?
Although the question is identical, the answers seem to focus on some other topic: function name($param, $line = __LINE__, $file = __FILE__) {};
also, not using Zend framework: how to have php get the __LINE__ or __FILE__ value where a function is called

Related

PHP Get Basename of File Where Function Called

Anyone can help me get the basename of directory where the function called? I mean:
file /root/system/file_class.php
function find_file($dir, $file) {
$all_file = scandir($dir);
....
}
function does_exist($file) {
$pathinfo = pathinfo($file);
$find = find_file($pathinfo["dirname"], $pathinfo["basename"]);
return $find;
}
file /root/app/test.php
$is_exist = does_exist("config.php");
Under /root/app i have file "config.php, system.php". Do you know how to get the directory where does_exist() called? In function find_file() argument $dir is important, since scandir() function need directory path to scaned. I mean, when i want to check file config.php i doesn't need to write /root/app/config.php. If i not provide fullpath in $file argument, the $pathinfo["dirname"] will be ".". I've try to use dirname(__file__) in file_find() function but it's return the directory /root/system not /root/app where it is the directory of does_exist() function called.
I need create those function since i can't use file_exists() function.
Found Solutions:
I'm using debug_backtrace() to get the recent file and line number of where users calling function. For example:
function read_text($file = "") {
if (!$file) {
$last_debug = next(debug_backtrace());
echo "Unable to call 'read_text()' in ".$last_debug['file']." at line ".$last_debug['line'].".";
}
}
/home/index.php
16 $text = read_text();
The sample output: Unable to call 'read_text()' in /home/index.php at line 16.
Thanks.
Use any of PHP magic constants
__DIR__
__FILE__
http://php.net/manual/en/language.constants.predefined.php
Or use realpath("./");
To define your own constant paths:
define("MYPATH", realpath("./") . "/dir/dir/";
You can then call this MYPATH from everywhere this code (file) is included.

Wordpress constants in plugin aren't available in included files

My problem is that my constants aren't being globally available to files included deeper within my plugin. Let me explain.
In my main wordpress plugin file, the very first line right after the necessary /* Plugin Name: blah blah etc... */ I include my constants like so
require_once( plugin_dir_path(__FILE__) . 'constants.php' );
Next, within my __construct() function I include another file which builds the menu items in the admin section of wordpress like so
require_once( TRADESHOW_DIR . 'structure/build_menu.php' );
The build_menu.php file works as a router with mor require_once calls to pages corresponding to each menu page, and submenu. Here's where I don't get it. within the build_menu.php I still have access to my constants, which I use to do wordpress database queries using them, but I don't have access to the constants within the files that are included right after. Here's the code:
//// build_menu.php
function tradeshow_all_forms() {// callback from a wordpress add_submenu_page() function
global $wpdb;
$TS = new Bio_Tradeshow_Request_Plugin();// store plugin class in variable
if( isset( $_GET['form_id'] ) && is_integer( intval( $_GET['form_id'] ) ) ) {
$form = $wpdb->get_results(
"
SELECT *
FROM " . TRADESHOW_FORMS . "
WHERE id = '" . $_GET['form_id'] . "'
"
);
$form = $form[0];
echo TRADESHOW_SUBMITTED . ' build_menu.php<br />';// echo's out the correct value
require_once( TRADESHOW_DIR . 'structure/fill_form.php' );
}
}
Here's the top part of the fill_form.php file
$user_id = $TS->user();
$user_id = $user_id['id'];
$form_aswers = $wpdb->get_row(// returns nothing because TRADESHOW_SUBMITTED doesn't work
"
SELECT answers
FROM " . TRADESHOW_SUBMITTED . "
WHERE user_id = '$user_id' AND form_id = '$form->id'
",
ARRAY_A
);
echo TRADSHOW_SUBMITTED;// echoes out TRADESHOW_SUBMITTED as a string and not a variable
So as you can see it's an include within an include within the main plugin.
Additionally if anyone could explain to me why the $this variable of the plugin isn't available within files included within the plugin. I have this at the top of my plugin
static $_o = null;
static public function init() {
if (self::$_o === null)
self::$_o = new self;
return self::$_o;
}
so that I can do $variable = new plugin_class; but I would like to understand if something gets lost when you include a file using require or require_once.
FROM " . self::TRADESHOW_SUBMITTED . "
When I'm writing a plugin, its always 100% inside class, so this always works for me. I define constants in __construct() and call them in child classes with self..

Getting controller variables in a loader view method?

I'm building a script that got a static class used to load few things including files and views.
class load
{
public static function view($file_path, $area)
{
debug::log('getting view <b>' . $area . $file_path . '</b>.');
ob_start();
self::file($file_path, 'areas/' . $area . '/views');
debug::log('flushing view <b>' . $area . $file_path . '</b>.');
eturn ob_get_clean();
}
public static function file($file, $folder)
{
if(is_file($file_path = ROOT . '/' . $folder . '/' . $file))
{
if(require_once $file_path)
{
debug::log('file <b>' . $file_path . '</b> included.');
return true;
}
}
else
debug::kill('requested file <b>' . $file_path . '</b> does not exist.');
}
}
In the controller Im calling the view method to get a view:
$html = load::view('public', 'path/to/view/file.php');
Obviously, Im not able to access the variables from the controller at the view file using this practice, so I did a small modification on the view class to capture the vars:
public static function view($file_path, $area, $vars = array())
And added the following lines of codes to get the keys into vars:
while(list($n_list_var,$v_list_var)=each($vars))
$$n_list_var = $v_list_var;
But again I can't access the vars since Im using a method to load a file.
I have a method to load the files because I wanna test and log each file include attempt and not repeat the code every time I need include a file. And I have the loader view inside the loader class so I have all the methods of this kind together. Should I give up on using a class to load files? Should I use the loader view method on a extendable class from my controller?
Instead of going ahead and modify my entire script I would like to hear some opinions ... what would be the best practice to go? Or is there a way to solve my problem? Maybe using __set and __get magic methods?
Thanks,
Why not just pass a $vars argument to load::file() and extract( $vars ) (possibly moving the vars you use inside file() into class variables to prevent them from being overwritten)?
I'm suggesting using extract() instead of:
while(list($n_list_var,$v_list_var)=each($vars))
$$n_list_var = $v_list_var;
By the way, it would be a good idea to name your class Load.

Is it possible to track a variable back to its extract() function?

I am working with a Drupal theme, and I see a lot of variables which look like were created with extract(). Is it possible to track back, and see where that array is?
I take you are referring to the variables passed to a template file, which effectively are extracted from an array.
The code that does that in Drupal 7 is in theme_render_template().
function theme_render_template($template_file, $variables) {
extract($variables, EXTR_SKIP); // Extract the variables to a local namespace
ob_start(); // Start output buffering
include DRUPAL_ROOT . '/' . $template_file; // Include the template file
return ob_get_clean(); // End buffering and return its contents
}
The function is called from theme(), which executes the following code.
// Render the output using the template file.
$template_file = $info['template'] . $extension;
if (isset($info['path'])) {
$template_file = $info['path'] . '/' . $template_file;
}
$output = $render_function($template_file, $variables);
$render_function by default is set to 'theme_render_template', but its value is set with the following code (in theme()).
// The theme engine may use a different extension and a different renderer.
global $theme_engine;
if (isset($theme_engine)) {
if ($info['type'] != 'module') {
if (function_exists($theme_engine . '_render_template')) {
$render_function = $theme_engine . '_render_template';
}
$extension_function = $theme_engine . '_extension';
if (function_exists($extension_function)) {
$extension = $extension_function();
}
}
}
Just echo the $GLOBALS variable and you might find where it came from if the array was not unset.
Im not familiar with Drupal so this is just a suggestion, but if drupal has a templating structure or if an array is passed from a controller or such then possible that extract is used,
You could use get_defined_vars within your view to get all vars and its possible that there is an array there that you can cross reference with variables you know of that are in the same array or such.
<?php
$vars = get_defined_vars();
print_r($vars);
//or maybe
print_r($this);
?>

File private variables in PHP

Is it possible to define private variables in a PHP script so these variables are only visible in this single PHP script and nowhere else? I want to have an include file which does something without polluting the global namespace. It must work with PHP 5.2 so PHP namespaces are not an option. And no OOP is used here so I'm not searching for private class members. I'm searching for "somewhat-global" variables which are global in the current script but nowhere else.
In C I could do it with the static keyword but is there something similar in PHP?
Here is a short example of a "common.php" script:
$dir = dirname(__FILE__);
set_include_path($dir . PATH_SEPARATOR . get_include_path());
// Do more stuff with the $dir variable
When I include this file in some script then the $dir variable is visible in all other scripts as well and I don't want that. So how can I prevent this?
There are a few things you could do to keep $dir out of subsequent files
Example 1
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path());
This is the most obvious.
Example 2
$dir = dirname(__FILE__);
set_include_path($dir . PATH_SEPARATOR . get_include_path());
// work with $dir
unset($dir);
Just unset the variable after defining it and using it. Note this will unset any variable named $dir used prior to including this script.
Example 3
define('DIR_THIS', dirname(__FILE__));
set_include_path(DIR_THIS . PATH_SEPARATOR . get_include_path());
It is less likely I suppose to redefine a global constant like this.
Example 4
function my_set_include_path {
$dir = dirname(__FILE__);
set_include_path($dir . PATH_SEPARATOR . get_include_path());
// Do more stuff with the $dir variable
$my_other_var = 'is trapped within this function';
}
my_set_include_path();
You can define as many variables within that function and not affect the global namespace.
Conclusion
The first method is the easiest way to solve this problem, however because you want to use $dir again, it may not be ideal. The last example will at least keep that $dir (and any others defined in that function) out of the global namespace.
The only way you're going to accomplish anything close to what you want is to wrap everything in that included file in a function, and call it. If the file needs to execute itself you could still do
<?php
run_myfile()
function run_myfile() {
...
}
?>
There is no generic way to make a variable scoped to only a file outside of namespaces, classes, or functions.
Well, I'm probably getting flailed for this, but you if you are totally desperate you could use a Registry for that. I've whipped up a small one that does without classes (since I assume from And no OOP is used here so I'm not searching for private class members. means you don't want to do it with OOP at all)
function &registry_get_instance()
{
static $data = array();
return $data;
}
The static $data variable inside is persisted inside the function scope, so you can call the function wherever you like and always get the same contents. The crucial point is returning by reference, e.g.
$registry = &registry_get_instance(); // get $data array by reference
$registry['foo'] = 'bar'; // set something to $data
unset($registry); // delete global reference to $data
print_r(&registry_get_instance()); // show $data
Obviously you'd still have $registry as a variable in the global scope when calling this method from the global scope. So, you could add some more functions to make the Registry more convenient to use, e.g. for setting data to the Registry:
function registry_set($key, $value)
{
$registry = &registry_get_instance();
$registry[$key] = $value;
}
and for getting it out again:
function registry_get($key)
{
$registry = &registry_get_instance();
if(array_key_exists($key, $registry)) {
return $registry[$key];
} else {
trigger_error(sprintf(
'Undefined Index: %s', htmlentities($key)
), E_USER_NOTICE);
}
}
and for checking if a key exists:
function registry_isset($key)
{
$registry = &registry_get_instance();
return array_key_exists($key, $registry);
}
which you could then use like:
registry_set('foo', 'bar'); // setting something to the registry
var_dump( registry_isset('foo') ); // check foo is in the registry now
echo registry_get('foo'); // prints 'bar'
echo registry_get('punt'); // raises Notice
You could populate the Registry from an include file with an additional method like this:
function registry_load_file($file)
{
if(!is_readable(realpath($file))) {
return trigger_error(sprintf(
'File is not readable: %s', htmlentities($file)
), E_USER_WARNING);
}
$config = include $file;
if(!is_array($config)) {
return trigger_error(sprintf(
'Expected file %s to return an array', htmlentities($file))
, E_USER_WARNING);
}
$registry = &registry_get_instance();
$registry += $config;
}
with the include file having to return an array:
// config.php
return array(
'setting1' => 'something'
);
and then you can do
registry_load_from_file('config.php'); // add the contents of config to registry
print_r(registry_get_instance()); // show content of registry
Of course, this is now six functions in the global scope just for not having a global variable. Don't know if it's worth it, especially since I consider static in functions and all that reference stuff doubtful practice.
Take it as a proof of concept :)
Why not just put everything in a static class? Then you only have a single "variable" that could possibly conflict with the global namespace.
class MyClass {
public static $myvar = 1;
public static $myvar2 = "xyz";
public static function myfunction() {
self::$myvar++;
self::$myvar2 = "abc";
}
}
// References to class items, if needed
MyClass::myfunction();
MyClass::$myvar += 3;
If the problem you are trying to is just:
$dir = dirname(__FILE__);
set_include_path($dir . PATH_SEPARATOR . get_include_path());
// Do more stuff with the $dir variable
Then the solution would be to change the include path relative to '.' in your ini settings. E.g. change:
include_path=includes:/usr/local/php
to
include_path=./includes:/usr/local/php
Note that a script does not come into scope except where you explicitly include/require it (both the _once check applies globally) however I would recommend strongly against calling include/require from within a function - its much more transparent having the includes/requires at the top of the script.
I think that the problem you are trying to solve is based on a false premise and you should look for another way of fixing it. If you want the code in an include file to behave differently depending on what includes it, then really you should seperate it out into 2 seperate files - or maybe even 3 - 2 for the different behaviours and 1 for the common.
C.

Categories