PHP: Storing callback functions with arguments on array - php

I am writing a simple router class which would allow you to execute a function when a route match is found. So, when you are defining routes, you would do something like this:
$message = 'Good morning!';
$router->addRoute('GET', 'welcome', function($message) {
echo $message;
});
Then, in the router class, this gets added to an array like:
public function addRoute($method, $pattern, $handler) {
$this->routes[$pattern] = ['method' => $method, 'handler' => $handler];
}
Once a match is found by comparing the pattern with the requested URL, I have:
return call_user_func($setting['handler']);
This gives me an error: Fatal error: Uncaught ArgumentCountError: Too few arguments to function
...so I tried using:
return call_user_func_array($setting['handler'], array($message));
...but this gives me: Notice: Undefined variable: message
How can I pass a function (including arguments, if existing) to execute within the router class using values stored on an array?

If you don't want to pass $message as an argument at call time, it should not be in the function parameter list. You're probably just looking to use the variable from the surrounding scope:
$message = 'Good morning!';
$router->addRoute('GET', 'welcome', function () use ($message) {
echo $message;
});

Related

How to use function from other class

Im trying to send a email with a list of companies that have the pending or waiting status.
This list is already collected in the following function:
public static function getCompaniesAwaitingCheck()
{
$awaitingChangeApproval = self::getAwaitingChangeApproval();
$awaitingPublication = self::getAwatingPublication();
return array_merge($awaitingChangeApproval, $awaitingPublication);
}
Now I want to use that function to put in the email. I have made a separate class for this (AdminPendingApprovalNotification.php)
In there is the following function:
public function notifyPendingApproval()
{
$dailyClaimMailTitle = get_field("daily_claim_overview_mail_title", 'options');
$dailyClaimMailText = get_field('daily_claim_overview_mail_text', 'options');
$dailyClaimMailAddress = get_field('daily_claim_overview_mail', 'options');
$company = new Company();
$pendingCompany = $company->getCompaniesAwaitingCheck();
wp_mail(
$dailyClaimMailAddress,
ecs_get_template_part('views/email/template', [
'title' => $dailyClaimMailTitle,
'text' => $dailyClaimMailText, $pendingCompany,
], false),
['Content-Type: text/html; charset=UTF-8']
);
}
When I dd($pendingCompany); I get the error:PHP Fatal error: Uncaught ArgumentCountError: Too few arguments to function Models\Model::__construct(), 0 passed in Emails/AdminPendingApprovalNotification.php on line 16 and exactly 1 expected
line 16: $company = new Company();
Unfortunately can’t get it to work, I’m a beginner, some help would be appreciated. Thanks!
Your method getCompaniesAwaitingCheck is static, so you should call it like:
$pendingCompany = Company::getCompaniesAwaitingCheck();
An error occured because your Company class requires arguments in the __construct method, which you didn't provide.

How to access variables created dynamically inside functions and methods in PHP?

