I'm using the Amazon Simple Email Service and am trying to implement it as an abstract class so that I can simply use it throughout as needed.
Problem
The problem occurs with the use, I cannot work out how to require the files and classes needed to use Ses as an abstract class without incurring errors.
require 'lib/aws/aws-autoloader.php';
use Aws\Common\Enum\Region;
use Aws\Ses\SesClient;
abstract class simpleemail {
function sendSesEmail($to, $subject, $body, $bodyHtml){
try {
$client = SesClient::factory(array(
'key' => "",
'secret' => "",
'region' => Region::US_EAST_1
));
$send = $client->sendEmail(array(
'Source' => 'Name <no-reply#contact.com>',
'Destination' => array('ToAddresses' => array($to)),
'Message' => array('Subject' => array('Data' => $subject), 'Body' => array('Html' => array('Data' => $bodyHtml)))));
return true;
}
catch(Exception $e){
echo $e->getMessage();
return false;
}
}
}
Error Messages
Fatal error: Class 'Aes\Ses\SesClient' not found in ....
I have tried changing the use to require but then get:
require 'lib/aws/Aws/Common/Enum/Region.php';
require 'lib/aws/Aws/Ses/SesClient.php';
Fatal error: 'SesClient' not found in ...
Solution?
How can I use/require the files I need to get this working inside an abstract class?
This doesn't work:
abstract class simpleemail
{
public function sendSesEmail()
{
use Aws\Common\Enum\Region;
use Aws\Ses\SesClient;
//...
}
}
use statements are, basically, imports, that are processed at compile-time, so they can't be scoped. They have to move to the outer scope (outside of the class).
If you want to scope them, you'll have to either, manually require them, or use class_alias.
Check my answer to this question for more details. Even more details can, as ever, be found on php.net
Side-notes:
Please, follow the coding standards as described by PHP-FIG. They're not official, but Zend, Symfony... all major players, in fact, subscribe to them.
Please get in the habbit of always specifying the accessmodifiers (public, protected and private)
When creating instances like you do $client = SesClient::factory, assign them to a property, to only create the instance once. At the moment, each method call creates the same instance over and over again. That's bad
When using properties: include them in the class definition!
You're calling sendEmail on an instance, and assign the return value to $send. You don't check the return value, nor do you return it. Either ignore the return value, or return it, so it can be checked!
Never use require, use require_once if you have to. Using require can cause errors when executing the same block of code twice: redeclaring functions/classes. If time is of the essence, you could opt for require (as require_once causes more overhead), but you have to know what you're doing.
don't require an autoloader, use spl_autoloader_register
Remember: Abstract classes can't be instantiated, only their children... children can also override the methods declared in the abstract class. Declare critical methods as final
So, the answer:
use Aws\Common\Enum\Region;
use Aws\Ses\SesClient;
abstract class simpleemail
{
protected $client = null;
final public function sendSesEmail()
{
$client = $this->getClient();//lazy-loads the client instance
return $client->sendEmail(/* ... */);//return the result
}
//lazy-loader
protected function getClient()
{
if ($this->client === null)
{
$this->client = SesClient::factory(array(
'key' => "",
'secret' => "",
'region' => Region::US_EAST_1
));
}
return $this->client;
}
}
Related
I'm new to Mockery. I'm trying to figure it out with the GitHub API by using a Laravel Package as a wrapper. How can I mock GitHub::repo()->show('symfony', 'demo'); without hiting the actual API? Is there something weird with Facades? I'm getting an error here:
In EvalLoader.php(34) : eval()'d code line 993:
Cannot redeclare Mockery_0_GrahamCampbell_GitHub_Facades_GitHub::shouldReceive()
Code:
use Mockery;
use Tests\TestCase;
use GrahamCampbell\GitHub\Facades\GitHub;
public function testExample()
{
$this->mockGitHubWith([
'id' => 1,
'name' => 'demo',
'full_name' => 'symfony/demo',
]);
$repo = GitHub::repo()->show('symfony', 'demo');
dd($repo);
}
protected function mockGitHubWith($expectations)
{
$github = Mockery::mock(GitHub::class, $expectations);
$github->shouldReceive('api')->andReturn($github);
app()->instance(GitHub::class, $github);
}
also tried:
use GrahamCampbell\GitHub\Facades\GitHub;
public function testExample()
{
Github::shouldReceive('api')->once()->andReturn(['id' => 1]);
$repo = Github::repo()->show('symfony', 'demo');
dd($repo);
}
Returns: Mockery\Exception\BadMethodCallException: Method Mockery_0::repo() does not exist on this mock object
Just to confirm, if I remove the GitHub::shouldReceive... line, it's successful but actually hits the GitHub API.
With the last example you are almost there. Remember you are trying to mock a two step call, first a static method and a call to an instance, therefor the mock should emulate that.
Create the repository that the repo() call will return. Using standard mockery functionality.
use Github\Api\Repo;
$repoMock = Mockery::mock(Repo::class);
$repoMock->shouldReceive('show')->with('symfony', 'demo')->once()->andReturn(['id' => 1]);
Now you can set the return type of the repo call through Laravels approach to mock facades.
Github::shouldReceive('repo')->once()->andReturn($repoMock);
When you call your code repo will return the repo mock, that expects a show call with the parameters symfony and demo.
$repo = Github::repo()->show('symfony', 'demo');
I have a class that calls a WordPress global function 'add_menu_page' This function receives a number of arguments, one of which is a callback that in this case creates a new instance of a class 'CreateAdminMountPoint'.
In my PHPUnit test I have mocked the admin_menu_page function and its expected 'with' arguments.
My problem lies with the callback class, in my test I have set the assertion to:
array(new CreateAdminMountPoint(array('slug' => 'brands')), 'addMountPoint')
This is obviously bad practice as my test now relies on a separate class rather than testing in isolation.
How can I change my test so it does not depend on CreateAdminMountPoint?
The Class
class CreateAdminMenus {
public function addMenuPages($tables) {
foreach( $tables as $menu ) {
add_menu_page(
$menu['title'],
$menu['title'],
$menu['wp-menu']['capability'],
'pup/' . $menu['title'] . '/edit.php',
[new CreateAdminMountPoint($menu), 'addMountPoint'], // Trying to test this without being dependant on CreatAdminMountPoint
$menu['wp-menu']['icon']
);
}
}
}
Test Case
class CreateAdminMenusTest extends LSMTestCase {
protected function setup() {
$this->tables = array(
'brands' =>
array(
'title' => 'title',
'slug' => 'brands',
'wp-menu' =>
array(
'capability' => 'capability',
'icon' => 'icon',
),
),
);
}
public function testMenusAreCreated() {
$adminMenus = new CreateAdminMenus;
$mock = $this->mockGlobalFunction('add_menu_page');
$mock->expects($this->exactly(1))
->method('add_menu_page')
->with(
'title',
'title',
'capability',
'pup/title/edit.php',
array(new CreateAdminMountPoint(array('slug' => 'brands')), 'addMountPoint'),
'icon'
);
$adminMenus->addMenuPages($this->tables);
}
}
The way your code is written now it's impossible to test it in any other way then you do now. Which actually isn't as bad as you might think. If I would see a test like this in a pull request I would approve it.
That being said, I can imagine that you do want to test it more thoroughly. Your best solution would be to use a CreateAdminMountPointBuilder class. So instead of creating the new object inline. You use your new class to build it. This way you can mock that class in your test and assert that the mocked response from the builder is put into your function call.
You can now also reuse the builder if you want to use it anywhere else in your project.
Hope it makes sense, otherwise I could add some sample code. Let me know!
I'm trying to consume a remote webservice but it doesnt work and I'm new to drupal.
below is my code:
namespace Drupal\mymodule\mymoduleAPI;
class RemoteConnection {
public function create() {
$default = array(
// We shall only enable TRACING & EXCEPTION for dev
'trace' => 1,
'exceptions' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
);
$myServerEndPointUrl = "wsdl server address";
return new SoapClient($myServerEndPointUrl , $default);
}
}
This gives me the following error
Fatal error: Class 'Drupal\mymodule\mymoduleapi\SoapClient' not found
in C:\wamp\www\drupalnew\modules\
So am I doing something wrong? I already checked the Soap extension and tested it outside drupal and found it to work fine.
Thanks
SoapClient is in the global namespace, so when accessing it from within your own namespace, use '\' in front of the class name, like:
return new \SoapClient($myServerEndPointUrl , $default);
Further info Here
You can add a use command after your namespace:
use \SoapClient;
e.g.
namespace Drupal\module_name\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use SoapClient;
In my controller function I am using a require statement to include a file:
require app_path().'/plivo/plivo.php';
After this statement, I try to redirect from this controller using the following statement:
return Redirect::back()->with('success', 'Note added successfully');
However, this gives me the following error:
Call to undefined method Redirect::back()
How can I redirect from this function?
This is my full code:
public function sendSMS(){
require app_path().'/plivo/plivo.php';
$auth_id = "XXXXXXXXXXXX";
$auth_token = "XXXXXXXXXXXXXXXXXXXXX";
$p = new \RestAPI($auth_id, $auth_token);
$params = array(
'src' => '1XX7XX0',
'dst' => '91XXXXXXXXX7',
'text' => 'Test SMS',
'method' => 'POST'
);
$response = $p->send_message($params);
return Redirect::back()->with('success', 'Note added successfully');
}
This answer assumes that plivo.php is from this git repo.
The issue is that the plivo.php library defines a Redirect class in the global namespace. Because of this, Laravel does not register the global Redirect alias to point to the Illuminate\Support\Facades\Redirect facade.
So, in your final line return Redirect::back()->with(...);, the Redirect class being used is the class defined in the plivo.php library, not Laravel's Illuminate\Support\Facades\Redirect class.
The quickest fix would be to change your line to:
return Illuminate\Support\Facades\Redirect::back()->with('success', 'Note added successfully');
Another option would be to inject Laravel's redirector into your controller, and use that instead of using the facade:
class MyController extends BaseController {
public function __construct(\Illuminate\Routing\Redirector $redirector) {
$this->redirector = $redirector;
}
public function sendSMS() {
require app_path().'/plivo/plivo.php';
//
return $this->redirector->back()->with('success', 'Note added successfully');
}
}
A third option would be to update your code to use the plivo composer package, which has a namespace. The updates have been done in the dev branch of the repo, which you can find here. If you did this, you would get rid of your require statement and use the namespaced plivo classes.
I have factories for Doctrine in Module.php method getServiceConfig() :
public function getServiceConfig()
{
return array(
'factories' => array(
'doctrine.entitymanager.orm_cst' => new \DoctrineORMModule\Service\EntityManagerFactory('orm_cst'),
'doctrine.connection.orm_cst' => function ($sm) {
$config = $sm->get('config');
return new \DoctrineORMModule\Service\DBALConnectionFactory('doctrine.entitymanager.' . $config['connection']);
},
'doctrine.configuration.orm_cst' => new \DoctrineORMModule\Service\ConfigurationFactory('orm_cst'),
'doctrine.driver.orm_cst' => new \DoctrineModule\Service\DriverFactory('orm_cst'),
'doctrine.eventmanager.orm_cst' => new \DoctrineModule\Service\EventManagerFactory('orm_cst'),
),
);
}
I'm trying to get connection value from config and I'm getting the following error:
Catchable fatal error: Object of class DoctrineORMModule\Service\DBALConnectionFactory could not be converted to string in W:\domains\zf\vendor\doctrine\orm\lib\Doctrine\ORM\EntityManager.php on line 939
It's ok if I'm not using function as array value:
'doctrine.connection.orm_cst' => new \DoctrineORMModule\Service\DBALConnectionFactory('orm_cst'),
What am I doing wrong? Please help.
The doctrine.connection should return a configured \Doctrine\DBAL\Connection.
At the moment, you are incorrectly returning the actual ZF2 factory instance (\DoctrineORMModule\Service\DBALConnectionFactory) rather than using it to create the connection.
If you wish to keep the closure, you can just manually call the createService() method and it should work.
'doctrine.connection.orm_cst' => function ($sm) {
$config = $sm->get('config');
$key = 'doctrine.entitymanager.' . $config['connection'];
$factory = new DBALConnectionFactory($key);
// Manually call the createService method and the factory will then
// return the Connection instance
return $factory->createService($sm);
},
The other (preferred) option would be to extend the default Doctrine factory and define the configuration key within the factory itself, this way you have everything needed to create the connection in one place (which is really the idea behind using a factory).
If you are not using the above closure, it is also worth noting that by creating your service factories with new you are recreating every service factory on every request - This will have an unnecessary negative performance impact. The solution again would be to extend and/or wrap the Doctrine factories in your own custom factory and just use a string to reference them. The service manager will then be able to lazy load them.
'doctrine.connection.orm_cst' => 'MyModule\Factory\CstConnectionFactory',