given a loop that sends an email to all subscriptions in an array
foreach($subscriptions as $s){
if(!$s->send_email()){
}
}
What would be the cleanest way to trigger a callback if all models have mailed successfully or show an error if one of the models fails to mail. Is it common to save all error messages till the end of the loop and print them as a whole, or break the loop with an error.
I'm using this in combination with a JSON REST API saving a project (/projects/1) which in turn emails all users.
The method I'm using now feels dirty with lot's of nested if else, sending 3 different response on different places
if($project->save()){
$subscriptions = Subscription::model()->findAllByAttributes(array('planning_id' => $planning->id));
foreach($subscriptions as $s){
if(!$s->send_email()){
$errors[] = "failed to send email. Subscription ". $s->id;
}
}
if(count($errors) > 0){
//send json api response with error response
} else {
//send json api success response
}
} else {
//send json api response with project error response
}
I was wondering what convention is concerning this
It is a little messy - and it combines multiple concerns within the "save" function - anyone reading the code needs to understand what "save" means, how we loop through the contacts etc.
I'd refactor it as follows:
if($project->save()){
$subscriptions = Subscription::model()->findAllByAttributes(array('planning_id' => $planning->id));
$errors = sendMailToSubscribers($subscriptions);
$response = determineResponse($errors);
// send JSON API response
} else {
//send json api response with project error response
}
function sendMailToSubscribers($subscriptions){
foreach($subscriptions as $s){
if(!$s->send_email()){
$errors[] = "failed to send email. Subscription ". $s->id;
}
}
return $errors;
}
function determineResponse($errors){
if(count($errors) > 0){
//return json api response with error response
} else {
//return json api success response
}
}
You can use while logic so that failure falls through to the end of the block.
while(1) {
if ($project->save()) {
foreach($subscripts as $s)
if (!$s->send_email())
$errors[] = "failed to send email. Subscription ". $s->id;
} else
$errors[] = 'failed to save the project';
if (empty($errors)) {
//send success here
break;
}
//send your errors here
break;
}
Related
I am using AWS to send emails in my project.. I created subscription and done all the steps. Its sending emails to the user but the notification is not receiving.. it says notification failed. I don't know the reason.. but I am getting notifications if I use http url instead https url.
The php code I am using is added below -
<?php
require 'path/to/vendor/autoload.php';
use AwsSnsMessageValidatorMessage;
use AwsSnsMessageValidatorMessageValidator;
use GuzzleHttpClient;
// Make sure the request is POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
die;
}
try {
// Create a message from the post data and validate its signature
$message = Message::fromRawPostData();
$validator = new MessageValidator();
$validator->validate($message);
} catch (Exception $e) {
// Pretend we're not here if the message is invalid
http_response_code(404);
die;
}
if ($message->get('Type') === 'SubscriptionConfirmation') {
// Send a request to the SubscribeURL to complete subscription
(new Client)->get($message->get('SubscribeURL'))->send();
} elseif ($message->get('Type') === 'Notification') {
// Do something with the notification
save_message_to_database($message);
}
I am following the AWS documentation. Do you know the reason.. is there any issue in this code?
class REST {
// this section is from http://www.tutorialsface.com/2016/02/simple-php-mysql-rest-api-sample-example-tutorial/
public function response($data,$status){
$this->_code = ($status)?$status:200;
$this->set_headers();
echo $data;
exit;
}
}
class API extends REST {
private function create_booking(){
if($this->get_request_method() != "POST"){
echo $this->response('Method Not Acceptable',406);
}
/*pseudocode for processing data if method is POST*/
Find whether record exists.
If exist {
set $message = "record found"
} else {
If not exist, insert record
set $message = "record inserted"
}
echo $this->response($message,200);
}
}
I would like to know, if let's say I have an API end point using the above method, when user is not using POST, will system stop processing after echo the error 406, or it will still continue after existing IF statement.
I have users submitting a new booking to this end point several times due to server did not response a message. End up I have duplicate bookings in my database.
What could lead to such incident?
Yes, it will execute further, but you need to stop further execution. You can add a simple return in your method below the echo message to quit the method:
if($this->get_request_method() != "POST"){
echo $this->response('Method Not Acceptable',406);
return;
}
I have a mail function in php that echoes a response.
The browser's developer tool is showing the response as string, but when I capture that response in the front end, with a callback function, it is showing as a JSON object.
So what is printed in the screen is :
{"0":"A","1":" ","2":"t","3":"e","4":"m","5":"p","6":"o","7":"r","8":"a","9":"r","10":"y","11":" ","12":"p","13":"a","14":"s","15":"s","16":"w","17":"o","18":"r","19":"d","20":" ","21":"h","22":"a","23":"s","24":" ","25":"b","26":"e","27":"e","28":"n","29":" ","30":"s","31":"e","32":"n","33":"t","34":" ","35":"t","36":"o","37":" ","38":"y","39":"o","40":"u","41":"r","42":" ","43":"e","44":"m","45":"a","46":"i","47":"l"}
I have no idea why? I am using Laravel 5.1 and AngularJS for the front end.
Here is the code for the function :
if (Mail::send('test-view', ['pass' => $pass], function($message) use ($data)
{
$message->to('esolorzano#renovatiocloud.com', 'Tarzan de la Selva')->subject('Password Reset');
})){
echo "A temporary password has been sent to your email";
}else {
echo "There was an error sending the temporary password";
}
I just fixed it, I did this
$response = new \stdClass();
$response->message = "A temporary password was sent to your email";
return json_encode($response);
And then in angular I just use response.message and that is it. Thanks
I fixed it
$response = new \stdClass();
$response->message = "A temporary password was sent to your email";
return json_encode($response);
then in Angular, I just use response.message.
This is my action on my controller
public function invite()
{
if($this->getRequest()->isPost()){
$data_array = $this->getAllParams();
$emailList = explode(",", $data_array['emails']);
$message = $data_array['message'];
$error_emails = array();
if(sizeof($emailList) <= 1){
$this->view->message = '<div class="alert alert-danger"> Please enter more than one email address and please separate emails by a "," (comma)</div>';
}
else{
$x = 0;
foreach($emailList as $singleEmail){
if (!filter_var(trim($singleEmail), FILTER_VALIDATE_EMAIL)) {
$error_emails[] = $singleEmail;
}
else{
//send emails here
$x++;
}
}
if(sizeof($error_emails) > 0){
$email_str = implode(",",$error_emails);
if($x > 0 ){
$this->view->message = '<div class="alert alert-success"> Successfully Sent! </div> ';
}
$this->view->message .= '<br /><div class="alert alert-danger"> Message to this emails were not sent! <b>'.$email_str.'</b> <br /> Please enter valid emails!</div>';
}else{
$this->view->message = '<div class="alert alert-success"> Successfully Sent! </div>';
}
}
$request = $this->getRequest();
$request->setParam('emails', null);
$request->setParam('message', null);
}
}
I tried the solutions i found;
As you can see i tried setParam method to set values to null. Unfortunately, this doesn't work.
also unset array didn't work- not sure if i did it right.
Anyway, i don't want to redirect. Just want to unset. Can someone help me out? I've been trying this for few hours now. Thanks in advance.
This problem is not specific to Zend Framework, There is a pattern called PRG (short for Post Redirect Get) to deal with resubmissions of forms: https://en.wikipedia.org/wiki/Post/Redirect/Get
Zend Framework provides PRG controller plugin http://framework.zend.com/manual/current/en/modules/zend.mvc.plugins.html#post-redirect-get-plugin In short, it stores post data in session and issues redirect, then on subsequent get request it returns that stored post data.
Example code suggests that you handle form on GET request after PRG plugin did redirect:
// Pass in the route/url you want to redirect to after the POST
$prg = $this->prg('/user/register', true);
if ($prg instanceof \Zend\Http\PhpEnvironment\Response) {
// returned a response to redirect us
return $prg;
} elseif ($prg === false) {
// this wasn't a POST request, but there were no params in the flash messenger
// probably this is the first time the form was loaded
return array('form' => $form);
}
// $prg is an array containing the POST params from the previous request
$form->setData($prg);
if($form->isValid()) {
...
But I disagree with that approach and suggest to always handle form on POST and use PRG to show form with filled data and validation messages afterwards (note $form here is an instance of Zend\Form):
if($this->getRequest()->isPost()) {
$form->setData($this->getRequest()->fromPost());
if ($form->isValid()) {
//handle form
// if you don't need to show form again, redirect:
return $this->redirect()->toRoute('some/route');
}
}
// And now do PRG to re-display form with data and validation errors
$prg = $this->prg('/user/register', true);
if ($prg instanceof \Zend\Http\PhpEnvironment\Response) {
// returned a response to redirect us
return $prg;
} elseif ($prg === false) {
// this wasn't a POST request, but there were no params in the flash messenger
// probably this is the first time the form was loaded
return array('form' => $form);
}
// $prg is an array containing the POST params from the previous request
$form->setData($prg);
// make sure form have validation result to display
$form->isValid();
return array('form' => $form);
But since you are not using forms, you will need to validate data manually twice. Once to process and once to display error messages. If you do not want to use Zend\Form component, I would suggest to look at Zend\InputFilter to validate input.
You can use CSRF protection in zend ie use a token while submitting the form
I'm trying to incorporate PagSeguro (a payment gateway - Brazil's version of PayPal) into my site. After the customer finishes with PagSeguro, they send data (via POST) to a function which I specify. However, I'm not receiving the POST. After doing all the troubleshooting I could think of, I contacted PagSeguro. They said that their log indicates that the POST is being sent as normal but they are receiving an HTTP 302 response.
In order to figure out why this is happening, I created a form with hidden values to simulate sending a POST to my function. I put this form under a different domain just in case it had something to do with that. Every time I send the POST from my simulation form, I receive an HTTP 200 response, and my log indicates that the POST was received.
How is it possible that PagSeguro is receiving a different response than my simulation? Could it have something to do with the server or is it something to do with my script?
Here is the function (using CodeIgniter) that should be receiving the POST:
function pagseguro_retorno(){
if (count($_POST) == 0) {
return FALSE;
}
$msg = 'POST RECEIVED';
$simulate = $this->input->post('Simulate');
if ( ! empty($simulate)){
$result = 'VERIFICADO';
$msg .= ' FROM SIMULATOR';
} else {
$this->load->library(PagSeguroNpi);
$result = $this->PagSeguroNpi->notificationPost();
}
$this->log($msg);
if ($result == "VERIFICADO") {
$id = $this->input->post('Referencia');//cart id
$this->load->model('transacao_model');
$trans_row = $this->transacao_model->get_transaction($id);
if ( ! is_object($trans_row)){
//LOAD NEW TRANSACTION
if ( ! $this->new_transaction($id)){
$notice = "Unable to load new transaction</p><p>";
$this->log($notice);
$notice .= '<pre>'.print_r($_POST, TRUE).'</pre>';
$this->email_notice($notice);
}
}
$this->load->model('carrinho_model');
if($_POST['StatusTransacao'] == 'Aprovado'){
$status = 'apr';
}elseif($_POST['StatusTransacao'] == 'Em AnĂ¡lise'){
$status = 'anl';
}elseif($_POST['StatusTransacao'] == 'Aguardando Pagto'){
$status = 'wtg';
}elseif($_POST['StatusTransacao'] == 'Completo'){
$status = 'cmp';
//nothing more happens here - client must click on 'mark as shipped' before cart is removed and history data is loaded
}elseif($_POST['StatusTransacao'] == 'Cancelado'){
//reshelf - don't set $status, because the cart's about to be destroyed
$this->carrinho_model->reshelf(array($id));
}
if (isset($status)){
$this->carrinho_model->update_carrinho($id, array('status' => $status));
}
} else if ($result == "FALSO") {
$notice = "PagSeguro return was invalid.";
$this->log($notice);
$notice .= '<pre>'.print_r($_POST, TRUE).'</pre>';
$this->email_notice($notice);
} else {
$notice = "Error in PagSeguro request";
$this->log($notice);
$notice .= '<pre>'.print_r($_POST, TRUE).'</pre>';
$this->email_notice($notice);
}
}
SECURITY UPDATE:
After posting, I soon realized that I was opening myself up to hack attempts. The function necessarily has to be public, so anyone who knows the name of the function could access it and post 'simulate' to get immediate verification. Then they could pass whatever data they wanted.
I changed the name of the function to something that would be impossible to guess, and when not in production mode, I've disabled the simulate option.
The problem was that my CSRF protection causes a redirect when the proper CSRF code isn't sent with the POST. My simulator was working, because I copied the HTML generated by a dynamic simulator that I made on the site in question. I then pasted the HTML to create a static simulator and put it under a different URL. Along with the HTML, I inadvertently pasted the CSRF code as well. The static simulator worked fine until the code expired. I quit using it after a couple times, so I didn't discover that it was no longer working until today.
I know that this exact scenario probably won't happen again in the next 500 years, but things like CSRF security can cause problems with things like payment gateways if not disabled for the function receiving the POST.