I can't access variables created dynamically in a function or method of my objects in PHP. It is probably a matter of scope, but I do not know why this happens as in JavaScript when you declare a var inside a function you can still access it outside that function.
What I'm doing is this:
#code
...
$inputs = ['olympiad', 'test_type', 'year', 'level', 'country', 'test', 'answersheet'];
$Form = new Form;
$Form->addFields($inputs);
foreach (array_keys($Form->fields) as $key) {
${"$key"} = $Form->fields["$key"];
}
$olympiad->required(true)->type('select')->inValues($olyimpiadsArray)->label('Olmpíada')->errorMessage('some error message here');
$test_type->required(true)->type('select')->inValues($testTypeArray)->errorMessage('bla bla');
$level->required(true)->type('select')->inValues(['Nacional', 'Regional'])->label('Nível')->errorMessage('sample error message');
$year->required(true)->type('int')->range(1998, 2019)->label('Ano')->errorMessage('another error message');
$country->required(true)->type('string')->range(4, 64)->label('País')->errorMessage('these arent the real error messages');
$test->type('file')->label('Prova')->allowedExtensions(['pdf'])->errorMessage('bla bla');
$answersheet->type('file')->label('Gabarito')->allowedExtensions(['pdf'])->errorMessage('bla bla bla');
That works because the Fields are objects and they are passed as reference, so I can access these objects through the created variables inside the foreach and the Form will also be able to validate the Field objects.
I'm using the same foreach in many webpages, so it is quite awkward because I'm copying and pasting the code, again and again.
EXPECTED RESULTS
What do I want? I want to do this:
Class Form {
#code
...
public function create_vars_for_fields() {
foreach(array_keys($this->fields) as $key) {
${"$key"} = $this->fields["$key"];
}
return $this;
}
}
Then, on my PHP webpages I shoul be able to do this:
require_once 'Form.php';
$inputs = ['olympiad', 'test_type', 'year', 'level', 'country', 'test', 'answersheet'];
$Form = new Form;
$Form->addFields($inputs)->create_vars_for_fields();
#code
...
//here I should be able to access my variables, which are now objects of the class Fields
echo $test_type->value; //should echo the test type of the olympiad, which is equal to $_POST['test_type']
echo $country->value; //should echo the country of the olympad, which is equal to $_POST['country']
However, the code above throws many errors saying that these variables are undefined.
Notice: Undefined variable: olympiad in C:\xampp\htdocs\projects\phpFormBuilder\tests\TestValidation.php on line 8
Fatal error: Uncaught Error: Call to a member function required() on null in C:\xampp\htdocs\projects\phpFormBuilder\tests\TestValidation.php:8 Stack trace: #0 C:\xampp\htdocs\projects\phpFormBuilder\tests\AddTest.php(3): require_once() #1 {main} thrown in C:\xampp\htdocs\projects\phpFormBuilder\tests\TestValidation.php on line 8
Line 8 is the line I say $olympiad->required(true)
How can I access these variables by creating them dynamically in a function or method?
I finally found a smart, clear way to accomplish this! The solution uses extract built-in function.
Although I do not need it anymore because I'm using Laravel for validation, I still want to share the answer so it can help others.
SOLUTION
Class Form {
#code
...
public function getFieldsCompacted() {
$compactedFields = [];
foreach(array_keys($this->fields) as $key) {
$compactedFiels["$key"] = $this->fields["$key"];
}
return $compactedFields;
}
}
Then, on the code:
require_once '/path/to/Form.php';
$inputs = ['olympiad', 'test_type', 'year', 'level', 'test', 'answer_sheet'];
$Form = new Form;
$Form->addFields($inputs);
extract($Form->getFieldsCompacted());
This way I can do:
$olympiad->required(true)->type('select')->inValues($olyimpiadsArray)->label('Olimpíada')->errorMessage('some error message here');
$test_type->required(true)->type('select')->inValues($testTypeArray)->errorMessage('bla bla');
$level->required(true)->type('select')->inValues(['Nacional', 'Regional'])->label('Nível')->errorMessage('sample error message');
$year->required(true)->type('int')->range(1998, 2019)->label('Ano')->errorMessage('another error message');
$test->type('file')->label('Prova')->allowedExtensions(['pdf'])->errorMessage('bla bla');
$answersheet->type('file')->label('Gabarito')->allowedExtensions(['pdf'])->errorMessage('bla bla bla');
Instead of doing:
$Form->fields['olympiad']->required(true)->type('select')->inValues($olyimpiadsArray)->label('Olimpíada')->errorMessage('some error message here');
$Form->fields['test_type']->required(true)->type('select')->inValues($testTypeArray)->errorMessage('bla bla');
$Form->fields['level']->required(true)->type('select')->inValues(['National', 'State'])->label('Nível')->errorMessage('sample error message');
$Form->fields['year']->required(true)->type('int')->range(1998, 2019)->label('Ano')->errorMessage('another error message');
$Form->fields['test']->type('file')->label('Prova')->allowedExtensions(['pdf'])->errorMessage('bla bla');
$Form->fields['answersheet']->type('file')->label('Gabarito')->allowedExtensions(['pdf'])->errorMessage('bla bla bla');
Edit:
RiggsFolly pointed out the insecurity of using extract. I totally agree that using extract in $_POST and $_GET. However, this is not the case here because the variables to extract are defined as you can see in
$inputs = ['olympiad', 'test_type', 'year', 'level', 'test', 'answer_sheet'];. So, the extract will only extract these variables and it won't override other variables. Therefore, no risk in the code above.

