Unable to find wrapper when testing Guzzle call with PHPUnit - php

I am writing a unit test for an API that I am developing. The API is written in the Codeigniter framework, that calls another API using Guzzle. The test I am writing verifies that the API call returns the correct response.
The Test.php file contains the following code
require '/application/libraries/apiWrappers/Breathehr.php';
class BreathehrTest extends PHPUnit_Framework_TestCase {
public function testCanReturnEmployeeArray() {
$breatheHR = new Breathehr();
$employees = $breatheHR->list_employees(1);
$this->assertArrayHasKey('employees', $employees);
}
}
The method that is being tested is as follows
class Breathehr {
function __construct() {
}
public function list_employees($page)
{
$client = new GuzzleHttp\Client(
['base_uri' => 'https://xxx/',
'headers' => ['X-API-KEY' => 'xxx'],
'verify' => false]
);
$request = $client->get('employees?page='.$page);
$employees = json_decode($request->getBody(true));
$employeeData = array(
'employees' => array(),
'pagination' => array()
);
$i = 0;
foreach($employees->employees as $employee) {
if($employee->status !== 'Ex-employee') {
$employeeData['employees'][$i]['firstName'] = $employee->first_name;
$employeeData['employees'][$i]['lastName'] = $employee->last_name;
$employeeData['employees'][$i]['jobTitle'] = $employee->job_title;
if(isset($employee->line_manager)) {
$employeeData['employees'][$i]['lineManagerName'] = $employee->line_manager->first_name . ' '. $employee->line_manager->last_name;
$employeeData['employees'][$i]['lineManagerID'] = $employee->line_manager->id;
}
$employeeData['employees'][$i]['workingHours'] = $employee->full_or_part_time;
$employeeData['employees'][$i]['email'] = $employee->email;
$employeeData['employees'][$i]['workPhone'] = $employee->ddi;
$employeeData['employees'][$i]['personalMobile'] = $employee->personal_mobile;
$employeeData['employees'][$i]['homeTelephone'] = $employee->home_telephone;
$employeeData['employees'][$i]['birthday'] = $employee->dob;
$i++;
}
}
$nextLink = $request->getHeader('Link');
$nextLinkSplit = explode(',', $nextLink[0]);
$pageination = array();
foreach($nextLinkSplit as $data) {
$split = explode(';', $data);
preg_match('/"(.*?)"/', $split[1], $keyMatch);
$key = isset($keyMatch[1]) ? $keyMatch[1] : FALSE;
$number = substr($split[0], -2, 1);
$pageination[$key] = $number;
}
array_push($employeeData['pagination'], $pageination);
return $employeeData;
}
}
The API call works correctly via Postman and from a browser, but the result of running PHPUnit from the command line is the following
RuntimeException: Error creating resource: [message] fopen(): Unable
to find the wrapper "https" - did you forget to enable it when you
configured PHP?
[message] fopen(https://api.breathehr.com/v1/employees?page=1): failed
to open stream: No such file or directory
I have googled the error message and came across this SO post Unable to find the wrapper "https" - did you forget to enable it when you configured PHP?
Making these changes has made no difference. It's worth noting this is on localhost, running MAMP.
Any ideas?
Thanks

Sometime the CLI use a different php.ini than Apache, so your settings made through the WAMP menu don't apply to CLI.
Check if the correct extension are loaded launching the
command php -i | grep ssl
In the same manner you can locate the php.ini script:
php -i | grep ini
hope this help

Related

Why success is not being returned in my PHP WSDL (Zf2)

I used these two resources as launching pad for my creation of a WSDL endpoint server.
https://odan.github.io/2017/11/20/implementing-a-soap-api-with-php-7.html
https://www.youtube.com/watch?v=e_7jDqN2A-Y&t=799s
By combining these two I was able to come up with a hybrid system that works. My issue that I am trying resolve right now is getting a response back from the api.php/endpoint server.
In the odan git example, it worked to the letter. But once I made changes to the code that requires objects. I started getting errors.
PHP Notice: Trying to get property of non-object
Here is a portion of the server code.
class wenoError
{
public $response = "Sucess";
public static function authenticate($header_params)
{
if($header_params->username == 'WEX' && $header_params->password == 'WEX1') return true;
else throw new SOAPFault('Wrong user/pass combination', 601);
}
/**
* #param string $payload
* #return string $delivery
*/
public function receivePayload($payload)
{
$xml = base64_decode($payload);
$fileName = 'message-'.rand().'.xml';
$file = file_put_contents('messages/'.$fileName, $xml);
$xml2json = simplexml_load_string($xml);
$jsonOut = json_encode($xml2json);
$arrayJson = json_decode($jsonOut, TRUE);
//$seeArray = print_r($arrayJson, true);
//file_put_contents('messages/converted-'.$fileName.'.json', $arrayJson['Header']['MessageID']);
$response = "Success";
return $response;
}
}
$serverUrl = "https://localhost/WenoErrors/api.php";
$options = [
'uri' => $serverUrl,
];
$server = new Zend\Soap\Server('wsdl', $options);
if (isset($_GET['wsdl'])) {
$soapAutoDiscover = new \Zend\Soap\AutoDiscover(new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence());
$soapAutoDiscover->setBindingStyle(array('style' => 'document'));
$soapAutoDiscover->setOperationBodyStyle(array('use' => 'literal'));
$soapAutoDiscover->setClass('wenoError');
$soapAutoDiscover->setUri($serverUrl);
header("Content-Type: text/xml");
echo $soapAutoDiscover->generate()->toXml();
} else {
$soap = new \Zend\Soap\Server($serverUrl . '?wsdl');
$soap->setObject(new \Zend\Soap\Server\DocumentLiteralWrapper(new wenoError()));
$soap->handle();
}
What I don't understand is the error message of $response being a non-object. According to the PHP manual https://www.php.net/manual/en/language.oop5.properties.php
The property is set correctly at the top of the class, the property is declared and a value us set.
What went wrong?
UPDATE:
Adding the client code.
$client = new Zend\Soap\Client('https://localhost/WenoErrors/api.php?wsdl');
$delivery = $client->call('receivePayload',[['payload' => $message]]);
Dumping client yields:
C:\eRxGateway\www\apa\WenoErrors\clientapi.php:55:
object(client)[3]
public 'delivery' => null
UPDATE:
What finally worked for me was this change.
First change:
$server = new Zend\Soap\Server('wsdl', $options);
$server = new Zend\Soap\Server(null, $options);
Your code seems to work fine for me. Though, I am getting a different result then yours as below:
$client = new Zend\Soap\Client('http://localhost/test/api.php?wsdl');
$message = ' -> Hello World';
$delivery = $client->call('receivePayload',[['payload' => $message]]);
var_dump($delivery);
object(stdClass)#4 (1) {
["receivePayloadResult"]=>
string(7) "Success"
}
Step 1
Please try to remove all the '/tmp/wsdl-****' files from your /tmp directory. You seem to be on windows, so instead of /tmp it might be something else like C:\Windows\Temp. You can easily find which directory by going into your php.ini file and looking for the below directive.
soap.wsdl_cache_dir="/tmp"
Step 2
Also, for developing and testing purposes always put the below ini directive at the start of your client php file, which in your case is clientapi.php file.
ini_set("soap.wsdl_cache_enabled", 0);
You shouldn't be required to put this directive at the start of the server(api.php) file, but you can if the above still does not work for you.

