Dynamically Create Functions with Eval() in PHP - php

I'm trying to create functions dynamically with eval(). But I get this warning: Notice: Use of undefined constant Any suggestion?
$funcs = array('func_a', 'func_b', 'func_c');
foreach($funcs as $func_name) {
eval( 'function ' . $func_name . '() {
mainfunc(' . $func_name . ');
}'
);
}
func_a();
func_b();
func_c();
function mainfunc($func_name) {
echo $func_name . '<br />';
}
Assuming the array $func is an option value stored in a database and I need the function names for a callback function in a separate part of the script. So creating anonymous functions with create_function() is not what I'm looking for.
Thanks for your info.

Use better approach than eval(), it is called overloading.
Example:
class MainFunc {
public function __call($name, $arguments)
{
echo "_call($name)<br>";
}
public static function __callStatic($name, $arguments)
{
echo "_callStatic($name)<br>";
}
}
# php >= 5.4.x
(new MainFunc)->func_a();
(new MainFunc)->func_b("param", "param2");
# or php < 5.4
$mainFunc = new MainFunc;
$mainFunc->func_a();
$mainFunc->func_b("param", "param2");
MainFunc::func_a_static();
MainFunc::func_b_static("param", "param2");
Output is:
_call(func_a)
_call(func_b)
_callStatic(func_a_static)
_callStatic(func_b_static)