OctoberCMS - Mail::send(), Undefined variables - Laravel

I am trying to send the SAME email to multiple addresses given from an array $emails.
I created a class called SendMail, and inside is a sendPost() method that accepts 2 arguments:
($post, $emails)
Here is my code:
class SendMail {
public static function sendPost($post, $emails)
{
Mail::send('acme.blog::mail.message', $post, function($message) {
$message->to($emails);
$message->from('mail#compuflexcorp.com', 'Compuflex Mail');
$message->subject($post['subject']);
$message->replyTo($post['email']);
});
}
}
The problem is, I keep receiving an error:
"Undefined variable $emails" on Line 14 of C:\...\SendMail.php
Line 14: $message->to($emails);
What I have tried:
I checked to see if I can access the $post and $emails variables inside of sendPost(), but outside of Mail::send(). And the answer is YES, I can access the information inside of $post and $emails inside of sendPost(), so the variables are, in fact, being passed to the sendPost() method.
I, at first, thought it had something to do with the fact that $emails is not one of the arguments for Mail::send(), so I put $post and $emails into one array called $vars, but then I got the error:
"Undefined variable $vars" on Line 14 of C:\...\SendMail.php
So, I realized that the issue seems to be that I can't pass any variables to Mail::send(), or in other words, I just don't know how to...
Any help would be greatly appreciated...
Thomas Yamakaitis
You need to pass the $emails variable as follows:
class SendMail {
public static function sendPost($post, $emails)
{
Mail::send('acme.blog::mail.message', $post, function($message) use ($emails) {
$message->to($emails);
$message->from('mail#compuflexcorp.com', 'Compuflex Mail');
$message->subject($post['subject']);
$message->replyTo($post['email']);
});
}
}

Can't "get" data in get ajax request using laravel

I just can't retrieve the data in my query string section.
I've used AJAX request throughout my website to implement a wide variety of tasks asynchronously and didn't had an issue of this kind.
Route
Route::get('/mensagem/enviar_mensagem', [ 'as' => 'mensagem.enviar_mensagem', 'uses' => 'MensagemController#enviar_mensagem']);
the testing url:
http://mydomain.com.br/mensagem/enviar_mensagem?para=email#bol.com.br
my action method:
public function enviar_mensagem(Request $request)
{
$para = $request->get('para');
//$para = $_GET['para']; I get an undefined index error
echo $para; //always empty string!
}
You need to use input. Like so:
Also, for testing, return versus echo.
public function enviar_mensagem(Request $request)
{
$para = $request->input('para');
return $para;
}
And to spark my curiosity, what does return $request->all() return?
Well, the provided code seems to be correct. Make sure you use \Illuminate\Http\Request. This code
Route::get('/mensagem/enviar_mensagem', function(\Illuminate\Http\Request $request) {
return $request->para;
// return $request->get('para'); // also works
});
returns email#bol.com.br by request http://your-site.app/mensagem/enviar_mensagem?para=email#bol.com.br
I copy pasted your code and both works:
$para = $request->get('para');
$para = $_GET['para'];
//$para = $_GET['para']; I get an undefined index error
Did you make sure the webserver is properly handling the HTTP request?
https://laravel.com/docs/5.4#web-server-configuration
You can try with below code :
use Request;
class xyzController {
public function enviar_mensagem()
{
$para = Request::all();
echo $para['para'];
}
}
First you will need to change the route to add also this
Route::get('/mensagem/enviar_mensagem/{para}',
[ 'as' => 'mensagem.enviar_mensagem', 'uses' =>
'MensagemController#enviar_mensagem']);
And after that in controller
public function enviar_mensagem($para){
return var_dump($para);
}
Use the route method on the request object to access GET parameters
public function enviar_mensagem(Request $request)
{
$para = $request->route('para');
echo $para;
}