How to run exec()/ linux commands using gearman extension from cakephp application?

I installed gearman extension and gearman command line tool also. I tried to reverse a string using gearman from simple php file.
Example:
$gmclient= new GearmanClient();
$gmclient->addServer();
$result = $gmclient->doNormal("reverse", "Test the reverse string");
echo "Success: $result\n";
output:
Success: gnirts esrever eht tseT
In the same way i tried to run exec('ls -l') , I am able to execute using simple php files from cakephp application from webroot directory. filepath: cakephp/app/webroot/worker.php, cakephp/app/webroot/client.php.
worker.php
<?php
$worker= new GearmanWorker();
$worker->addServer();
$worker->addFunction("exec", "executeScript");
while ($worker->work());
function executeScript($job)
{
$param = $job->workload();
$t = exec($param);
return $t;
}
?>
client.php
<?php
$client= new GearmanClient();
$client->addServer();
$cmd = 'ls -l';
print $client->do("exec", $cmd);
?>
How to implement the same type of execution using View, Controller from cakephp?
Workflow: Post data from View to Controller using ajax method and execute "exec() from gearman" , send output back to View as response of ajax POST methhod.
Why are you using exec?! That brings a huge security risk. Use DirectoryIterator instead.
Your client code should be part of the controller.
<?php
class UploadController extends AppController
{
public function directoryList()
{
$directory = '';
// Get data
if (!empty($this->data['directory']) && is_string($this->data['directory']))
{
$directory = $this->data['directory'];
}
$client= new GearmanClient();
$client->addServer("localhost",4730); // Important!!!
$result = $client->do("fileList", serialize($data));
return $result;
}
}
Then from view use requestAction.
$uploads = $this->requestAction(
array('controller' => 'upload', 'action' => 'directoryList'),
array('return')
);
Worker could look like this:
<?php
$worker= new GearmanWorker();
$worker->addServer("localhost",4730); // Important!!!
$worker->addFunction("fileList", "getFileList");
while ($worker->work());
// From Art of Web
// http://www.the-art-of-web.com/php/directory-list-spl/
function getFileList($dir)
{
// array to hold return value
$retval = array();
$dir = $job->workload();
// add trailing slash if missing
if(substr($dir, -1) != "/") $dir .= "/";
// open directory for reading
$d = new DirectoryIterator($dir) or die("getFileList: Failed opening directory $dir for reading");
foreach($d as $fileinfo) {
// skip hidden files
if($fileinfo->isDot()) continue;
$retval[] = array(
'name' => "{$dir}{$fileinfo}",
'type' => ($fileinfo->getType() == "dir") ?
"dir" : mime_content_type($fileinfo->getRealPath()),
'size' => $fileinfo->getSize(),
'lastmod' => $fileinfo->getMTime()
);
}
return $retval;
}
This is pseudo code. Do not use it in production!!! See Gearman documentation for more advance worker setup.
To actually take advantage of load distribution Gearman server should not be on localhost of course.
Your worker.php needs to be already running on a server for this to work. For testing, open up a new terminal window to the server where you want worker.php to run. Start the worker: php worker.php on the command line. (On a production server, you might want to look at supervisor to run your worker without a terminal.)
The code in client.php would go in your controller, but save the result to a variable instead of a print statement.
The fact that this would be from an AJAX call is irrelevant, it will work the same as a normal web page. When the controller executes, the gearman client code will get a response from the worker, and you can output the result to the view.

