I'm developing a solution that i need use Amazon WebServices library. Their library use namespace in all the project and how i am a beginner in PHP development i need your help to understand better how it works.
Here is my class:
<?php
// include('AmazonSNS\vendor\aws\aws-sdk-php\src\Sdk.php');
// include('AmazonSNS\model\CustomCredentials.php');
use Aws\Sdk;
class AwsSns {
public $sns;
public $platformApplicationArn;
public function __construct(){
$sdk = new Sdk([
'version' => 'latest',
'debug' => false,
'retries' => 3,
'credentials' => [
'key' => CustomCredentials::SNS_KEY,
'secret' => CustomCredentials::SNS_SECRET
],
'Sns' => [
'region' => 'sa-east-1'
]
]);
$this->sns = $sdk->createSns();
$this->generatePlatformApplicationArn();
}
private function generatePlatformApplicationArn( ){
$result = $this->sns->createPlatformApplication( array(
// Name is required
'Name' => 'GCMPedro',
// Platform is required
'Platform' => 'GCM',
// Attributes is required
'Attributes' => array(
// Associative array of custom 'String' key names
'PlatformCredential' => "AIzaSyBYjNaE7ShuLc2y4mf53bVwszDt8XA-YTI" //__API_KEY__
),
));
$this->platformApplicationArn = $result->get('PlatformApplicationArn');
Util::generateFile('PlataformApplicationArn: '.$this->platformApplicationArn, 'a');
}
public function getEndpointArn( $token ){
$result = $this->sns->createPlatformEndpoint(array(
// PlatformApplicationArn is required
'PlatformApplicationArn' => $this->platformApplicationArn,
// Token is required
'Token' => $token,
//'CustomUserData' => 'string',
'Attributes' => array(
// Associative array of custom 'String' key names
'Enabled' => 'true'
),
));
Util::generateFile('EndpointArn: '.$result->get('EndpointArn'), 'a');
return( $result->get('EndpointArn') );
}
}
?>
1) About name space, to use it, Do have I include or not include the .php file?
Observation:
When i don't use the include, the php returns the following error message:
Fatal error: Class 'Aws\Sdk' not found in C:\Program Files
(x86)\VertrigoServ\www\AmazonSNS\extra\AwsSns.php on line 14
Sure of your attention i thank you so much.
When you haven't set up an autoloading like PSR-0 or PSR-4 (like in the common PHP frameworks is used) or something else, the neccessary file is not going to be loaded/included automatically when its being called. I guess you haven't such an autoloading set up, so you can include with the include keyword.
In the official documentation of PHP you can read all about namespaces.
Citate of the manual. 2 benefits:
In the PHP world, namespaces are designed to solve two problems that authors of libraries and applications encounter when creating re-usable code elements such as classes or functions:
Name collisions between code you create, and internal PHP classes/functions/constants or third-party classes/functions/constants.
Ability to alias (or shorten) Extra_Long_Names designed to alleviate the first problem, improving readability of source code.
Related
If I put $author='Steven King'; it works without issue, however it does not work with a post variable.
To be "clear" if I hard code the the author in the JSON it will in fact post the message to the SQS queue. This is the expected result, however if I pass the string from the Post Variable e.g. $author=$_POST['author], the message is never delivered.
$message = array(
// Associative array of custom 'String' key names
'Author' => array(
'StringValue' =>$author,
'DataType' => 'String'
),
);
Any thoughts or help on this I would be grateful.
<?php
$author =$_POST["author"];
require 'vendor/autoload.php';
use Aws\Common\Aws;
use Aws\Sqs\SqsClient;
use Aws\Exception\AwsException;
// Get the client from the builder by namespace
$client = SqsClient::factory(array(
'profile' => 'default',
'region' => 'us-west-2',
'version' => '2012-11-05'
));
$queueUrl ='https://sqs.us-west-2.amazonaws.com/blahblahblah';
$message = array(
// Associative array of custom 'String' key names
'Author' => array(
'StringValue' =>$author,
'DataType' => 'String'
),
);
var_dump($message);
$result = $client->sendMessage(array(
'QueueUrl' => $queueUrl,
'MessageBody' => 'An awesome message!',
'MessageAttributes' =>$message,
));
So the issue was caused by the credential provider, which is why it worked in the cli e.g. php posts.php and not in the browser. Because in the CLI it has the correct environment and permissions.
Note: However on AWS SDK example does not include the credential provider only a reference to 'profile' => 'default' which would be thought to grab your default credentials, however that is not the case.
Thank you apache logs!
The fix was to set the right permissions on the /.aws/credentials and ensure
that your $HOME path is set correctly.
On to the next piece. Thanks community.
I am using this library https://github.com/yiioverflow/yii2-imap
'imap' => [
'class' => 'roopz\imap\Imap',
'connection' => [
'imapPath' => '{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX',
'imapLogin' => 'abc#gmail.com',//set this value dynamically
'imapPassword' => '123',//set this value dynamically
'serverEncoding' => 'encoding', // utf-8 default.
'attachmentsDir' => 'uploads/attachments'
],
],
//Create imap class object
$mailbox = yii::$app->imap->connection;
// Read all messaged into an array:
$mailsIds = $mailbox->searchMailbox('ALL');
in controller. want to set this value with help of session in yii2.
I found alternative php-imap library here [PHP IMAP][1]
[1]: https://github.com/barbushin/php-imap. which can be easily install with composer in yii2. and can pass a dynamic value
$mailbox = new PhpImap\Mailbox('{imap.gmail.com:993/imap/ssl}INBOX', 'some#gmail.com', '*********', __DIR__);
// Read all messaged into an array:
$mailsIds = $mailbox->searchMailbox('ALL');
Passing dynamic values that depends on Yii::$app right in config will not work because you are referring to application, and it's constructed using that config (components is a part of application too) and doesn't exist at this moment. It needs to be set later, when application is initialized and Yii::$app object exists. For example, in controller or some custom component.
Using library yiioverflow/yii2-imap it can be done like this:
use Yii;
...
Yii:$app->imap->connection = [
'imapPath' => '{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX',
'imapLogin' => $imapLogin, // Set this value dynamically
'imapPassword' => $imapPassword, // Set this value dynamically
'serverEncoding' => 'encoding', // utf-8 default
'attachmentsDir' => 'uploads/attachments',
],
Then you need to call:
Yii:$app->imap->createConnection();
to properly update the config.
There is no way to set separately imapLogin or imapPassword because of the way this component is written (these properties are protected and filled from connection array). If you want to do that, you have to subclass this component and write these setters by yourself and replace used component with your custom one.
More info about application components can be found in official docs.
You can use own "service layer" (that works similar to global Yii::$app). Just create \yii\di\ServiceLocator instance:
// Init service layer.
$services = new ServiceLocator();
$services->setComponents([
'imap' => [
'class' => 'roopz\imap\Imap',
'connection' => [
'imapPath' => '{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX',
'imapLogin' => 'abc#gmail.com',//set this value dynamically
'imapPassword' => '123',//set this value dynamically
'serverEncoding' => 'encoding', // utf-8 default.
'attachmentsDir' => 'uploads/attachments'
],
],
// ...
]);
// Retrieving the defined components:
$imap = $services->get('imap');
$imap = $services->imap;
If imap component will use only your controller, you can store $services as protected/private property of this controller.
Described approach works completely similarly to usual components in Yii::$app, because application class is also ServiceLocator.
Alternatively, you can define or redefine your component using imap-instance:
// Preparing components
$defaultImapConfig = [
'connection' => [
'imapPath' => '{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX',
'imapLogin' => null,
'imapPassword' => null,
'serverEncoding' => 'encoding', // utf-8 default.
'attachmentsDir' => 'uploads/attachments'
],
];
// Init service layer.
$services = new ServiceLocator();
// Define component
$imap = new \roopz\imap\Imap(ArrayHelper::merge($defaultImapConfig, ['connection' => [
'imapLogin' => 'abc#gmail.com',
'imapPassword' => '123',
]]));
$services->set('imap', $imap);
// Redefine component with new config
$imap = new \roopz\imap\Imap(ArrayHelper::merge($defaultImapConfig, ['connection' => [
'imapLogin' => 'dfg#gmail.com',
'imapPassword' => '456',
]]));
$services->set('imap', $imap); // If component definition with the same name already exist, it will be ovewritten.
Of course, you can use similar way to redefine global components in Yii::$app, but it is bad practice. I recommend to create separate (local) service layer, that can be accessed from your controllers, models etc.
More details about work with service locators you can found here.
I am working on a project where we will be creating both subdomains as well as domains in Route53. We are hoping that there is a way to do this programmatically. The SDK for PHP documentation seems a little light, but it appears that createHostedZone can be used to create a domain or subdomain record and that changeResourceRecordSets can be used to create the DNS records necessary. Does anyone have examples of how to actually accomplish this?
Yes, this is possible using the changeResourceRecordSets call, as you already indicated. But it is a bit clumsy since you have to structure it like a batch even if you're changing/creating only one record, and even creations are changes. Here is a full example, without a credentials method:
<?php
// Include the SDK using the Composer autoloader
require 'vendor/autoload.php';
use Aws\Route53\Route53Client;
use Aws\Common\Credentials\Credentials;
$client = Route53Client::factory(array(
'credentials' => $credentials
));
$result = $client->changeResourceRecordSets(array(
// HostedZoneId is required
'HostedZoneId' => 'Z2ABCD1234EFGH',
// ChangeBatch is required
'ChangeBatch' => array(
'Comment' => 'string',
// Changes is required
'Changes' => array(
array(
// Action is required
'Action' => 'CREATE',
// ResourceRecordSet is required
'ResourceRecordSet' => array(
// Name is required
'Name' => 'myserver.mydomain.com.',
// Type is required
'Type' => 'A',
'TTL' => 600,
'ResourceRecords' => array(
array(
// Value is required
'Value' => '12.34.56.78',
),
),
),
),
),
),
));
The documentation of this method can be found here. You'll want to take very careful note of the required fields as well as the possible values for others. For instance, the name field must be a FQDN ending with a dot (.).
Also worth noting: You get no response back from the API after this call by default, i.e. there is no confirmation or transaction id. (Though it definitely gives errors back if something is wrong.) So that means that if you want your code to be bulletproof, you should write a Guzzle response handler AND you may want to wait a few seconds and then run a check that the new/changed record indeed exists.
Hope this helps!
Yes, I done using changeResourceRecordSets method.
<?php
require 'vendor/autoload.php';
use Aws\Route53\Route53Client;
use Aws\Exception\CredentialsException;
use Aws\Route53\Exception\Route53Exception;
//To build connection
try {
$client = Route53Client::factory(array(
'region' => 'string', //eg . us-east-1
'version' => 'date', // eg. latest or 2013-04-01
'credentials' => [
'key' => 'XXXXXXXXXXXXXXXXXXX', // eg. VSDFAJH6KXE7TXXXXXXXXXX
'secret' => 'XXXXXXXXXXXXXXXXXXXXXXX', //eg. XYZrnl/ejPEKyiME4dff45Pds54dfgr5XXXXXX
]
));
} catch (Exception $e) {
echo $e->getMessage();
}
/* Create sub domain */
try {
$dns = 'yourdomainname.com';
$HostedZoneId = 'XXXXXXXXXXXX'; // eg. A4Z9SD7DRE84I ( like 13 digit )
$name = 'test.yourdomainname.com.'; //eg. subdomain name you want to create
$ip = 'XX.XXXX.XX.XXX'; // aws domain Server ip address
$ttl = 300;
$recordType = 'CNAME';
$ResourceRecordsValue = array('Value' => $ip);
$client->changeResourceRecordSets([
'ChangeBatch' => [
'Changes' => [
[
'Action' => 'CREATE',
"ResourceRecordSet" => [
'Name' => $name,
'Type' => $recordType,
'TTL' => $ttl,
'ResourceRecords' => [
$ResourceRecordsValue
]
]
]
]
],
'HostedZoneId' => $HostedZoneId
]);
}
If you get any error please check into server error.log file. If you get error from SDK library then there is might PHP version not supported.
if you run this code from your local machine then you might get "SignatureDoesNotMatch" error then Make sure run this code into same (AWS)server environment.
I have been trying to configure our Module.php to use the Module Manager Listeners for configuration (i.e interfaces that are available under Zend\ModuleManager\Feature\*). Specifically, I want to be able to configure the routes of my module outside of the main module.config.php. I have not been able to find any actual examples of this.
What I have found, if I have read the documentation correctly, is that the method getRouteConfig() should merge in my routes into the array provided by getConfig()?
Module.php
class Module implements Feature\RouteProviderInterface
{
//...
public function getRouteConfig()
{
return include __DIR__ . '/config/route.config.php';
}
//...
}
/config/route.config.php
return array(
'route_manager' => array(
'router' => array (
'routes' => array(
//.. routes that were working correctly when added to module.config.php
),
),
),
);
I can see the array returned via getRouteConfig() so I know the method is being called correctly.
Perhaps I am misunderstanding the purpose of the above interface, or I have not provided the correct "key" (route_manager) for this to be merged correctly, as I'm getting 404 for my routes.
Any help would be appreciated!
I haven't done this in the way you mentioned yet, but the key route_manager is not required within the getRouteConfig() Method.
This is due to the fact that all of the get{$specificManager}Config()-Methods are called directly from their respective Manager-Classes. Therefore the initial key is not required. Using another terminology, when using getRouteConfig() you are already in the scope of route_manager. Same as when you use getServiceConfig() you're already in the scope of service_manager. However getConfig() is within the application-scope and therefore accessing configuration of application-parts, you need to address tose specificaly.
One thing to note is: the configuration of getConfig() can be cached to increase performance, whereas all the other get{$specificManager}Config() methods are not. Especially in the case of the RouteConfiguration I'd highly suggest to use the getConfig()-Method for your RouteConfig.
If you really need to separate the configuration, then I'd suggest the way that #Hendriq displayed for you.
Well I have it working but I only use the getConfig(). What is do is I use an array_merge in the getConfig().
public function getConfig()
{
return array_merge(
require_once 'path_to_config/module.config.php',
require_once 'path_to_config/routes.config.php'
);
}
My router.config.php looks then like:
return [
'router' => [
'routes' => [
// routes
]
]
];
This way I also got some other config files seperated (ACL).
Edit
Thanks to the article Understanding ZF2-Configuration, I got an idea. I think your array should not be:
return array(
'route_manager' => array(
'router' => array (
'routes' => array(
//.. routes that were working correctly when added to module.config.php
)
)
)
);
but rather be
return array(
'router' => array (
'routes' => array(
//.. routes that were working correctly when added to module.config.php
),
),
);
The getRouteConfig is similar to the other providers it is there so you're able to create some custom routes. I guess what you're trying to do is most appropiate through hendriq's method.
An example of getRouteConfigcan be found at http://zf2cheatsheet.com/
public function getRouteConfig()
{
return array(
'factories' => array(
'pageRoute' => function ($routePluginManager) {
$locator = $routePluginManager->getServiceLocator();
$params = array('defaults' => array('controller' => 'routeTest','action' => 'page','id' => 'pages'));
$route = Route\PageRoute::factory($params);
$route->setServiceManager($locator);
return $route;
},
),
);
}
In our Module\Route namespace we create the class PageRoute which implements Zend\Mvc\Http\RouteInterface and, in our specific case for the example, Zend\ServiceManager\ServiceManagerAwareInterface. Now just implement the functions of the interface... In the sample he uses Doctrine to load the pages from the database.
Finally we can add our new custom route to our module.config.php so it can be used:
'page' => array(
'type' => 'pageRoute',
),
As you can see in this last step we go back to Hendriq's solution as the intended use is not to load the routes into the router, but creating custom routes.
Hope this helps
I can't get Zend to autoload a custom form element class. I did things exactly as Marcin describes here (except that my classes start with 'Zend' and not 'my' but I'm getting this error:
Warning: include_once(Zend\Form\Element\Div.php) [function.include-once]: failed to open stream: No such file or directory
I have Zend_Form_Element_Div inside forms\elements\ and Zend_View_Helper_FormDiv inside views\helpers\
Basically, every folder in the error message is missng an 's', the right path is Zend\Forms\Elements\Div.php
I also have this in my bootstrap, though I'm not sure if it's necessary, but I'm also using this for my forms and models folder (and some others, but I don't think there's need to post them all):
<?php
$resourceLoader->addResourceTypes(array(
'model' => array(
'namespace' => 'Model',
'path' => 'models'
),
'element' => array(
'namespace' => 'Element',
'path' => 'elements'
),
'form' => array(
'namespace' => 'Form',
'path' => 'forms'
)
));
?>
(Is there actually any other way of doing this autoloading? Instead of declaring every single folder?)
Update:
Element_Div in application/forms/elements/Div.php
In my forms init() method: $this->addElementPrefixPath('Element_', APPLICATION_PATH . '/forms/elements');
Error I'm getting: Fatal error: Class 'Element_Div' not found in C:\xampplite\htdocs\code\application\forms\PostForm.php on line 63
You essentially have to tell the form where to find custom elements by using:
$form->addElementPrefixPath()
In your case, you would use - either within the form's init() or __construct() method - something like:
$this->addElementPrefixPath('Zend_Form_Element_', APPLICATION_PATH . '/elements);;
However, I have agree with #Marcin. Naming your own classes with the Zend_ pseudo-namespace is ill-advised. Either:
Decide on an application namespace and declare it in your Bootstrap when you create your $resourceLoader
Create an custom library that resides on your include path - probably at the same level as the Zend library - and put your custom stuff out there.
Let me know if you need more details on either of these suggestions and I'll fatten up the explanations a bit.
Update based on comments
Using an empty appnamespace, your call to addElementPrefixPath() now changes to:
$this->addElementPrefixPath('Element_', APPLICATION_PATH . '/elements);
And I guess you could remove the elements entry from the $resourceLoader definition in your Bootstrap since it's really not doing anything.
Update 2
I assumed that you were adding the element to the form using the shortname, something like:
$form->addElement('div', 'my_div');
In this circumstance, we need to tell the $form and its plugin registry where to find an element of type 'div'. That's why we dealt with $form->addElementPrefixPath().
However, from the error message you are reporting, it appears that you are adding your custom element to the form using something like:
$div = new Element_Div();
$form->addElement($div, 'my_div');
In this case, it is not the $form and its plugin registry that has to worry about finding/loading/instantiating the custom element; it is the $autoloader via its $resourceLoader. In that case, there is no need for the $form->addElementPrefixPath(), which is essentially a hint to the form on how to find custom elements invoked by shortname.
What we need is to configure the $resourceLoader back in Bootstrap so it knows where to find the class. Assuming you stick with empty appnamespace (so your class is named Element_Div) and you place the file in application/forms/elements/Div.php, then the $resourceLoader call is as follows:
$resourceLoader->addResourceTypes(array(
'model' => array(
'namespace' => 'Model_',
'path' => 'models'
),
'element' => array(
'namespace' => 'Element_',
'path' => 'forms/elements'
),
'form' => array(
'namespace' => 'Form_',
'path' => 'forms'
)
));
That should do it. [Famous last words, eh?]
I prefer creating forms like this:
$form->addElement(new My_Form_Element_Whatever(array(
'name' => 'my_element',
'label' => 'My element',
)));
or
$form->addElement($whatever = new My_Form_Element_Whatever(array(
'name' => 'my_element',
'label' => 'My element',
)));
$whatever->removeDecorator('Errors');
when I need to further modify the element.