How does one define a PECL bbcode extension callback when writing a CodeIgniter library?

I'm writing a CodeIgniter library around PHP's bbcode PECL extension, but I'm having some trouble with callbacks.
I set up the handler in the library constructor:
function __construct() {
$basic = array(
'url' => array(
'type' => BBCODE_TYPE_OPTARG,
'open_tag' => '<a href="{PARAM}" rel="nofollow">',
'close_tag' => '</a>',
'childs'=>'i,b,u,strike,center,img',
'param_handling' => array($this, 'url')
)
);
$this->handler = bbcode_create($basic);
}
public function parse($bbcode_string) {
return bbcode_parse($this->handler, htmlentities($bbcode_string));
}
As you notice, this uses a callback for handling what's allowed to go into the URL. I use this to insert an "exit redirect" page
public static function url($content, $argument) {
if (!$argument) $argument = $content;
$url = parse_url($argument);
if (!isset($url['host'])) {
if (strlen($argument) > 0 && $argument[0] != '/') return false;
$destination = '//'.$_SERVER['HTTP_HOST'].$argument;
} elseif ($url['host'] != $_SERVER['HTTP_HOST']) {
$destination = '//'.$_SERVER['HTTP_HOST'].'/exit?'.urlencode($argument);
} else {
$destination = $argument;
}
return htmlspecialchars($destination);
}
And I also have a little function which helps me test this out as I work:
function test() {
$string = '[url]http://www.google.com[/url]';
echo '<pre>';
die($this->parse($string));
}
This all works fine if the test() method is called from within the library. For example, if I throw $this->test() at the bottom of the constructor, everything works exactly as I would expect. However, calling $this->bbcode->test() from somewhere else (e.g. in a controller), I get the following errors:
**A PHP Error was encountered**
Severity: Warning
Message: Invalid callback , no array or string given
Filename: libraries/bbcode.php
Line Number: 122
**A PHP Error was encountered**
Severity: Warning
Message: bbcode_parse(): function `' is not callable
Filename: libraries/bbcode.php
Line Number: 122
http://www.google.com
The callback does not get executed, and as a result the link's href attribute is empty. Line 122 refers to the single line of code in my parse function:
return bbcode_parse($this->handler, htmlentities($bbcode_string));
How do I address this callback function such that it can be located when $this->bbcode->test() is called from inside a controller?
Now I'm even more confused...
So in the hopes of just putting this all behind me, I put these callback functions in a helper so I can just call them directly. So I now have code like this:
function __construct() {
$basic = array(
'url' => array(
'type' => BBCODE_TYPE_OPTARG,
'open_tag' => '<a href="{PARAM}" rel="nofollow">',
'close_tag' => '</a>',
'childs'=>'i,b,u,strike,center,img',
'param_handling' => 'bbcode_url'
)
);
$this->handler = bbcode_create($basic);
}
With the above, I get the following error:
**A PHP Error was encountered**
Severity: Warning
Message: Invalid callback 6.7949295043945E-5, no array or string given
Filename: libraries/bbcode.php
Line Number: 176
**A PHP Error was encountered**
Severity: Warning
Message: bbcode_parse(): function `6.7949295043945E-5' is not callable
Filename: libraries/bbcode.php
Line Number: 176
(line 176 is the new location of the parse() function)
Um... I don't even know what's going on. The number 6.7949295043945E-5 changes with every attempt.
The only solution I have found to this is quite simply not to set the handler up in a constructor. Instead, the parse() method contains both the bbcode_create() call and the bbcode_parse() call. That is, the bbcode_handler is freshly created for every string of bbcode to be parsed.
This seems needlessly wasteful to me. But, over the course of the lifetime of this project, it is exceedingly unlikely to cost even a tenth of the amount of time that I have spent trying to sort this out "properly", so I'm calling it a day.
I'm posting this "solution" here in case somebody else happens across this question and can thereby save themselves a few hours' pain. That said, I would really like to know what on earth is going on, and how to do this properly.

Categories