Codeigniter, Benchmark, pseudo-variables in braces '{pseudo-variables}' syntax meaning? - php

So basically the Benchmark class is very simple and it is quit clear how it works. It "remembers" the time when it was launched and when we want to know how long do the program is executing it just subtract marked time from current time.
Lets look into the code:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class CI_Benchmark {
public $marker = array();
public function mark($name)
{
$this->marker[$name] = microtime(TRUE);
}
public function elapsed_time($point1 = '', $point2 = '', $decimals = 4)
{
if ($point1 === '')
{
return '{elapsed_time}';
}
if ( ! isset($this->marker[$point1]))
{
return '';
}
if ( ! isset($this->marker[$point2]))
{
$this->marker[$point2] = microtime(TRUE);
}
return number_format($this->marker[$point2] - $this->marker[$point1], $decimals);
}
public function memory_usage()
{
return '{memory_usage}';
}
}
But there are still a few questions:
"if ($point1 === '')" it returns '{elapsed_time}'. And I kinda don't understand what it means both from syntax point of view and how do the PHP interpreter behave when it sees this line of code? What does it do in real world? Just returns the string?
function memory_usage() also not clear.
The documentation states:
* Simply returns the {memory_usage} marker.
*
* This permits it to be put it anywhere in a template
* without the memory being calculated until the end.
* The output class will swap the real value for this variable.
What "output class" will swap real value?
I am kinda confused, any help or explanation would be great.
:(

Those pseudo variables are replaced in output.php after the page is loaded in the next code:
$elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
if ($this->parse_exec_vars === TRUE)
{
$memory = round(memory_get_usage() / 1024 / 1024, 2).'MB';
$output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
}
It is built like this due to the reason that we don't know how much actual memory we consumed before the page is loaded.
I will come up with more explanations in the future for "new in Codeignaiter" guys like me .

Related

Convert a string to function (callable) and keep it cached

I'm trying to make a little benchmarking script where I can enter short pieces of code for quick evaluation of my anticipations. I imagine it similar to jsPerf (but password-protected for security reasons).
The main loop should look like this:
public function run(&$t, $count) {
//Run setup function
if(is_callable($this->setup))
call_user_func($this->setup);
//Save inital time
$t($this->name);
//THE MAIN LOOP
for($i=0; $i<$count; $i++) {
call_user_func($this->fn);
}
//Save end time
$t($this->name."_end");
//return time difference
return $t[$this->name."-".$this->name."_end"];
}
However, this will only work with static approach - with functions defined while making the script:
//New instance of tester
$b = new Benchmarker();
$b->add(
//Name
"touch",
//closure
function() {
touch("file.txt");
},
//Code seen in final reports
"touch()"
);
So as you see, I use call_user_func, not eval. Besides the fact that it's evil function in it's nature, I want to avoid it for performance reasons. If I'm testing a code that takes about 10ns to process and eviluation takes about 100ns, my results will be rather random.
This is why I'm looking for a way to convert string to a callable object. You can think about it like one-time eval.
$callable = string_to_callable("function() {echo \"Hello world!\";}");
$b->add(
//Name
"echo",
//callable object
$callable,
//Code seen in final reports
"echo \"...\""
);
Is that possible?
Note:
I can see funny workaround using include:
//Code received from the user
$code = "echo \"Hello world!\";";
//Random name for a new function
$rndname = "fn_".rand(0,100000); //There are smarter ways to do this of course
//String of the new function
$func = "function $rndname() {{$code}}";
//Define a filename
$f = $rndname.".php";
//Put the code in the file
file_put_contents($f, "<?php\n$func\n?".">");
//Include the new script
include $f;
//Call the function
call_user_func($rndname);
//Delete the file
unlink($f);
I really do hope that I won't need the code above!
Apart from creating a new file, there may be a closure trick:
function string_to_callable($string) {
return eval("return function() {{$string}};");
}

Best practice for tiny code reuse in PHP

For a long time I have a problem - should I reuse small parts of code and if so, how should I do it so it would be the best practice.
What I mean about small code is for example:
if (!is_array($table)) {
$table = array($table);
}
or
$x = explode("\n", $file_content);
$lines = array();
for ($i=0, $c = count($x); $i<$c; ++$i) {
$x[$i] = trim($x[$i]);
if ($x[$i] == '') {
continue;
}
$lines[] = $x[$i];
}
Such tiny parts of code may be used in many classes in one project but some of them are used also in many projects.
There are many possible solutions I think:
create simple function file and put them all reusable piece of codes as function, include them simple in project and use them whenever I want
create traits for those piece of codes and use them in classes
reuse code by simple copy paste or creating function in specific class (??)
other ?
I think all of those solutions have their pros and cons.
Question: What method should I use (if any) to reuse such code and why is this approach the best one in your opinion?
I think that "the best way" depends on many factors including the technology your applications use (procedural, OOP), versions of PHP they run on, etc. For example, traits are interesting and useful but they are available only since php 5.4.0 so using this tool to group your code snippets you will not be able to reuse them in systems running on earlier PHP versions. On the other hand if your app uses an OOP style and you organized your resuable small code snippets in functions, their usage may seem awkward in an OOP app and conflict with the function names in a particular class. In this case I think grouping your functions in classes would seem more natural.
Putting everything together, it seems that classes provide better tool for grouping resuable code snippets in terms outline above, namely backward compatibility with earlier PHP versions, avoiding function names conflicts, etc.) Personally I code mostly in OOP, so i have a Util class where I group small functions representing resuable pieces of code snippets that do not directly relate to each other and thus could not be logically groupped in other classes.
As mentioned already traits are a good thing. But might be a bit hard to manage after a while, and it might not be supported everywhere since its new.
What I do is to create Tool classes that have a lot small static functions, like:
class ArrayTools
{
static public function CheckArray($array)
{
if (!is_array($array))
{
$array = array($array);
}
return $array;
}
}
So you can call it with ArrayTools::CheckArray($array)
Please go with traits if your code mainly involves classes and objects.. As the the concept of traits exclusively focusses on code reuse ability.
Following are the code snippets which actually I use with Plain PHP projects, these code snippets are used from various frameworks good traits and best practices.
1.
The following code is used to check the environment in which your working, based on the environment you can set the some global variables, error reporting as so on.
if(!defined('ENVIRONMENT')){
define('ENVIRONMENT','DEVELOPMENT');
}
if (defined('ENVIRONMENT'))
{
switch (ENVIRONMENT)
{
case 'DEVELOPMENT':
case 'TESTING':
$base_url = 'http://localhost/project_name/';
error_reporting(E_ALL);
break;
case 'PRODUCTION':
$base_url = 'http://hostname/project_name/';
error_reporting(0);
break;
default:
exit('The application environment is not set correctly.');
}
}
2.
/* This function is used to PRINT the ARRAY data in the pre formatted manner */
if (!function_exists('pr')) {
function pr($data) {
echo '<pre>', print_r($data), '</pre>';
}
}
3.
/* This function is used to Sanitize the user data and make data safe to insert into the database */
function sanitize($data) {
global $link;
$data = trim($data);
return htmlentities(strip_tags(mysqli_real_escape_string($link, $data)));
}
4.
/* Used to get the difference of 2 arrays
Returns the array with difference
*/
function multi_diff($arr1,$arr2){
$result = array();
foreach ($arr1 as $k=>$v){
if(!isset($arr2[$k])){
$result[$k] = $v;
} else {
if(is_array($v) && is_array($arr2[$k])){
$diff = multi_diff($v, $arr2[$k]);
if(!empty($diff))
$result[$k] = $diff;
}
}
}
return $result;
}
5.
/* This fnction is used to generate the random keys of specific length
Accepts parameter of certain length if not specified it will generate 20 bit length automatically
*/
function generate_random_key($length = 20) {
//Initializing the varialble
$keystring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
$random_key = '';
for ($i = 0; $i < $length; $i++) {
$random_key.=$keystring[rand(0, strlen($keystring) - 1)];
}
//Return the randomly generated key
return $random_key;
}
6.
/* This function outputs the errors in ul>li format with unstyled
* To get the bullets styling remove class='list-unstyled' in <ul> tag */
function output_errors($errors){
$output = array();
foreach ($errors as $error) {
$output[] = '<li>'.$error.'</li>';
}
return '<ul class="list-unstyled">'.implode('', $output).'</ul>';
}
7.
/* Checks whether the user is loggedin else will redirect to the protectect page */
function protected_page(){
if(is_loggedin() === false){
// header('Location: protected.php');
header('Location: logout.php');
exit();
}
}
8.
/* If user tries to access the page directly accessing through the URL,
* If already loggedin then redirect him to any of the inner page
*/
function login_redirect(){
if(is_loggedin() === true){
header('Location: home.php');
}
}
9.
/* This function is used to check whether the user exists or not */
function email_exists($email){
/* Your Code */
}
/* This function is used to check whether the user isActive or not */
function is_active($email){
/* Your Code */
}
/* This function will get the userid from the email */
function userid_from_email($email) {
/* Your Code */
}
/* This fucntion is used to login the user based on the email-id and password */
function login($email,$password){
/* Your Code */
}
/* Check whether the USER is loggedin or not */
function is_loggedin(){
return (isset($_SESSION['userid'])) ? true : false;
}
Hope this helps you. Cheers!