TesseractOCR with zend framework 2

I want to use TesseractOCR in zend framework 2 project, i've installed TesseractOCR and when i call recognize function from an action i get the following errors:
file_get_contents(/tmp/1999512125.txt): failed to open stream: No such file or directory in var/www/res-admin/vendor/thiagoalessio/tesseract_ocr/TesseractOCR/TesseractOCR.php on line 235
unlink(/tmp/1999512125.txt): No such file or directory in /var/www/res-admin/vendor/thiagoalessio/tesseract_ocr/TesseractOCR/TesseractOCR.php on line 248
I need to read email address from a hosted image like this one.
This is the function from where i call TesseractOCR recognize function:
public function getTextFromImage($img){
$tesseract = new TesseractOCR($img);
return $tesseract->recognize();
}
and this is the action:
public function emailAction(){
$request = $this->getRequest();
if ($request->isPost())
{
$id = $request->getPost('id');
$maj = $this->email($id);
$data = new JsonModel(array(
'success' => true,
'maj' => $maj
));
return $data;
}
}
where email is:
public function email($source){
$maj = 0;
if($source=='toutes les sources') $annonces = $this->getAnnonces();
else $annonces = $this->getAnnoncesBySource($source);
foreach($annonces as $annonce){
$annonce['email'] = $this->getTextFromImage($annonce['email_annonceur']);
$this->updateEmail($annonce);
$maj +=1;
}
return $maj;
}
Does not seems to be a zf2 related pb.
Look at your tmp file *.txt if it exists and if rights are well set.
If it does not exist check why in the source method.
If it exists, check your rights.
No more ;).

