Changelog
All notable changes to shabayek/laravel-sms are documented in this file.
v3.0 - 2026-10-28
This is a major release: it requires PHP 8.1, adds response codes to send() results and removes setLanguage(). Read the upgrade notes before updating from 2.x.
Removed
- Support for PHP 8.0. The package now requires PHP 8.1 or later, for the native
ResponseCodeenum. setLanguage()and thelanguageconfig value (SMS_LANGUAGE). Messages use one language, and the encoding sent to the gateway is detected from the message text.
Added
send()results have acodekey holding aShabayek\Sms\Enums\ResponseCodecase:Sent,Rejected,HttpError,InvalidResponseorDisabled. Checkcodeinstead of comparingmessagetext. A case serializes to its string value (sent,rejected,http_error,invalid_response,disabled) in JSON.- The text under
messagecomes from the newmessagesconfig array, one entry per code, so it can be changed without touching code. The defaults are the texts returned in 2.x. send()results have aproviderkey with the gateway's owncodeandmessagewhen it returned them (SMS Misr code, Victory Link result, OoredooResult, SMS EGerror.msg).- The encoding sent to SMS Misr (
language), Victory Link (SMSLang) and Ooredoo (messageType) is detected from the message text: ASCII text is sent as English/Latin, Arabic text as Arabic, and other text as Unicode on SMS Misr and Arabic elsewhere.
Changed
messageinconfig/sms.phpis one string, and the default OTP message is English:Your verification code is: {code}.- SMS EG rejections return
Message not sent successfullyundermessage. The gateway's error text, which was returned there before, is now underprovider.message. - Victory Link and Ooredoo messages in plain ASCII text are sent as English/Latin. Before, the default
arlanguage sent them as Arabic.
Fixed
- Ooredoo
balance()no longer throwsTypeError: Cannot access offset of type string on stringwhen the response contains a single credit entry.
Upgrade notes
- PHP 8.1 or later is required. Applications on PHP 8.0 stay on v2.0 until they upgrade PHP.
- Remove calls to
setLanguage(). If you published the config, replace themessagearray with one string and removelanguage; to keep sending Arabic, setSMS_MESSAGEto your Arabic text. Add themessagesarray to customize response texts. - Code that compared
send()['message']with'Message not sent successfully'or read SMS EG's error text from it should checksend()['code']andsend()['provider']['message']instead. - Custom drivers extending
Shabayek\Sms\Drivers\Driverno longer have a$languageproperty, and inherit new protectedresponse(),isUnicode()andisArabic()methods. A subclass that already defines a method with one of these names must keep a compatible signature.
v2.0 - 2026-10-28
This is a major release: it requires PHP 8.0 and changes behaviour that applications may rely on. Read the upgrade notes before updating from 1.x.
Security
verify()no longer uses loose comparison. Only non-negative integers and strings of digits are accepted, and codes are compared withhash_equals(). Booleans,null, floats, padded strings and numeric-looking strings such as1.2e5now returnfalse. Previously,verify($phone, true, $code)returnedtruefor any non-zero code.- Locally generated codes use
random_int()instead ofrand(). - The fixed development code
1234is only used in thelocalandtestingenvironments. Previously it was used in every environment not named exactlyproduction, includingstaging,prodanduat. - Transport failures no longer expose request details. Guzzle ends cURL error messages with the full request URL, which for several drivers holds the username, password, phone number, message text and otp code, so a timeout could write them to the application log. Every driver now rethrows these errors as the same exception type (
Illuminate\Http\Client\ConnectionException, or Guzzle'sRequestException/TooManyRedirectsExceptionfor other cURL errors) with a message that names only the gateway host and cURL error number, for exampleSMS gateway request to smsmisr.com failed (cURL error 28). Request details were removed from this message.
Removed
- Support for PHP 7.3 and 7.4. The package now requires PHP 8.0 or later.
Added
- Gateway requests time out. The new
timeoutandconnect_timeoutconfig values (SMS_TIMEOUT,SMS_CONNECT_TIMEOUT) default to 30 and 10 seconds; previously requests could wait indefinitely on Laravel 8. A timeout throwsIlluminate\Http\Client\ConnectionException.
Fixed
- SMS EG and SMS Misr
send()no longer report success for HTTP errors, empty or malformed bodies, or responses without the expected success value. - Victory Link and Ooredoo
send()returnsuccess: false(andsendOtp()returnsnull) for HTTP errors and for bodies that are not well-formed XML. Previously a plain-text error page threw anErrorExceptionfrom the XML parser, and an HTTP error with a success-looking body counted as sent. sendOtp()returnsnullinstead of a code when the message was not sent (SMS EG normal service, SMS Misr, Victory Link and Ooredoo). SMS Misr no longer returns a code without sending anything whenservicehas an unknown value.- SMS EG
verify()with serviceotpreturnsfalsewhen the verification request does not return HTTP 2xx, and rejects malformed codes without calling the provider. - The
Smsalias incomposer.jsonnow points to the facade classShabayek\Sms\Facade\Sms. - The Ooredoo driver can be resolved with
Sms::driver('ooredoo')or by settingSMS_CONNECTION=ooredoo. Previously the manager threwDriver [ooredoo] not supported.and the driver had to be built by hand. - The Ooredoo and null drivers no longer create dynamic properties, which are deprecated since PHP 8.2.
- Creators registered with
Sms::extend()receive the application and the connection config. Previously they receivednull. A creator can now also be registered under the connection'sdrivervalue; one registered under the connection name still takes precedence. - An undefined connection reports
SMS connection [name] is not defined.instead ofCache store [name] is not defined.
Documentation
- README examples now import the facade from
Shabayek\Sms\Facade\Sms(previously the non-existentShabayek\Sms\Facades\Sms), and the publish command names the actual providerShabayek\Sms\Providers\SmsServiceProvider. - The custom driver example is valid PHP and uses the config passed to the creator.
- Fixed the
setLanguage()example and the descriptions of the normal and otp services.
Upgrade notes
- PHP 8.0 or later is required. Applications on PHP 7.3 or 7.4 stay on the 1.x releases until they upgrade PHP.
- If you store the result of
sendOtp()and later pass it toverify(), check fornullfirst.verify()throwsActual otp is required for this servicewhen the stored value isnull. - A code stored as a string with a leading zero (
'012345') no longer matches the integer12345. Codes generated by this package never start with zero. - Servers using an environment other than
localortestingnow send random codes. Tests or QA flows that relied on1234there need another approach. - Custom drivers extending
Shabayek\Sms\Drivers\Driverinherit a new protectedisValidOtp()method thatverify()uses. A subclass that already defines a method with that name replaces this validation with its own. - Transport exceptions thrown by the drivers no longer chain the original exception, so
getPrevious()returnsnulland the Guzzle handler context is gone. GuzzleRequestExceptions carry a copy of the request without query string, headers or body, and no response. Code that parsed the old message or read these details needs updating. - Custom drivers extending
Shabayek\Sms\Drivers\Driverinherit a new protectedsendSafely()method. A subclass that already defines a method with that name must keep a compatible signature. - A creator registered with
Sms::extend()now also applies to every connection whosedrivermatches its name. If you override a built-in driver such assmsegunder its name and also have other connections using'driver' => 'smseg', those connections now use your creator too. Register the creator under a name no connection uses as itsdriverto keep the old behaviour. Ooredoo::$customer_idandSmsNull::$configare now declaredprotected. They used to be created as public dynamic properties, so code that read them from outside the class needs to keep its own copy of the config instead.- Gateway requests that take longer than 30 seconds (10 seconds to connect) now fail with a
ConnectionException. RaiseSMS_TIMEOUT/SMS_CONNECT_TIMEOUT, or set them to0to wait indefinitely as before. Published config files don't need the new keys; the defaults apply. A timed-out request may still have been accepted by the gateway, so don't resend automatically.
Known limitations
- SMS EG with service
otpreturnsnullfromsendOtp()whether or not the provider accepted the request, so the return value cannot be used to detect failures. - SMS Misr with service
otptreats any HTTP 2xx response fromvSMSas sent, because the response body format is unverified. A rejection returned inside a 200 response is not detected. - The SMS Misr driver still calls
/api/v2and/api/vSMS, which are not described in SMS Misr's current public API documentation (that documents/api/SMS/and/api/OTP/). They are kept unchanged until SMS Misr confirms their status. The success values the drivers check, SMS EG'stype: "success"and SMS Misr's1901, match the providers' documentation. - No driver confirms SMS delivery. A successful result means, at most, that the provider accepted the request.
- Connection failures throw
Illuminate\Http\Client\ConnectionException, while rejected messages returnnullorsuccess: false.