This content originally appeared on Twilio Blog and was authored by Matthew Setter
In my previous tutorial on how to send an SMS with PHP’s Mezzio framework, we covered sending a text message in PHP. In this tutorial, we’ll expand on the application that we built so that it can also reply to an SMS when it is received.
Prerequisites
To follow along with this tutorial you should have the following:
- A Twilio account
- Git
- Composer globally installed
- Basic knowledge of PHP and the Mezzio framework
To get started, clone version 1.0.0 of the repository that we created in the previous tutorial. Alternatively, follow the previous tutorial to create what we achieved.
Note: If you cloned the repository, make sure that you add your Twilio Account SID, Auth Token, and phone number to the .env file in the root directory of the project. You will also need to run composer install
to install existing dependencies for the project.
Regardless of the approach that you choose, when you’re ready, let’s continue. As we achieved a lot in the previous article, this article is going to be comparatively short. That said, it still allows us to learn more about both Mezzio and Twilio’s SMS API.
Here’s a broad overview of how we’re going to extend our small Mezzio application. We’ll create a new endpoint that will receive an HTTP POST request from Twilio, sent after an SMS is sent to our Twilio phone number. Essentially, it’s a webhook.
After the request is received from Twilio, our application will send a response back telling Twilio to send a reply SMS to the sender. To do this, the request’s body will contain TwiML (the Twilio Markup Language).
If this is your first time hearing about TwiML, to quote the Twilio docs:
It is “an XML-based language that instructs Twilio on how to handle various events such as incoming and outgoing calls, SMS messages, and MMS messages.” Here’s an example of the response.
<Response>
<Message>
Thanks for sending your SMS. We'll be in touch with you shortly.
</Message>
</Response>
Gladly, implementing that functionality won’t take too much work, yet will still be a fun journey of discovery all the same. Here are the steps that we’ll follow.
- Add a new method to TwilioService to generate the response’s content
- Create a new Handler class (and instantiate a factory class) to receive the request from Twilio and send the generated response
- Register a new route in the routing table so that the route can be used
After that’s done, we’ll then test that everything works by sending an SMS and seeing the response returned. If you’re keen, then let’s get started.
Update the TwilioService
The first thing we need to do is to add a new method, replyToSMS
, to src/App/src/Service/TwilioService.php
, which you can see above. If you remember from the previous article, TwilioService is the class that contains the core logic for interacting with Twilio’s API.
public function replyToSMS(string $replyMessage): MessagingResponse
{
$message = new MessagingResponse();
$message->message($replyMessage);
return $message;
}
The method instantiates a new MessagingResponse
object, sets the message to send in the response using the method’s $replyMessage
parameter, and returns the newly instantiated object.
Note: make sure you add the required use statement to the top of the class: use Twilio\TwiML\MessagingResponse;
Create the SMS Reply Handler
Next, we need to create a new Handler class to receive the request and send back the response. To do that, we’ll use one of the pre-defined Composer scripts that comes with Mezzio projects: composer mezzio
. This runs Mezzio’s command line tooling, akin to Laravel’s Artisan Console, saving us the time and effort of generating most of the new functionality.
Run the command below in the root directory of the project, and then I’ll explain what it does.
composer mezzio handler:create 'App\Handler\SMSReplyHandler'
The command does three things:
- Creates a new request handler class named
SMSReplyHandler
insrc/App/src/Handler
- Creates a factory class to instantiate the request handler, named
SMSReplyHandlerFactory
, also insrc/App/src/Handler
- Registers the new class in the application’s DI (Dependency Injection) container configuration (in
config/autoload/mezzio-tooling-factories.global.php
).
It doesn’t register a route in the application’s routing table, but we’ll do that later on. Let’s start stepping through SMSReplyHandler.php
.
Create the request handler
<?php
declare(strict_types=1);
namespace App\Handler;
use App\Service\TwilioService;
use Laminas\Diactoros\Response\XmlResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
It starts off with the namespace declaration and imports the required classes into that namespace.
class SMSReplyHandler implements RequestHandlerInterface
{
const REPLY_MESSAGE = "Thanks for sending your SMS. We'll be in touch with you shortly.";
private TwilioService $twilioService;
public function __construct(TwilioService $twilioService)
{
$this->twilioService = $twilioService;
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
$messageResponse = $this
->twilioService
->replyToSMS(self::REPLY_MESSAGE);
return new XmlResponse($messageResponse->__toString());
}
}
After that, it initializes a class constant containing the message to be sent in the response and a TwilioService
object, which is initialized in the class constructor.
In the handle method, as you’d expect, the core of the class’ work happens. In that method, the new replyToSMS
method is called, passing in the class constant, initializing a new variable $messageResponse
, with the returned Twilio\TwiML\MessagingResponse
object. It then finishes up initializing and returning a new XmlResponse object with the result of calling $messageResponse
’s __toString
method.
XmlResponse
classes, as the name implies, provide a simple way of sending XML responses. This will help us prepare our response in the format that the Twilio SMS API expects to receive. They:
- Set the string or
Stream
object provided in the first constructor argument as the response’s body. This is why we called the$messageResponse
’s__toString
method. It’s less effort and overhead than instantiating a newStream
object. - Set the response code to 200, unless otherwise specified
- Set the
content-type
header toapplication/xml; charset=utf-8
Create the request handler’s initialiser
Now that we’ve fleshed out the request handler, we have to refactor the generated factory class (src/App/src/Handler/SMSReplyHandlerFactory.php
). Most of the class remains as it was, but we’re going to refactor the __invoke
magic method, as you can see in the following example.
<?php
declare(strict_types=1);
namespace App\Handler;
use App\Service\TwilioService;
use Laminas\ServiceManager\Exception\ServiceNotFoundException;
use Psr\Container\ContainerInterface;
use Psr\Http\Server\RequestHandlerInterface;
class SMSReplyHandlerFactory
{
public function __invoke(ContainerInterface $container): RequestHandlerInterface
{
if (!$container->has(TwilioService::class)) {
throw new ServiceNotFoundException(
'Twilio service not found.'
);
}
$twilioService = $container->get(TwilioService::class);
return new SMSReplyHandler($twilioService);
}
}
Here, we’re initializing a new variable, $twilioService
, by retrieving the TwilioService
object from the DI container, if it’s registered in the container. If it isn’t registered, we throw a ServiceNotFoundException
. We shouldn’t encounter this exception, as the service was registered in the previous tutorial. The new $twilioService
is then used to initialize the request handler, before it’s returned.
Update the Routing Configuration
Now that the core functionality has been created, we need to update the routing table so that the request handler can be accessed. To do that, update config/routes.php
by adding the following code to the list of available routes.
$app->post(
'/sms/reply',
App\Handler\SMSReplyHandler::class,
'sms.reply'
);
Testing the Changes
At this point, the code’s ready to go, so let’s have some fun and see it in action! To do that, we first need to launch the application locally. Using Composer, run the following command in your terminal (or command line):
composer serve
Second, we need to start an ngrok tunnel so that Twilio can reach your development machine. To do that, run the command below, replacing <Your Twilio Phone Number>
with your Twilio phone number (and updating the URI, if necessary).
Note: The Twilio CLI will need to be installed in order to complete these steps.
twilio phone-numbers:update "<Your Twilio Phone Number>" \
--sms-url="http://localhost:8080/sms/reply"
» WARNING: Detected localhost URL.
» For convenience, we will automatically create an encrypted tunnel using the 3rd-party service https://ngrok.io
» While running, this will expose your computer to the internet.
» Please exit this command after testing.
? Do you want to proceed? Yes
twilio-cli configuration saved to "/Users/bigsetts/.twilio-cli/config.json"
If this is your first time running the command, you’ll likely see output similar to the below output to the console, because ngrok needs to be installed before it can be used.
» Installing ngrok ...
warning ngrok > request-promise-native@1.0.9: request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142
warning ngrok > request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
warning ngrok > request > har-validator@5.1.5: this library is no longer supported
SID Result SMS URL
PNb5375d1b4518e2ffe27c39bebe6e046e Success https://f39eba2cbbb5.ngrok.io/sms/reply
ngrok is running. Open http://127.0.0.1:4040 to view tunnel activity.
Press CTRL-C to exit.
Send an SMS and receive a reply
With those two steps completed, send an SMS to your Twilio phone number and wait a few seconds for a reply to be sent. Here’s what it looked like on my iPhone. Assuming that everything worked, you’re done.
Debugging
Now, it’s always possible that something didn’t work, whether that’s because of a missing class, configuration, or some other issue. Well, if it does, then it’s handy to know how to debug the problem from Twilio’s end after you’ve done everything you can in your local development environment.
To do that, from the Twilio Console navigate to the Programmable Messaging Dashboard: (All Products & Services → Programmable Messaging).
There, you’ll see two charts: “Messages” and “Errors & Warnings”. In Messages, you can see how many SMS’ have been received by Twilio, and how many have been sent. That will give you a broad indication of where the problem might be. Errors & Warnings shows you all the issues that were encountered and allows you to drill-down deeper and investigate further.
To do that, either click on one of the events or click "View all errors and warnings". In an event, you can see a range of properties, including the message, timestamp, issue description, and the request inspector. There, you can find all that you need to help you debug issues that you encounter.
If you encounter an HTTP 500 error, check whether development mode is enabled, by running the following command in the terminal:
compose development-status
It should echo the following to the console:
> laminas-development-mode status
Development mode is ENABLED
If it doesn’t, then run the following command to enable development mode:
compose development-enable
If development mode was disabled, then the application’s configuration was cached, so the changes that we made were not used. By enabling development mode, the cache is no longer used, so our new changes will take effect.
In conclusion
And that’s how to extend our Mezzio-based application so that it sends an SMS to people when they send an SMS to our Twilio phone number. It was a relatively short tutorial but still provided us the opportunity to learn more about Twilio’s SMS API and PHP’s Mezzio framework.
I hope that you’ve learned something and will continue exploring both the API and framework as and when you are able. If you have any questions, feel free to ask me on Twitter. I’m @settermjd.
Matthew Setter is a PHP Editor in the Twilio Voices team and — naturally — a PHP developer. He’s also the author of Mezzio Essentials. When he’s not writing PHP code, he’s editing great PHP articles here at Twilio.
This content originally appeared on Twilio Blog and was authored by Matthew Setter
Matthew Setter | Sciencx (2021-02-22T22:43:07+00:00) How to Reply to an SMS with PHP’s Mezzio Framework. Retrieved from https://www.scien.cx/2021/02/22/how-to-reply-to-an-sms-with-phps-mezzio-framework/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.