I wanted to create closure with dynamic code. To do that I wrote below code :-
function generateFunction ($class, $method){
$code = "require_once 'Init.php';";
$code = '$model = new '.$class.'();';
$code .= '$model->'.$method.'();';
return function() use(&$code){
return eval($code);
};
}
I know eval is evil but unable to find any alternative yet.
To get the function in variable I used below code :-
$myNewFunction = generateFunction ('svn', 'update');
Now I want to run this function by passing it as argument to my PHP file. But this function has same body on the output while I expected it to be like below.
function(){return eval(require_once 'Init.php';$model = new svn(); $model->update())}
Basically this need arise when I decided to integrate the Jobby.
Basically this need arise when I decided to integrate the Jobby.
I assume you want to create callback functions dynamically. Then your code is complete overkill and also doesn't need eval at all. You can get the same results by passing this as callback:
$callback = array(new SVN, 'update');
That's essentially all your code does anyway. Though with the difference that SVN is instantiated here, not when the callback is triggered. For an eval-free version of your code, this'll do:
function generateFunction($class, $method) {
return function () use ($class, $method) {
require_once 'Init.php';
$model = new $class;
return $model->$method();
};
}
To complete your answers, here is a complete example with jobby :
$jobby = new \Jobby\Jobby();
$WebSitesSchedule = new WebSitesSchedule();
$WebSitesSchedule->load_all();
foreach ($WebSitesSchedule as $WebSiteSchedule)
{
$Url = $WebSiteSchedule->ScheduleAction.'?'.$WebSiteSchedule->ScheduleParameters;
$jobby->add( "CmsSchedule_".strval($WebSiteSchedule->ScheduleID), array(
'command' => generateFunction($Url),
'schedule' => $WebSiteSchedule->ScheduleCron,
'output' => '/PATH/log/CmsSchedule.log',
'enabled' => true,
'debug' => true,
'recipients' => 'toto#gmail.com'
));
}
function generateFunction($Url)
{
return function () use ($Url)
{
$Result = Proxy::curl_request($Url);
if($Result["status"]==="success")
{
print_r($Result);
return TRUE;
}
else
{
print_r($Result);
return FALSE;
}
};
}
$jobby->run();
In your case you don't need to use eval(). You have to use call_user_func (or call_user_func_array) in conjunction with class_exists and method_exists instead.
require_once 'Init.php';
function generateFunction ($class, $method) {
if (!class_exists($class)) return false;
$model = new $class();
return method_exists($model, $method)? array($model, $method) : false;
}
$myNewFunction = generateFunction ('svn', 'update');
if ($myNewFunction) call_user_func($myNewFunction);
Related
It's a bit hard to explain, but it should be clear from this example what I'm trying to do:
public function onMessage($data)
{
if (!function_exists("sendClientToHost")) {
$sendClientToHost = function(&$sendThis) use($type) {
$sendThis += array('type' => $type);
//.... execute code ....//
};
}
if (!function_exists("sendObjects")) {
$sendObjects = function() use($data) {
$user_id = $data->user_id;
$sendThis = array(
"msg" => $user_id,);
$sendClientToHost($sendThis);
};
}
$sendObjects();
}
I try calling the internal function sendObjects within the OnMessage function, this part works. However, the sendObjects function has a critical error where it says sendClientToHost is undefined. How do I call sendClientToHost from within the sendObjects function?
Here you are calling a function, but you have written a dollar sign at the beginnig as if it was a variable: $sendClientToHost($sendThis);.
Just delete the "$" and I think it should be fine...
I needed to use the closure in the other function.
$sendObjects = function() use($data, $sendClientToHost) {...};
I am making a REST method with Zend Framework 2 which returns JSON response. For that I'm using the following code:
return new JsonModel($result);
In some cases, result is an empty array and JsonModel outputs that as []. Is it possible to force JsonModel to display empty array as an object, i.e. {}?
In PHP, you can do the following:
echo json_encode(array(), JSON_FORCE_OBJECT);
Is there a similar option to JSON_FORCE_OBJECT that I could use with JsonModel in zf2? I have tried all combinations of the following, but with no luck.
return new JsonModel(array(), JSON_FORCE_OBJECT);
If you'd've followed the code, you would've found that it's not possible with the provided JsonModel by Zend.
Zend\View\Model\JsonModel has this serialize function:
public function serialize()
{
$variables = $this->getVariables();
if ($variables instanceof Traversable) {
$variables = ArrayUtils::iteratorToArray($variables);
}
$options = [
'prettyPrint' => $this->getOption('prettyPrint'),
];
if (null !== $this->jsonpCallback) {
return $this->jsonpCallback.'('.Json::encode($variables, false, $options).');';
}
return Json::encode($variables, false, $options);
}
The Json::encode leads to Zend\Json\Json, and the encode() function
public static function encode($valueToEncode, $cycleCheck = false, array $options = [])
{
if (is_object($valueToEncode)) {
if (method_exists($valueToEncode, 'toJson')) {
return $valueToEncode->toJson();
}
if (method_exists($valueToEncode, 'toArray')) {
return static::encode($valueToEncode->toArray(), $cycleCheck, $options);
}
}
// Pre-process and replace javascript expressions with placeholders
$javascriptExpressions = new SplQueue();
if (isset($options['enableJsonExprFinder'])
&& $options['enableJsonExprFinder'] == true
) {
$valueToEncode = static::recursiveJsonExprFinder($valueToEncode, $javascriptExpressions);
}
// Encoding
$prettyPrint = (isset($options['prettyPrint']) && ($options['prettyPrint'] === true));
$encodedResult = self::encodeValue($valueToEncode, $cycleCheck, $options, $prettyPrint);
// Post-process to revert back any Zend\Json\Expr instances.
$encodedResult = self::injectJavascriptExpressions($encodedResult, $javascriptExpressions);
return $encodedResult;
}
As you can see, the encoding function has been commented for you, so we need the self::encodeValue function, clicking through on that leads to:
private static function encodeValue($valueToEncode, $cycleCheck, array $options, $prettyPrint)
{
if (function_exists('json_encode') && static::$useBuiltinEncoderDecoder !== true) {
return self::encodeViaPhpBuiltIn($valueToEncode, $prettyPrint);
}
return self::encodeViaEncoder($valueToEncode, $cycleCheck, $options, $prettyPrint);
}
Judging by your question, you have json_encode built-in function available, so we step into the if() and execute the self::encodeViaPhpBuiltIn() function:
private static function encodeViaPhpBuiltIn($valueToEncode, $prettyPrint = false)
{
if (! function_exists('json_encode') || static::$useBuiltinEncoderDecoder === true) {
return false;
}
$encodeOptions = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP;
if ($prettyPrint) {
$encodeOptions |= JSON_PRETTY_PRINT;
}
return json_encode($valueToEncode, $encodeOptions);
}
This has the same check again, but finally ends up with:
return json_encode($valueToEncode, $encodeOptions);
The options are set hardcoded in the function, with the exception of the optional "JSON_PRETTY_PRINT" option.
The answer you're looking for is: no, it's not possible.
However, you could, technically, write your own replacement for JsonModel, make sure that your own model is used by the JsonViewStrategy and use it then... Just an option.
Use the response object:
$result = array();
$data = json_encode($result, JSON_FORCE_OBJECT);
$response = $this->getResponse();
return $response->setContent($data);
I'm in the process of learning PHP, and the concept of callback functions have me slightly confused. I understand that there are synchronous and asynchronous callbacks and that the common callback in PHP is synchronous. I have read through a lot of information regarding this topic, but I'm none the wiser still.
How is this:
function string($string, $callback) {
$results = array(
'upper' => strtoupper($string),
'lower' => strtolower($string)
);
if(is_callable($callback)) {
call_user_func($callback, $results);
}
}
string('Danny', function($name) {
echo $name['upper'];
}
);
Different or better than this:
function string($string, $case) {
$options = [
'upper' => strtoupper($string),
'lower' => strtolower($string)
];
echo $options[$case];
}
string('Danny', 'upper');
Here's a typical example where the callback is better than the direct value:
function intricateProcessToCalculateDefault() {
//Takes 3 seconds
}
function valueOrDefault($value, $default = null) {
if ($value === null) {
if (is_callable($default)) {
return $default();
}
return $default;
}
return $value;
}
Now consider these two examples:
$valueToUse = valueOrDefault(
$_GET["parameterWithFallback"] ?? null,
intricateProcessToCalculateDefault()
);
// or
$valueToUse = valueOrDefault(
$_GET["parameterWithFallback"] ?? null,
function () { return intricateProcessToCalculateDefault(); }
);
Now here's the thing. If you don't pass parameterWithCallback this will take 3 seconds either way. However if you do then intricateProcessToCalculateDefault will never be called if passed as a callback and will therefore save you time.
In your case, since the function code is ran in either case, there's no benefit.
PHP is mostly synchronous so there's no easy way to get asynchronous functions. Some libraries do offer them but that's usually tied with calling a shell executable or something similar.
Another use case is for inversion for control. There are many generic algorithms which can be implemented with a "placeholder" for user code for. array_map is such an example.
$array = [ "string1", "sting2" ];
$action = "upper";
array_map(function ($value) use ($action) {
return ucfirst(strtolower($value));
}, $array);
In this case control is inverted. A PHP function is calling your function instead of the other way around.
In this example with a callback you could easily pass a different function without changing the original code:
<?php
function string($string, $callback) {
$results = array(
'upper' => strtoupper($string),
'lower' => strtolower($string)
);
if(is_callable($callback)) {
call_user_func($callback, $results);
}
}
string('Danny', function($name) {
echo $name['upper'];
}
);
string('Danny', function($name) {
echo 'Mr.'.$name['upper'];
}
);
string('Danny', function($name) {
echo 'Mrs.'.$name['upper'];
}
);
I have one PHP class as below (part of the code):
class myclass{
private static $arrX = array();
private function is_val_exists($needle, $haystack) {
if(in_array($needle, $haystack)) {
return true;
}
foreach($haystack as $element) {
if(is_array($element) && $this->is_val_exists($needle, $element))
return true;
}
return false;
}
//the $anInput is a string e.g. Michael,18
public function doProcess($anInput){
$det = explode(",", $anInput);
if( $this->is_val_exists( $det[0], $this->returnProcess() ) ){
//update age of Michael
}
else{
array_push(self::$arrX, array(
'name' => $det[0],
'age' => $det[1]
));
}
}
public function returnProcess(){
return self::$arrX;
}
}
The calling code in index.php
$msg = 'Michael,18';
myclass::getHandle()->doProcess($msg);
In my webpage says index.php, it calls function doProcess() over and over again. When the function is called, string is passed and stored in an array. In the next call, if let's say same name is passed again, I want to update his age. My problem is I don't know how to check if the array $arrX contains the name. From my own finding, the array seems to be re-initiated (back to zero element) when the code is called. My code never does the update and always go to the array_push part. Hope somebody can give some thoughts on this. Thank you.
There is a ) missing in your else condition of your doProcess() function, it should read:
else{
array_push(self::$arrX, array(
'name' => $det[0],
'age' => $det[1]
)); // <-- there was the missing )
}
Here is a complete running solution based on your code:
<?php
class myclass{
private static $arrX = array();
private function is_val_exists($needle, $haystack) {
if(in_array($needle, $haystack)) {
return true;
}
foreach($haystack as $element) {
if(is_array($element) && $this->is_val_exists($needle, $element))
return true;
}
return false;
}
//the $anInput is a string e.g. Michael,18
public function doProcess($anInput){
$det = explode(",", $anInput);
if( $this->is_val_exists( $det[0], $this->returnProcess() ) ){
//update age of Michael
for ($i=0; $i<count(self::$arrX); $i++) {
if (is_array(self::$arrX[$i]) && self::$arrX[$i]['name'] == $det[0]) {
self::$arrX[$i]['age'] = $det[1];
break;
}
}
} else{
array_push(self::$arrX, array(
'name' => $det[0],
'age' => $det[1]
));
}
}
public function returnProcess(){
return self::$arrX;
}
}
$mc = new myclass();
$mc->doProcess('Michael,18');
$mc->doProcess('John,23');
$mc->doProcess('Michael,19');
$mc->doProcess('John,25');
print_r($mc->returnProcess());
?>
You can test it here: PHP Runnable
As I said in comments, it looks like you want to maintain state between requests. You can't use pure PHP to do that, you should use an external storage solution instead. If it's available, try Redis, it has what you need and is quite simple to use. Or, if you're familiar with SQL, you could go with MySQL for example.
On a side note, you should read more about how PHP arrays work.
Instead of array_push, you could have just used self::$arrX[] = ...
Instead of that, you could have used an associative array, e.g. self::$arrX[$det[0]] = $det[1];, that would make lookup much easier (array_key_exists etc.)
Can you try updating the is_val_exists as follows:
private function is_val_exists($needle, $haystack) {
foreach($haystack as $element) {
if ($element['name'] == $needle) {
return true;
}
return false;
}
I have few helpers - I want to redclare each helper's method as a lambda anonymous function.
I'm trying to do it by getting the helpers methods, and then doing an eval function, but it wont work, im getting parse error..
My current code:
foreach($this->helpers as $helper)
{
include(master_path . 'helpers/'.$helper.'Helper.php');
$helperClass = new applicationHelper();
$methods = get_class_methods($helperClass);
foreach($methods as $method )
{
eval ( "\$$method = function (\$text) {
\$helperClass->$method(\$text);
}");
}
}
Due to efficiency fears - I'd like a better solution if you know it, thanks!
Thanks Guys!
That should work:
foreach($methods as $method )
{
$$method = function($text) use ($method, $helperClass) {
return $helperClass->$method($text);
}
}
But still dont know why are you doing that.
EDIT
PHP 5.3.x needed -> look here Anonymous funcions
foreach ($this->helpers as $helper) {
include(master_path . 'helpers/'.$helper.'Helper.php');
$helperClass = new applicationHelper();
foreach (get_class_methods($helperClass) as $method) {
$$method = function($text) use($helperClass, $method) {
$helperClass->$method($text);
};
}
}
That get's rid of the slow eval.