How to count number of instances in AWS PHP SDK2

Assume composer is installed and you need to setup an ec2 client.
Suppose SDK setup with recommended method using Composer. First call aws_setupthen create an ec2 client object with security credentials. Since composer has been invoked, it will automatically load required libraries.
Then use DescribeInstances to get all running instances.
I packaged the function countInstances so it can be reused. You can call DescribeInstances with
with an array to filter results which is posted at the end.
Setup as follows:
require('/PATH/TO/MY/COMPOSER/vendor/autoload.php');
function aws_setup()
{
$conf_aws = array();
$conf_aws['key'] = 'MYKEY';
$conf_aws['secret'] = 'MYSECRET';
$conf_aws['region'] = 'us-east-1';
return $conf_aws;
}
function countInstances($list)
{
$count = 0;
foreach($list['Reservations'] as $instances)
{
foreach($instances['Instances'] as $instance)
{
$count++;
}
}
return $count;
}
$config = aws_setup();
$ec2Client = \Aws\Ec2\Ec2Client::factory($config);
$list = $ec2Client->DescribeInstances();
echo "Number of running instances: " . countInstances($list);
If you want to filter your results try something like this as a parameter to DescribeInstances:
array('Filters' => array(array('Name' => 'tag-value', 'Values' => array('MY_INSTANCE_TAG'))));
The code executes without error, but I had to adapt it to post it here.
EDIT: Added list of instances to countInstances function. Otherwise it wouldn't be visible.

Channel.Connect.Failed error NetConnection.Call.BadVersion