URL parameters when running PHP via CLI

Is there any way I can pass parameters (like Query string in URL, or URL parameters) to a PHP file which is being run via CLI? I need this for some PHP cron jobs which need input from parameters. For example:
$ php /home/abc/www/myphp.php?param1=abc
There are two special variables in every command line interface argc and argv.
argv - array of arguments passed to the script.
argc - the number of command line parameters passed to the script (if run on the command line).
Make script cli.php
<?php
print_r($_SERVER['argv']);
and call it with argument:
$ php cli.php argument1=1
You should get the output looking like:
Array
(
[0] => cli.php
[1] => argument1=1
)
Source: http://www.php.net/manual/en/reserved.variables.server.php
If you are still so demanding to have a single point entry and to be able to process the url query as $_GET make your script act like a router by adding a switch:
if (PHP_SAPI === 'cli')
{
// ... BUILD $_GET array from argv[0]
}
But then - it violates SRP - Single Responsibility Principle! Having that in mind if you're still so demanding to make it work like you stated in the question you can do it like:
if (PHP_SAPI === 'cli')
{
/** ... BUILD from argv[0], by parsing the query string, a new array and
* name it $data or at will
*/
}
else
{
// ... BUILD new $data array from $_GET array
}
After that convert the code to use $data array instead of $_GET
...and have a nice day!
If the underlying code absolutely requires $_GET (and does not rely on other HTTP features) you can write a simple wrapper that abuses the fact that isn't a read-only superglobal:
<?php
// Untested (tweak to your own needs)
foreach($argv as $i){
list($k, $v) = explode('=', $i, 2);
$_GET[$k] = $v;
}
require_once('/home/abc/www/myphp.php');
... and then schedule your proxy instead:
php /path/to/wrapper.php param1=abc
Here's an extracted/rewritten class from my CLI Application library using $_SERVER['argv'] in the same form that linux runs commands (foo.sh --param "value" --marap):
<?php
class ArgumentScanner {
private static $instance;
private $arguments;
public static function get() {
if (empty(self::$instance)) {
self::$instance = new ArgumentScanner();
}
return self::$instance;
}
public function __construct() {
$this->arguments = $this->parseArguments($_SERVER['argv']);
}
public function __isset($argument) {
return isset($this->arguments[$argument]);
}
public function __get($argument) {
return (isset($this->arguments[$argument]) ? $this->arguments[$argument] : null);
}
/**
* Is used to parse the contents of $_SERVER['argv']
* #param array $argumentsRaw The arguments from $_SERVER['argv']
* #return stdClass An object of properties in key-value pairs
*/
private function parseArguments($argumentsRaw) {
$argumentBuffer = '';
foreach ($argumentsRaw as $argument) {
if ($argument[0] == '-') {
$argumentBuffer = substr($argument, ($argument[1] == '-' ? 2 : 1));
$equalSign = strpos($argumentBuffer, '=');
if ($equalSign !== false) {
$argumentKey = substr($argumentBuffer, 0, $equalSign);
$argumentsParsed[$argumentKey] = substr($argumentBuffer, $equalSign + 1);
$argumentBuffer = '';
} else {
$argumentKey = $argumentBuffer;
$argumentsParsed[$argumentKey] = '';
}
} else {
if ($argumentBuffer != '') {
$argumentKey = $argumentBuffer;
$argumentsParsed[$argumentKey] = $argument;
$argumentBuffer = '';
}
}
}
return (object)$argumentsParsed;
}
}
?>
Use:
<?php
$argumentScanner = ArgumentScanner::get();
if (isset($argumentScanner->reset)) {
echo '--reset was passed!';
}
if (isset($argumentScanner->test)) {
echo '--test was passed as ' . $argumentScanner->test . '!';
}
if (isset($argumentScanner->foo)) {
echo '--foo was passed as ' . $argumentScanner->foo . '!';
}
if (isset($argumentScanner->bar)) {
echo '--bar was passed as ' . $argumentScanner->bar . '!';
}
?>
php script.php --foo "bar" --reset -test="hey you!"
Output:
--reset was passed!
--test was passed as hey you!
--foo was passed as bar!
Best solution there would be to refactor and abstract away the business logic, retain the original script as a wrapper for it. Then add a CLI script to wrap the same business logic for the CLI.
That way, you can use the normal CLI parameter handling: http://www.php.net/manual/en/reserved.variables.argv.php
I needed to do the same thing, pass query parameters to a php script to run as a cron job. The script was from a third party and I didn't want to modify or write a new script. The solution was to place the query string parameters in single quotes and replace the question mark with an ampersand. In my case, I pass three parameters.
php /home/abc/www/myphp.php '&param1=abc&param2=def&param3=123'
I found the solution in this post, tried it and it works fine for me.
if (PHP_SAPI === 'cli')
{
parse_str(implode('&', array_slice($argv, 1)), $_GET);
}
This is solution that I use, if running via cli, put vars and values in $_GET,
seems that is working for simple tasks , not sure if work with php getopt etc
try wget and output to null
wget http://localhost/myphp.php?param1=abc -o /dev/null

