multiple return statements at once? - php

I am trying to develop a chat bot.
I have a doubt regarding the functionality.
Here's a part of the code:
<?php
require_once 'bootstrap.php';
require_once CB_HOME.'/library/CommonFunctions.php';
class testBot extends AbstractCbRest{
public function subscriptionCreated($userName){
return "Welcome ";
}
public function subscriptionDeleted($userName){
return "Thanks ";
}
public function messageReceived($from, $message){
return "" ; // how to return multiple here
}
In the messageReceived function ,I am going to implement the chatbot functionality.
Whenever i get a message from the user i am going to return something.For that i will have to run some php scripts and make some api calls.The final result i will send to the user.
All this is going to take approximately 20-25 seconds.
Is there a way I can send multiple return statements?
Like while the Api calls are being made i can send a message to the user and then wait for the actual result to come and then send it?
I cannot think of a way because as soon i sent a message,i cannot return something until and unless user replies with something.

Use an array :
public function messageReceived($from, $message){
return array(
'Welcome',
'Thanks',
'Hello',
'Whatever'
);
}
Or even an associative array
array(
'msg1' => 'Welcome',
'msg2' => 'Thanks',
'msg3' => 'Hello',
'msg4' => 'Whathever'
)
Then you can use a particular message with :
array['msg1']

You could create an array and return that :
public function messageReceived($from, $message){
$retval = array();
$retval[] = "message1";
$retval[] = "message2";
return $retval;
}
or
public function messageReceived($from, $message){
return array("message1","message2");
}

It sounds like you need to think about using AJAX rather than making synchronous calls while the page rendering is being processed.

Related

How to ignore specific private static function with PHPUnit?

I'm new to PHPUnit and wondering is it possible to write a test for which ignore specific method.
The code is like examine whether $data is Valid or not, and if it find irregular data, send message to slack with it.
My question is, is it possible to run a test without sending alert message, like ignore sendAlert function?
If possible, I want to know how to write it, If not, I want know why and how to make this code testable.
Thanks!!
example code )
public static function isValid($data) {
// some code here
if (Valid) {
return true;
} else {
// some code here to find irregular
if (irregular) {
self::sendAlert($data);
}
return false;
}
}
private static function sendAlert($data) {
// send alert to slack
Example_Model_Slack::post($slackMsg, $channel);
}
<?
class Example_Model_Slack
{
public static function post($text, $channel = '') {
// make $params from $text and $channel
// POST
$stream = [
'http' => [
'method' => 'POST',
'protocol_version' => 1.1,
'content' => http_build_query($params),
],
];
return file_get_contents(self::POST_URL, false, stream_context_create($stream));
}
}
Edit after the question edit
If your code is in a namespace (which should be, it's good practice), it's extremely easy:
Create a new function in a separate file that is only included by your UnitTest file. This file should have the same namespace as your code. In this example, Example_Model_Slack is in the namespace Foobar\Models.
<?php
namespace Foobar\Models;
function file_get_contents(string $filename, bool $use_include_path = false, resource $context = ?)
{
return 'Whatever you want';
}
When you call a function, the code looks for it:
In the specifically used functions.
In the same namespace.
In the built-in functions.
Therefore, your code will use the built-in file_get_contents (namely \file_get_contents), but your test will use the one in the same namespace (namely \Foobar\Models\file_get_contents).
Original answer
The easiest would be to actually call sendAlert, but to mock the call to its content. As you didn't provide the code of that method, I can't be more precise, juste browse through the doc and figure it out by yourself or, alternatively, show us the code.
For a theorectical and general answer: your sendAlert method probably uses one that is provided by an external vendor, let's say \SlackApi\Slack::send($message). In that case, you could mock the provided \SlackApi\Slack class to replace the send method with one that doesn't actually send anything but still returns the expected data.

Yii, Is it good MVC practice to echo in the controller?

It seems like i am not following the MVC design structure.
I'm making an ajax call from my view to a Controller function
Controller
public function actionGetClient()
{
$user = Client::model()->findByAttributes(array('email'=>$_POST['email'], 'password'=>$_POST['pass']));
echo $user->fullname;
}
View (the calling ajax)
CHtml::ajaxLink(
$text = 'get user',
$url = Yii::app()->createUrl('[my controller]/getClient'),
$ajax=array (
'type'=>'POST',
'data' => array('email'=>email, 'pass'=>pass),
'beforeSend' => "function( request )
{
$(\".result\").html(\"fetching...\")
}",
'success'=>"function(data){
$(\".result\").html(\"user is :\"+data)
}
"
));
Is it good to "echo" the $user->fullname inside the controller for the ajax success function to display it? My boss doesn't like it when i print stuff in my controller, how can i approach this
because when i use return instead, the ajax success gets a null value
return $user->fullname;
No,
It's not a good practice.
You need to create a view to use echo.
You can use return $this->renderPartial('VIEW_NAME'); to render a view without Layout.
You should write 'return' instead of 'echo'. 'echo' is not a good practice for ajax response. You don't need to make a new view for just return a name in your case.
public function actionGetClient()
{
$user = Client::model()->findByAttributes(array('email'=>$_POST['email'],'password'=>$_POST['pass']));
return $user->fullname;
}
No. A controller’s supposed to pass its results to a view for rendering.
I would avoid echoing in the controller what we usually do is have a ajax view folder and a json view and render with that so:
public function actionGetClient()
{
$user = Client::model()->findByAttributes(array(
'email'=>$_POST['email'],
'password'=>$_POST['pass']
));
$this->render("json",array("outputData"=>$user));
}
then add this to the controller as well:
public function getViewPath(){
if(Yii::app()->request->isAjaxRequest){
if(($module=$this->getModule())===null)
$module=Yii::app();
return $module->getViewPath().DIRECTORY_SEPARATOR."ajax";
}
return parent::getViewPath();
}
and in the ajax views folder add a json.php file like so
header('Content-Type: application/json');
// output data
echo json_encode($outputData);
please degug the code as I wrote it free hand. You can also set a marker in the controller like $viewPath and set it before the rendering

php function inside another function?

I created a contact form on my classified ads website.
I use the following function to get the uploader email :
public function uploadermail()
{
return $this->User_id?$this->User->email:lang('anonymous');
}
It works fine and I get the result using an echo :
<?php echo $image->uploadermail(); ?>
Then I use a function to send the mail :
public static function sendmail_anon()
{
$form = new Form('sendmail_anon');
$form->field('email', 'text', array
(
'valid_email' => true
));
$form->field('message', 'textarea', array
(
'min_length' => 25
));
if($data = $form->validate())
{
$envoi = array
(
'message' => $data['message'],
'email' => $data['email']
);
mail($data['email'], lang('account_details'), lang('email_contact', $envoi), 'From: noreply#'.$_SERVER['HTTP_HOST']);
}
return $form;
}
The problem is that this is sending the mail to the e-mail from the form field.
I would like to replace $data['email'] and insert the uploadermail instead. I tried :
mail($image->uploadermail(), lang('account_det.....
And it returns the following error :
Fatal error: Call to a member function uploadermail() on a non-object
Is it possible to do and how should I writte it exactly ?
I tried :
mail($uploadermail,....
And it doesn't returns errors, but didn't received any mail, how can I check what exactly contains $uploadermail on the browser ?
This means that $image is not an class instance. So you cannot do $image->uploadermail().
You can check it out by doing: var_dump($image);
After posting your full code I see you are accessing that function from public static function sendmail_member():
mail($image?$image->uploadermail():$data['email'], lang('account_details'), lang('email_contact', $envoi), 'From: noreply#'.$_SERVER['HTTP_HOST']);
However $image is never declared (/ instantiated) in that scope. Upon further investigation I see that there is a method called uploadermail in the correct class which I guess you are trying to access.
So to access that method you should do:
self::uploadermail()
or
$this->uploadermail()
PS
You should really try to prevent using statics. Static stuff are basically globals and they tightly couple you code and it prevent the L in SOLID programming.
You should instantinate an object for $image before usage! e.g.
$image = new ...
or
$image = x.GetImage(...

Run a method if it has not already ran with the current class?

I have a class that I am writing and I have a method that I would like to run once per initiation of the class. Normally this would go in the construct method, but I only need it to run when I call certain methods, not all.
How would you all recommend I accomplish this?
Create a private property $methodHasBeenRun which has a defualt value of FALSE, and set it to TRUE in the method. At the start of the method, do:
if ($this->methodHasBeenRun) return;
$this->methodHasBeenRun = TRUE;
You didn't specify exactly why you only want to run a given method once when certain methods are called, but I am going to make a guess that you're loading or initializing something (perhaps data that comes from a DB), and you don't need to waste cycles each time.
#DaveRandom provided a great answer that will work for sure. Here is another way you can do it:
class foo {
protected function loadOnce() {
// This will be initialied only once to NULL
static $cache = NULL;
// If the data === NULL, load it
if($cache === NULL) {
echo "loading data...\n";
$cache = array(
'key1' => 'key1 data',
'key2' => 'key2 data',
'key3' => 'key3 data'
);
}
// Return the data
return $cache;
}
// Use the data given a key
public function bar($key) {
$data = $this->loadOnce();
echo $data[$key] . "\n";
}
}
$obj = new foo();
// Notice "loading data" only prints one time
$obj->bar('key1');
$obj->bar('key2');
$obj->bar('key3');
The reason this works is that you declare your cache variable as static. There are several different ways to do this as well. You could make that a member variable of the class, etc.
I would recommend this version
class example {
function __construct($run_magic = false) {
if($run_magic == true) {
//Run your method which you want to call at initializing
}
//Your normale code
}
}
so if you do not want to run it create the class like
new example();
if you want
new example(true);

Passing multiple variables to a Gearman worker function

How do you pass two variables to the same worker function? For example, say I wished to concat two strings that I pass from the client. I saw in some example code an array being used, but I can't get it to work.
<?php
$client= new GearmanClient();
$client->addServer();
$arguments = array(
"string1" => "hey",
"string2" => "there"
);
$client->addTask("string_concat", $arguments);
$client->runTasks();
?>
This tells me it's an invalid workload however (I assume cause it's an array being passed). How should I be passing them - should I create a task for each?
Then if I can't send an array, how can I use multiple variables in the worker function. I've tried like function String_Concat($job, $job2) but then I'm not sure how I'd add them to the workload()
Here is some example code if I were able to pass arrays:
<?php
$worker= new GearmanWorker();
$worker->addServer();
$worker->addFunction("string_concat", "String_Concat");
while ($worker->work());
function String_Concat($job)
{
$arguments = $job->workload();
return $arguments["string1"] . $arguments["string2"];
}
?>
What's the best way to do this? Thanks a lot!
You should serialize it.
Something like:
$data = serialize( $array );
$client->addTask("string_concat", $data);
Then, from your worker, you could do something like...
if (is_string($data) && $data = unserialize($workload)) {
} else {
// Maybe throw Exception or something?
}

Categories