Your eval body needs to read:
mainfunc(\'' . $func_name . '\');
Without the single quotes, eval() makes code that has an unquoted literal--an undefined constant.

For those who were wondering what I was talking about, here is the sample WordPress plugin which demonstrates how dynamic function creation comes handy.
/* Plugin Name: Sample Action Hooks with Dynamic Functions */
// assuming this is an option retrieved from the database
$oActions = array( 'a' => array('interval' => 10, 'value' => 'hi'),
'b' => array('interval' => 30, 'value' => 'hello'),
'c' => array('interval' => 60, 'value' => 'bye')
);
add_action('init', LoadEvents);
function LoadEvents() {
global $oActions;
foreach($oActions as $strActionName => $array) {
eval( 'function ' . $strActionName . '() {
SampleEvents(\'' . $strActionName . '\');
}'
);
add_action('sampletask_' . md5($strActionName), $strActionName);
if (!wp_next_scheduled( 'sampletask_' . md5($strActionName)))
wp_schedule_single_event(time() + $oActions[$strActionName]['interval'], 'sampletask_' . md5($strActionName));
}
}
function SampleEvents($strActionName) {
global $oActions;
// just log for a demo
$file = __DIR__ . '/log.html';
$current = date('l jS \of F Y h:i:s A') . ': ' . $strActionName . ', ' . $oActions[$strActionName]['value'] . '<br />' . PHP_EOL;
file_put_contents($file, $current, FILE_APPEND);
wp_schedule_single_event(time() + $oActions[$strActionName]['interval'], 'sampletask_' . md5($strActionName));
}
And the same functionality could be achieved with __call().
/* Plugin Name: Sample Action Hooks */
add_action('init', create_function( '', '$oSampleEvents = new SampleEvents;' ));
class SampleEvents {
public $oActions = array( 'a' => array('interval' => 10, 'value' => 'hi'),
'b' => array('interval' => 30, 'value' => 'hello'),
'c' => array('interval' => 60, 'value' => 'bye')
);
function __construct() {
foreach($this->oActions as $strActionName => $arrAction) {
add_action('sampletask_' . md5($strActionName), array(&$this, $strActionName));
if (!wp_next_scheduled( 'sampletask_' . md5($strActionName)))
wp_schedule_single_event(time() + $this->oActions[$strActionName]['interval'], 'sampletask_' . md5($strActionName));
}
}
function __call($strMethodName, $arguments) {
// just log for a demo
$file = __DIR__ . '/log.html';
$current = date('l jS \of F Y h:i:s A') . ': ' . $strMethodName . ', ' . $this->oActions[$strMethodName]['value'] . '<br />' . PHP_EOL;
file_put_contents($file, $current, FILE_APPEND);
wp_schedule_single_event(time() + $this->oActions[$strMethodName]['interval'], 'sampletask_' . md5($strMethodName));
}
}

Related

codeignigter use rest to pass data to external site

OK in plain PHP I use the following to pass data to a GET
file_get_contents('https://ws.mysite.com/some.svc/here?userID=' . $session_id . '&score=' . $percentilescore . '&assessmentID=' . $testID . '&assessmentTitle=some');
I now want to apply this same piece of code to my CI project.
This Is how I have tried.
private function getResults()
{
$score = $this->actions->getSentEmailCount();
$percentilescore = $percentile = $this->actions->getPercentile($score);
$testID = '134';
$percentile = $this->actions->getPercentile($score);
$time = $this->input->get('time');
$timemath = 60000;
$timeinmseconds = $time * $timemath;
$adddata = array(
'uniID' => '5',
'testName' => 'some',
'testID' => $testID,
'total' => '10',
'correct' => $score = $this->actions->getScore(),
'percent' => $score = $this->actions->getScore(),
'dateTaken' => date('Y-m-d H:i:s'),
'timeSpent' => $timeinmseconds,
'userID' => $session_id,
);
$this->actions->add_record($adddata);
return $this->load->view('client/results', $data);
file_get_contents('https://ws.mysite.com/123.svc/some?userID=' . $session_id . '&score=' . $percentilescore . '&assessmentID=' . $testID . '&assessmentTitle=some');
}
It is not posting the data any idea why and how I should do it in CI ?
return should be the last statement in the function because it's immediately ends execution of the current function. Just put file_get_contents before return:
file_get_contents('https://ws.mysite.com/123.svc/some?userID=' . $session_id . '&score=' . $percentilescore . '&assessmentID=' . $testID . '&assessmentTitle=some');
$this->actions->add_record($adddata);
return $this->load->view('client/results', $data);

How can I email myself the RAW SQL query that this php function is producing?

I want to run explain on a query that is slow but I don't know how to view the raw sql so I can run it in phpmyadmin and debug it. Here is the function.
private function getAttImages($limit, $forumIds = 0, $fidsReverse = false, $topicIds = 0, $membersIds = 0, $order = 'attach_date', $sort = 'desc', $group = null)
{
$fids = '';
if ($forumIds)
{
$r = '';
if ($fidsReverse)
{
$r = ' NOT ';
}
if (is_array($forumIds))
{
$forumIds = implode(',', $forumIds);
}
$fids = ' AND forums_topics.forum_id ' . $r . ' IN (' . $forumIds . ')';
}
$tids = '';
if ($topicIds)
{
$tids = ' AND forums_topics.tid IN (' . $topicIds . ')';
}
$mids = '';
if ($membersIds)
{
$mids = ' AND core_attachments.attach_member_id IN (' . $membersIds . ')';
}
$whereT = array();
$joinsT = array();
$findInPosts = ' AND ' . \IPS\Db::i()->findInSet('queued', array('0'));
$joinsT[] = array(
'select' => 'forums_posts.*',
'from' => 'forums_posts',
'where' => array("forums_posts.pid=core_attachments_map.id2" . $findInPosts),
);
$findInTopics = ' AND ' . \IPS\Db::i()->findInSet('approved', array('1'));
$joinsT[] = array(
'select' => 'forums_topics.*',
'from' => 'forums_topics',
'where' => array("forums_topics.tid=forums_posts.topic_id" . $findInTopics . $fids . $tids),
);
$select = 'core_attachments.attach_id AS custom_data, core_attachments.*';
if ($group)
{
$select = 'core_attachments.attach_id AS custom_data, COUNT(attach_is_image) as cnt_images, SUM(attach_hits) as summ_attach_hits, core_attachments.*';
}
$joinsT[] = array(
'select' => $select,
'from' => 'core_attachments',
'where' => array('core_attachments.attach_is_image=1 AND core_attachments.attach_is_archived=0 AND core_attachments.attach_id=core_attachments_map.attachment_id' . $mids),
);
$joinsT[] = array( 'select' => 'core_members.member_id, core_members.member_group_id, core_members.mgroup_others, core_members.name, core_members.members_seo_name',
'from' => 'core_members',
'where' => array('core_attachments.attach_member_id=core_members.member_id' . $mids),
);
$joinsT[] = array( 'select' => 'core_permission_index.perm_id',
'from' => 'core_permission_index',
'where' => array("core_permission_index.app='forums' AND core_permission_index.perm_type='forum' AND core_permission_index.perm_type_id=forums_topics.forum_id"),
);
$groupT = $group;
$whereT[] = array(
"core_attachments_map.location_key='forums_Forums' AND " .
\IPS\Db::i()->findInSet('perm_view', array_merge(array(\IPS\Member::loggedIn()->member_group_id), array_filter(explode(',', \IPS\Member::loggedIn()->mgroup_others)))) . " OR perm_view='*'" .
$fids . $tids . $mids
);
$table = new \IPS\Helpers\Table\Db(
'core_attachments_map',
\IPS\Http\Url::internal('app=core&module=system&controller=nbattachpictures', 'front', 'nbattachpictures'),
$whereT,
$groupT
);
$table->joins = $joinsT;
$table->sortBy = $order;
$table->sortDirection = $sort;
$table->limit = $limit;
$table->rowsTemplate = array(\IPS\Theme::i()->getTemplate('plugins', 'core', 'global'), 'nbAttachmentsBlocksRows');
$table->parsers = array(
'custom_data' => function( $val, $row )
{
return array(
'topic_data' => \IPS\Http\Url::internal("app=forums&module=forums&controller=topic&id={$row['tid']}", 'front', 'forums_topic', array($row['title_seo'])),
'summ_attach_hits' => $row['summ_attach_hits'],
'jewel' => $this->attachJewel($row['summ_attach_hits']),
);
},
);
return $table;
}
Anybody know how I can see the SQL query only that is produced by this function? email is better than echo as I want to grab query from live site.
You could var_dump($table) and write the result in an email using the native php mail function or write it in a log file (this option is better).
Is that framework open-source? Because I couldn't find any documentation about the class \IPS\Helpers\Table\Db. Probably there's a method in it to build the query, you could look for it at that class source code and put the result of that method into the email message or log file instead of var_dump the table.

How to query database in MediaWiki?

I am working on a custom extension/special page for the first time. I am trying to create a simple page that queries the database and display the result on the page. I got the following code that does that:
class SpecialBuildRating extends SpecialPage {
function __construct() {
parent::__construct( 'BuildRating' );
}
function execute( $par ) {
if(isset($_GET['id'])){
$buildId = $_GET['id'];
$db = wfGetDB( DB_SLAVE );
$res = $db->select(
'build_rating',
array('article_id', 'user_id', 'vote', 'comment', 'date'),
'article_id = 1485', //BuildId instead of 1485
__METHOD__,
array( 'ORDER BY' => 'date ASC' )
);
}
$request = $this->getRequest();
$output = $this->getOutput();
$this->setHeaders();
# Get request data from, e.g.
$param = $request->getText( 'param' );
# Do stuff
# ...
$wikitext = 'Hello world!';
$output->addWikiText( $wikitext );
$outP = '<table style="width:100%">
<tr>
<td>article_id</td>
<td>user_id</td>
<td>vote</td>
<td>comment</td>
<td>date</td>
</tr>
';
if ($res != null) {
foreach( $res as $row ) {
$outP .= '<td>' . $row->article_id . '</td><td>' . $row->user_id . '</td><td>' . $row->vote . '</td><td>' . $row->comment . '</td><td>' . $row->date . '</td>';
}
}
$output->addWikiText( $outP );
}
}
How do I pass the $buildIdto the WHERE statement instead of 1485 in a safe way that prevents injection?
Another question that I have that isn't really an issue is the $output->addWikiText($var); output call, is there any easier/more effective way to do it?
$res = $db->select(
'build_rating',
array('article_id', 'user_id', 'vote', 'comment', 'date'),
array( 'article_id' => $buildId ),
__METHOD__,
array( 'ORDER BY' => 'date ASC' )
);
See https://www.mediawiki.org/wiki/Manual:Database_access for details.
As of outputting, use $output->addHTML(), however in that case you're responsible yourself for preventing XSS.
Another point, in MediaWiki it's recommended to use $this->getRequest()->getInt( 'name', $defaultValue ) instead of accessing request globals directly.

Create instance of class based on string for name and array for parameters

The title is really explicit, but I am going to give an example:
class Foo
{
public function __construct($param1, $param2) {
echo 'Class name: ' . __CLASS__ . "\n" .
'param1 = ' . $param1 . "\n" .
'param2 = ' . $param2;
}
}
$class_name = 'Foo';
$params =
[
'content1',
'content2'
];
Having this code, how can I create an instance of Foo, using the parameters in $params.
I know I can do
$class = new $class_name($params);
But this would pass the parameters as one array, I would like it to expand the parameters. The following would be the behavior I would like:
$class = new $class_name($param[0], $param[1]);
Thank you!
If you have 5.6:
$instance = new $class(...$args);
If not use reflection:
$reflect = new ReflectionClass($class);
$instance = $reflect->newInstanceArgs($args);
I hesitate to post this because really you should design to either accept an array of parameters or individual parameters. However, here are to hackish ways:
Using another function:
public function init($params) {
echo 'Class name: ' . __CLASS__ . "\n" .
'param1 = ' . $params[0] . "\n" .
'param2 = ' . $params[1];
}
public function __construct($param1, $param2=null) {
if(!is_array($param1)) {
$param1 = func_get_args();
}
call_user_func_array(array($this, 'init'), $param1);
}
Doing it all in the __construct:
public function __construct($param1, $param2=null) {
if(!is_array($param1)) {
$param1 = func_get_args();
}
echo 'Class name: ' . __CLASS__ . "\n" .
'param1 = ' . $param1[0] . "\n" .
'param2 = ' . $param1[1];
}

Zend Framework Class in Custom Form Decorator

I am using a custom form decorator found at: http://code.google.com/p/digitalus-cms/source/browse/trunk/library/Digitalus/Form/Decorator/Composite.php?r=767
At the bottom of the file (line 70) is:
$output = '<div class="form_element">'
. $label
. $input
. $errors
. $desc
. '</div>';
I would like to make the DIV class dynamic and passed when I create the elements in my controller. Any built-in ZEND functions I use only modifies the LABEL or INPUT. Here's an example of my element creation:
$decorator = new Composite();
$this->addElement('text', 'start', array(
'label' => 'Start Number',
'required' => true,
'filters' => array('StringTrim'),
'validators' => array(
'alnum',
),
'decorators' => array($decorator)
));
Any ideas would be very much appreciated. Thanks for taking the time to look!
Now sure why all CSS classes are hardcoded, if you are allowed to change this current decorator just fix the render() method:
class Digitalus_Form_Decorator_Composite
{
/* ... */
public function render($content)
{
$element = $this->getElement();
if (!$element instanceof Zend_Form_Element) {
return $content;
}
if (null === $element->getView()) {
return $content;
}
$separator = $this->getSeparator();
$placement = $this->getPlacement();
$label = $this->buildLabel();
$input = $this->buildInput();
$errors = $this->buildErrors();
$desc = $this->buildDescription();
$output = '<div class="'.$this->getOption('class').'">'
. $label
. $input
. $errors
. $desc
. '</div>';
switch ($placement) {
case (self::PREPEND):
return $output . $separator . $content;
case (self::APPEND):
default:
return $content . $separator . $output;
}
}
/* ... */
}
And during element creation:
$element->setDecorators(array(
/* ... */
array(array('div'=>'Composite'), array('class' => 'my_class_name'))
/* ... */
)));
If you don't want to edit existing decorator, just extend it and override render() method...

Categories