I can't get PHPUnit work on my code, it kept saying undefined index: ecs

I am working on a website using someone else's source code called ecshop, a e-commerce website. I want to use PHPUnit to unit test my code but meet a problem.
This is what the error looks like:
C:\Users\maoqiuzi\Documents\Shanglian\XinTianDi\xintiandi\admin>phpunit
--stderr wang_test.php PHPUnit 3.7.27 by Sebastian Bergmann.
E
Time: 1.03 seconds, Memory: 6.75Mb
There was 1 error:
1) ShopTest::test_get_shop_name Undefined index: ecs
C:\Users\maoqiuzi\Documents\Shanglian\XinTianDi\xintiandi\includes\lib_common.ph
p:564
C:\Users\maoqiuzi\Documents\Shanglian\XinTianDi\xintiandi\admin\includes\init.ph
p:147
C:\Users\maoqiuzi\Documents\Shanglian\XinTianDi\xintiandi\admin\wang.php:10
C:\Users\maoqiuzi\Documents\Shanglian\XinTianDi\xintiandi\admin\wang_test.php:10
FAILURES! Tests: 1, Assertions: 0, Errors: 1.
The source code of wang_test.php:
<?php
require_once("wang.php");
class ShopTest extends PHPUnit_Framework_TestCase
{
public function test_get_shop_name()
{
$shop = new Wang();
$first_row_of_shop_list = $shop->get_shop_list();
}
}
The source code of wang.php:
<?php
class Wang
{
private $exchange;
function get_shop_list()
{
define("IN_ECS", 1);
require(dirname(__FILE__).'/includes/init.php');
$this->exchange = new exchange($GLOBALS['ecs']->table('shop'), $GLOBALS['db'], 'shop_id', 'shop_name');
$sql = "SELECT * FROM " . $GLOBALS['ecs']->table('shop');
$shop_list = $GLOBALS['db']->getAll($sql);
if($shop_list != array())
return $shop_list;
else
return array();
}
}
code in init.php
require(ROOT_PATH . 'includes/lib_common.php');
class ECS //line 82
{
var $db_name = '';
var $prefix = 'ecs_';
function ECS($db_name, $prefix)
{
$this->db_name = $db_name;
$this->prefix = $prefix;
}
...
}
...
$ecs = new ECS($db_name, $prefix); // line 114
... // other initialization codes here
$_CFG = load_config(); //line 147
code in lib_common.php
function load_config()
{
$arr = array();
$data = read_static_cache('shop_config');
if ($data === false)
{
$sql = 'SELECT code, value FROM ' . $GLOBALS['ecs']->table('shop_config') . ' WHERE parent_id > 0';
$res = $GLOBALS['db']->getAll($sql);
...
}
I've been working on this for days, and felt very frustrated. Hope anyone help me out! Thanks!!!
As you can see in PHPunit's manual part related on "How to test for PHP errors", how error_reporting is configured affects the test suite; which is your case.
You have (at least) three different options:
Fix the code to check and not use undefined indexes of arrays
Change the error_reporting to ignore notices (one of the examples in the link)
Create (and use) a phpunit.xml configuration file and set convertNoticesToExceptions to false
In init.php, if you change:
$ecs = new ECS($db_name, $prefix);
to:
$GLOBALS['ecs'] = new ECS($db_name, $prefix);
does it start to work (or at least move on to a different error message)?
What I'm thinking is that init.php is expecting it is running as global code, so isn't being explicit like that, but then PHPUnit is doing something clever such that it is not run as global code. So $ecs just ends up being treated as a local variable, not as a global.
(If it does change the error message, go through all other global code in your libraries and change them to use $GLOBALS[...] explicitly too. This is a GoodThingâ„¢ to do anyway, as it makes code clearer when other people look at it, and avoids easy-to-make mistakes when refactoring global code into functions.)

Is a destructor the right place to mark the end of a script's execution?

I'm doing some broad performance investigation in an application I maintain and I've set up a simple solution to track the execution time of requests, but I'm unable to find information to verify if this is going to be satisfyingly accurate.
This appears to have netted some good information and I've already eliminated some performance issues as a result, but I'm also seeing some confusing entries that make me question the accuracy of the recorded execution time.
Should I add an explicit method call at the end of each calling script to mark the end of its execution or is this (rather tidy) approach using the destructor good enough?
The calling code at the top of the requested script:
if( file_exists('../../../bench') )
{
require('../../../includes/classes/Bench.php');
$Bench = new Bench;
}
And here is the class definition (reduced for clarity):
require_once('Class.php');
class Bench extends Class
{
protected $page_log = 'bench-pages.log';
protected $page_time_end;
protected $page_time_start;
public function __construct()
{
$this->set_page_time_start();
}
public function __destruct()
{
$this->set_page_time_end();
$this->add_page_record();
}
public function add_page_record()
{
$line = $this->page_time_end - $this->page_time_start.','.
base64_encode(serialize($_SERVER)).','.
base64_encode(serialize($_GET)).','.
base64_encode(serialize($_POST)).','.
base64_encode(serialize($_SESSION))."\n";
$fh = fopen( APP_ROOT . '/' . $this->page_log, 'a' );
fwrite( $fh, $line );
}
public function set_page_time_end()
{
$this->page_time_end = microtime(true);
}
public function set_page_time_start()
{
$this->page_time_start = microtime(true);
}
}
What you need to do is use Xdebug. It is very simple once you set it up and don't have to change anything about your code to get full coverage of what took how long.

Categories