Okay - So I've been looking all over the place to try and correct this problem - But I keep finding different answers, and frankly, it is getting terribly frustrating trying to figure this out. Lemme post some code for you to look at:
PHP Script:
public function addNewCompany(CompanyVO $item)
{
$stmt = mysqli_prepare($this->connection,
"INSERT INTO `companies` ('companyName') VALUES (?);");
$this->throwExceptionOnError();
mysqli_bind_param($stmt, 's', $item->companyName);
$this->throwExceptionOnError();
mysqli_stmt_execute($stmt);
$this->throwExceptionOnError();
$autoid = mysqli_stmt_insert_id($stmt);
mysqli_stmt_free_result($stmt);
mysqli_close($this->connection);
return $autoid;
}
Portions of the MXML Main App:
protected function companysignupsheet1_addCompanyEventHandler(event:AddCompanyEvent):void
{
companyservicero.addNewCompany({Data:event.companyData});
}
<s:RemoteObject id="companyservicero"
source="CompanyServices"
destination="addNewCompany"
endpoint = "http://localhost/PHP_RO/public/gateway.php"
result="companyservicero_resultHandler(event)"
fault="companyservicero_faultHandler(event)"/>
A Part of code from Component:
protected function button_submitNewCompany_clickHandler(event:MouseEvent):void
{
var companyData11:CompanyVO = new CompanyVO();
companyData11.companyName = textinput_NewCompanyName.text;
var eventObject:AddCompanyEvent = new AddCompanyEvent("addCompanyEvent", companyData11);
dispatchEvent(eventObject);
}
The Event:
package events
{
import flash.events.Event;
import valueObjects.CompanyVO;
public class AddCompanyEvent extends Event
{
public var companyData:CompanyVO;
public function AddCompanyEvent(type:String, companyData:CompanyVO)
{
super(type);
this.companyData = companyData;
}
}
}
If I need to post more I will be happy to do so. Also - I know it is a bit overkill to try and just send the one text value in this fashion, but there will be much, much more that will go with it when I get it working - I just was trying to focus on where the problem is. Oh - and I don't know if it helps at all...But currently I can retrieve records from the mySQL database this is attached to (although I am not doing that via the RemoteObject way) - I can also add to the same table using the old drag-and-drop (Connect to Data/Services) functionality of an exact copy of the PHP above (although with the information hard coded in (I.E. the CompanyName=testtest)).
And to finish it all off - earlier when I didn't define the datatype for the argument:
public function addNewCompany($item){.....
for addNewCompany - it DID add a record in the database, although it was blank and it would still popup an error message with the whole Channel.Connect, etc..... And now in Zend Server's logs it is saying that the data is getting transferred in a stdClass wrapper and it is needed in CompanyVO datatype.
I am sooo frustrated with this all - I've been stuck with this type of problems for about 2-3 days now and I give up! PLEASE help. Thank you so much for your time and assistance!
-CS
EDIT - MORE INFO
GATEWAY.PHP
<?php
ini_set("display_errors", 1);
$dir = dirname(__FILE__);
$webroot = $_SERVER['DOCUMENT_ROOT'];
$configfile = "$dir/amf_config.ini";
$servicesdir = $dir.'/../services';
$librarydir = $dir.'/../library';
//default zend install directory
$zenddir = $webroot.'/ZendFramework/library';
//Load ini file and locate zend directory
if (file_exists($configfile)) {
$arr = parse_ini_file($configfile, true);
if (isset($arr['zend']['webroot'])) {
$webroot = $arr['zend']['webroot'];
$zenddir = $webroot.'/ZendFramework/library';
}
if (isset($arr['zend']['zend_path'])) {
$zenddir = $arr['zend']['zend_path'];
}
if (isset($arr['zend']['library'])) {
$librarydir = $arr['zend']['library'];
}
if (isset($arr['zend']['services'])) {
$servicesdir = $arr['zend']['services'];
}
}
// Setup include path
// add zend directory, library and services to include path
set_include_path(get_include_path()
.PATH_SEPARATOR.$zenddir
.PATH_SEPARATOR.$librarydir
.PATH_SEPARATOR.$servicesdir);
// Initialize Zend Framework loader
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()->setFallbackAutoloader(true)- >suppressNotFoundWarnings(true);
// Load configuration
$default_config = new Zend_Config(array("production" => false), true);
$default_config->merge(new Zend_Config_Ini($configfile, 'zendamf'));
$default_config->setReadOnly();
$amf = $default_config->amf;
// Store configuration in the registry
Zend_Registry::set("amf-config", $amf);
// Initialize AMF Server
$server = new Zend_Amf_Server();
$server->setProduction($amf->production);
if (isset($amf->directories)) {
$dirs = $amf->directories->toArray();
foreach ($dirs as $dir) {
if ($dir == "./") {
$server->addDirectory($webroot);
} else
if (realpath("{$webroot}/{$dir}")) {
$server->addDirectory("{$webroot}/{$dir}");
} else
if (realpath($dir)) {
$server->addDirectory(realpath($dir));
}
}
}
// Initialize introspector for non-production
if (! $amf->production) {
$server->setClass('Zend_Amf_Adobe_Introspector', '',
array("config" => $default_config, "server" => $server));
$server->setClass('Zend_Amf_Adobe_DbInspector', '',
array("config" => $default_config, "server" => $server));
}
// Handle request
echo $server->handle();
AMF_CONFIG
[zend]
;set the absolute location path of webroot directory, example:
;Windows: C:\apache\www
;MAC/UNIX: /user/apache/www
webroot = "C:/Zend/Apache2/htdocs"
;set the absolute location path of zend installation directory, example:
;Windows: C:\apache\PHPFrameworks\ZendFramework\library
;MAC/UNIX: /user/apache/PHPFrameworks/ZendFramework/library
zend_path ="C:/Zend/Apache2/htdocs/.metadata/.plugins/org.zend.php.framework.resource/resources/ZendFramework-1/library"
library ="C:/Zend/Apache2/htdocs/PHP_RO/library"
services ="C:/Zend/Apache2/htdocs/PHP_RO/services"
[zendamf]
amf.production = false
amf.directories[]=PHP_RO/services
Channel.Connect.Failed error NetConnection.Call.BadVersion usually happens when PHP echoes an error or warning to the amf response. Flex gets an amf message appended with something like 'warning something went wrong on line X' and can't parse it.
Turn on the network monitor in Flash Builder and view the latest raw response. You will see the error formatted with html tags.

Categories