I have a function that takes an input variable and outputs a template with the following call:
outputhtml($blue_widget);
outputhtml($red_widget);
outputhtml($green_widget);
And a simplified version of the function:
function outputhtml($type)
{
static $current;
if (isset($current))
{
$current++;
}
else
{
$current = 0;
}
//some logic here to determine template to output
return $widget_template;
}
Now here is my problem. If I call the function in a script three times or more, I want the output to be one way, but if I only call the function twice, then I have some html changes that need to be reflected in the templates that are returned.
So how can I modify this function to determine if there are only two calls for it. I can't go back after the fact and ask "hey function did you only run twice???"
Having trouble getting my head around how I tell a function that it is not going to be used after the second time and the necessary html modifications can be used. How would I go about accomplishing this?
function outputhtml($type)
{
static $current = 0;
$current++;
//some logic here to determine template to output
if ($current === 2) {
// called twice
}
if ($current > 2) {
// called more than twice
}
return $widget_template;
}
That would not be practical using a static $current inside the function; I would suggest using an object to maintain the state instead, like so:
class Something
{
private $current = 0;
function outputhtml($type)
{
// ... whatever
++$this->current;
return $template;
}
function didRunTwice()
{
return $this->current == 2;
}
}
The didRunTwice() method is asking "did you run twice?".
$s = new Something;
$tpl = $s->outputhtml(1);
// some other code here
$tpl2 = $s->outputhtml(2);
// some other code here
if ($s->didRunTwice()) {
// do stuff with $tpl and $tpl2
}
The only way you can find out if a function was only called twice is by putting the test at the end of your code; but perhaps by then the templates are no longer accessible? Can't tell much without seeing more code.
Related
I've been racking my brains over this one but I'll do my best to describe the problem as best as possible. I have a custom function written within template.php, with a bunch of conditionals. When a condition is true, I would like to assign a value to a variable, and then pass that variable intro a node preprocess function that allows that variables to be rendered on a node template.
The function containing the condition:
function _mytheme_date_repeat_string($vars) {
$exdate_pos = strpos($rrule['WKST'], 'EXDATE:');
if($exdate_pos > 0) {
$vars['testvar'] = 'abc123';
}
}
The preprocess function that I would like to render the variable in for node template use:
function mytheme_preprocess_node(&$vars, $hook) {
$vars['new_variable'] = $testvar;
}
Intended usage in node.tpl.php:
<?php print $new_variable; ?>
I'm not great with PHP, but I know enough about programming to know that variable scope might be an issue here. What would be the best way to implement this? Any guidance is greatly appreciated.
Thanks,
Mark.
If it is not called, your _mytheme_date_repeat_string() function will never be executed. Preprocess functions (ie. any function starting mytheme_preprocess_, are called automatically by Drupal's theme system.
What you need is either move the code of _mytheme_date_repeat_string() in mytheme_preprocess_node() or refactor it and call it.
function _mytheme_date_repeat_string($rrule) {
$exdate_pos = strpos($rrule['WKST'], 'EXDATE:');
if($exdate_pos > 0) {
return 'abc123';
}
else {
return NULL;
}
}
/**
* Prepares variables for node templates.
*/
function mytheme_preprocess_node(&$variables, $hook) {
// Get $rrule from somewhere
$rrule = ... ;
$testvar = _mytheme_date_repeat_string($rrule);
if ($testvar) {
$variables['new_variable'] = $testvar;
}
}
You code does not show where the $rrule calue comes from. I assume you would get it for $variables['node'].
So I've built a small conditional to evaluate which button is pressed in my form (as there are 2). This works fine and fires off the correct method and writes the appropriate data to the DB, however my redirect is not working. It saves() to the DB and then simply stays on the page designated as the POST route.
I suspect the problem has something to do with my conditional and the use of $this.
Here is my check_submit method:
public function check_submit()
{
if(!is_null(Input::get('add_to_invoice'))){
$this->invoice_add_item();
} elseif(!is_null(Input::get('complete_invoice'))) {
$this->invoice_complete();
}
}
Here is one of the 2 methods which I am currently testing:
public function invoice_add_item()
{
$input = Request::all();
$invoice_items = new Expense;
$invoice_items->item_id = $input['item_id'];
$invoice_items->category_id = $input['category'];
$invoice_items->price = $input['price'];
$invoice_items->store_id = $input['store'];
if(Input::has('business_expense'))
{
$invoice_items->business_expense = 1;
}
else{
$invoice_items->business_expense = 0;
}
$invoice_items->save();
return redirect('/');
}
Perhaps there is a better way of handling this in my routes(web) file, but I'm not sure how to go about this.
You should add the return to the check_submit() method. Something like
public function check_submit()
{
if(!is_null(Input::get('add_to_invoice'))){
return $this->invoice_add_item();
} elseif(!is_null(Input::get('complete_invoice'))) {
return $this->invoice_complete();
}
}
Better yet, you should probably return a boolean on invoice_add_item() and based on that, redirect the user to the correct place (or with some session flash variable with an error message)
I am starting to write phpUnit test and faced with such problem. 80% of my functions ending on such lines
$data["res"] = $this->get_some_html($this->some_id);
echo my_json_encode($data);
return true;
How can i make test on such kind of functions in my classes?
You need to isolate your code into testable 'chunks'. You can test that the function returns TRUE/FALSE given specified text, and then test the JSON return data given fixed information.
function my_json_encode($data)
{
return ...;
}
function get_some_html($element)
{
return ...;
}
function element_exists($element)
{
return ..;
}
function display_data($element)
{
if(element_exists($element)
{
$data = get_some_html($element);
$json = my_json_encode($data);
return true;
}
else
{
return false;
}
}
Testing:
public function test_my_json_encode()
{
$this->assertEquals($expected_encoded_data, my_json_encode($text));
}
public function test_get_some_html()
{
$this->assertEquals($expected_html, get_some_html('ExistingElementId'));
}
public function test_element_exists()
{
$this->assertTrue(element_exists('ExistingElementId');
$this->assertFalse(element_exists('NonExistingElementId');
}
function test_display_data()
{
$this->assertTrue(display_data('ExistingElementId'));
$this->assertFalse(element_exists('NonExistingElementId');
}
This is a simple, abstract example of the changes and the testing. As the comments above have indicated, you might want to change the return to be the JSON text, and a FALSE on error, then use === testing in your code to decide to display the text or not.
The next step would be to mock out the Elements, so you can get expected data without the need for a real HTML page.
I am doing some PHP programming and have a question: How can I load a PHP function when the PHP script is first run, and only when it is first run?
thanks
You can use a Lock file
$lock = "run.lock" ;
if(!is_file($lock))
{
runOneTimeFuntion(); //
touch($lock);
}
Edit 1
One Time Function
runOneTimeFuntion ();
runOneTimeFuntion ();
runOneTimeFuntion ();
runOneTimeFuntion ();
runOneTimeFuntion ();
runOneTimeFuntion ();
runOneTimeFuntion ();
runOneTimeFuntion ();
function runOneTimeFuntion() {
if (counter () < 1) {
var_dump ( "test" );
}
}
function counter() {
static $count = 0;
return $count ++;
}
Output
string 'test' (length=4)
EACH time you start php script he starts as a new one, no matter how much times it was called befor.
And if you aware of re-declare functions, which is forbided in PHP, load functions from external fles useing:
<?php require_once(my_function_file.php); ?>
If you want scrpt remember if he was called befor, it's possible to do using some form of logging (data base\file) and cheacking it befor load... But I don't see any reason for this in case of function load...
Or use a plain boolean...
$bFirstRun = true;
if( $bFirstRun ) {
run_me();
$bFirstRun = false;
}
There is a PHP function called function_exists
You can define your own function within this function, and then you could see whether this exists or not.
if (!function_exists('myfunction')) {
function myfunction() {
// do something in the function
}
// call my function or do anything else that you like, from here on the function exists and thus this code will only run once.
}
Read more about function_exists here: http://www.php.net/function_exists
So I'm setting up a system that has a lot of emails, and variable replacement within it, so I'm writing a class to manage some variable replacement for templates stored in the database.
Here's a brief example:
// template is stored in db, so that's how this would get loaded in
$template = "Hello, %customer_name%, thank you for contacting %website_name%";
// The array of replacements is built manually and passed to the class
// with actual values being called from db
$replacements = array('%customer_name%'=>'Bob', '%website_name%'=>'Acme');
$rendered = str_replace(array_keys($replacements), $replacements, $template);
Now, that works well and good for single var replacements, basic stuff. However, there are some places where there should be a for loop, and I'm lost how to implement it.
The idea is there'd be a template like this:
"hello, %customer_name%, thank you for
requesting information on {products}"
Where, {products} would be an array passed to the template, which the is looped over for products requested, with a format like:
Our product %product_name% has a cost
of %product_price%. Learn more at
%product_url%.
So an example rendered version of this would be:
"hello, bob, thank you for requesting
information on:
Our product WidgetA has a cost of $1.
Learn more at example/A
Our product WidgetB has a cost of $2.
Learn more at example/B
Our product WidgetC has a cost of $3.
Learn more at example/C.
What's the best way to accomplish this?
Well, I really dont see the point in a template engine that uses repalcements/regex
PHP Is already a template engine, when you write <?php echo $var?> its just like doing <{$var}> or {$var}
Think of it this way, PHP Already translates <?php echo '<b>hello</b>'?> into <b>hello</b> by its engine, so why make it do everything 2 times over.
The way i would implement a template engine is like so
Firstly create a template class
class Template
{
var $vars = array();
function __set($key,$val)
{
$this->vars[$key] = $val;
}
function __get($key)
{
return isset($this->vars[$key]) ? $this->vars[$key] : false;
}
function output($tpl = false)
{
if($tpl === false)
{
die('No template file selected in Template::output(...)');
}
if(!file_exists(($dir = 'templates/' . $tpl . '.php')))
{
die(sprintf('Tpl file does not exists (%s)',$dir));
}
new TemplateLoader($dir,$this->vars);
return true;
}
}
This is what you use in your login such as index.php, you will set data just like an stdClass just google it if your unsure. and when you run the output command it sends the data and tpl to the next class below.
And then create a standalone class to compile the tpl file within.
class TemplateLoader
{
private $vars = array();
private $_vars = array(); //hold vars set within the tpl file
function __construct($file,$variables)
{
$this->vars = $variables;
//Start the capture;
ob_start();
include $file;
$contents = ob_get_contents();
ob_end_clean(); //Clean it
//Return here if you wish
echo $contents;
}
function __get($key)
{
return isset($this->vars[$key]) ? $this->vars[$key] : (isset($this->_vars[$key]) ? $this->_vars[$key] : false) : false;
}
function __set($key,$val)
{
$this->_vars[$key] = $val;
return true;
}
function bold($key)
{
return '<strong>' . $this->$key . '</string>';
}
}
The reason we keep this seperate is so it has its own space to run in, you just load your tpl file as an include in your constructor so it only can be loaded once, then when the file is included it has access to all the data and methods within TemplateLoader.
Index.php
<?php
require_once 'includes/Template.php';
require_once 'includes/TemplateLoader.php';
$Template = new Template();
$Template->foo = 'somestring';
$Template->bar = array('some' => 'array');
$Template->zed = new stdClass(); // Showing Objects
$Template->output('index'); // loads templates/index.php
?>
Now here we dont really want to mix html with this page because by seperating the php and the view / templates you making sure all your php has completed because when you send html or use html it stops certain aspects of your script from running.
templates/index.php
header
<h1><?php $this->foo;?></h1>
<ul>
<?php foreach($this->bar as $this->_foo):?>
<li><?php echo $this->_foo; ?></li>
<?php endforeach; ?>
</ul>
<p>Testing Objects</p>
<?php $this->sidebar = $this->foo->show_sidebar ? $this->foo->show_sidebar : false;?>
<?php if($this->sidebar):?>
Showing my sidebar.
<?php endif;?>
footer
Now here we can see that were mixing html with php but this is ok because in ehre you should only use basic stuff such as Foreach,For etc. and Variables.
NOTE: IN the TemplateLoader Class you can add a function like..
function bold($key)
{
return '<strong>' . $this->$key . '</string>';
}
This will allow you to increase your actions in your templates so bold,italic,atuoloop,css_secure,stripslashs..
You still have all the normal tools such as stripslashes/htmlentites etc.
Heres a small example of the bold.
$this->bold('foo'); //Returns <strong>somestring</string>
You can add lots of tools into the TempalteLoader class such as inc() to load other tpl files, you can develop a helper system so you can go $this->helpers->jquery->googleSource
If you have any more questions feel free to ask me.
----------
An example of storing in your database.
<?php
if(false != ($data = mysql_query('SELECT * FROM tpl_catch where item_name = \'index\' AND item_save_time > '.time() - 3600 .' LIMIT 1 ORDER BY item_save_time DESC')))
{
if(myslq_num_rows($data) > 0)
{
$row = mysql_fetch_assc($data);
die($row[0]['item_content']);
}else
{
//Compile it with the sample code in first section (index.php)
//Followed by inserting it into the database
then print out the content.
}
}
?>
If you wish to store your tpl files including PHP then that's not a problem, within Template where you passing in the tpl file name just search db instead of the filesystem
$products = array('...');
function parse_products($matches)
{
global $products;
$str = '';
foreach($products as $product) {
$str .= str_replace('%product_name%', $product, $matches[1]); // $matches[1] is whatever is between {products} and {/products}
}
return $str;
}
$str = preg_replace_callback('#\{products}(.*)\{/products}#s', 'parse_products', $str);
The idea is to find string between {products} and {products}, pass it to some function, do whatever you need to do with it, iterating over $products array.
Whatever the function returns replaces whole "{products}[anything here]{/products}".
The input string would look like that:
Requested products: {products}%product_name%{/products}