PHP: Load a class in a class - php

I have a class which is meant to "load" an another class, however I haven't been able to get it to work.
Error Message
Fatal error: Call to undefined method stdClass::echoString() in C:\Program Files (x86)\EasyPHP-DevServer-14.1VC11\data\localweb\classes\example.php on line 5
Code
My code is broken up into three main sections:
api.php - the class to load the other classes.
API/exampleExternalAPI.php - (multiple files) the classes that api.php loads
example.php - the file that uses the main class (api.php)
If it helps these files can be downloaded from my dropbox
api.php
<?php
/* Config */
define('pathToAPIs','API/');
/* Autoload Function */
spl_autoload_register(function($className){
$namespace=str_replace("\\","/",__NAMESPACE__);
$className=str_replace("\\","/",$className);
$class=pathToAPIs.(empty($namespace)?"":$namespace."/")."{$className}.php";
include_once($class);
});
class api {
private $listOfAPIs;
public $APIs;
public function __construct($setAPI = null){
$this->updateListOfAPIs();
if (isset($setAPI)){
return $this->setAPI($setAPI);
}
}
public function setAPIs($setAPIs){
$this->APIs = null; // clears a previous call to this method
if (!is_array($setAPIs)){ // if not an array
$setAPIs = array($setAPIs); // make array
}
foreach ($setAPIs as $setAPIType){
if(in_array($setAPIType,$this->listOfAPIs)){
$array[$setAPIType] = new $setAPIType;
}
}
$this->APIs = json_decode(json_encode($array), FALSE); // convert array of required api objects to an object
return $this->APIs;
}
public function getListOfAPIs($update = false){
if ($update){
$this->updateListOfAPIs();
}
return $this->listOfAPIs;
}
private function updateListOfAPIs(){
$this->listOfAPIs = null; // clears a previous call to this method
$it = new FilesystemIterator(pathToAPIs);
foreach ($it as $fileinfo){
$filename = pathinfo($fileinfo->getFilename(), PATHINFO_FILENAME); // removes extension
$this->listOfAPIs[]= $filename;
}
}
public function __call($method,$args){
}
}
API/exampleExternalAPI.php
<?php
class exampleExternalAPI {
public function echoString($string){
echo $string;
}
}
example.php
<?php
require_once 'api.php';
$api = new api();
$api->setAPIs('exampleExternalAPI');
$api->APIs->exampleExternalAPI->echoString('string');
Background Info
(may give some insight to my madness)
I'm working on a project where I need to connect to lots of external APIs.
So I decided to creating a class to look after all my communications with external APIs ( not sure if best way - new to Object Oriented Programming).

I'm not entirely sure what problem you're trying to solve, but if your APIs is a simple stdClass instance it should work as expected:
public function setAPIs($setAPIs)
{
$this->APIs = new stdClass; // clears a previous call to this method
if (!is_array($setAPIs)) { // if not an array
$setAPIs = array($setAPIs); // make array
}
foreach ($setAPIs as $setAPIType) {
if (in_array($setAPIType, $this->listOfAPIs)) {
$this->APIs->{$setAPIType} = new $setAPIType;
}
}
return $this->APIs;
}

Related

PHP save Class in other File and call it from another class

Im just tryed to work in PHP how in java, and I got a Problem with save Objects in other php File.
In java will looks like this:
package xxxx
public class Data {
public static Writer writer = null;
}
To save class there?
Data.writer = new Writer(file);
To get it from other Class or use it?
Data.writer.setLine("blablabla");
But when I tryed do the same in PHP there comes Problem then the objects will be not saved in the other file.
My Codes:
ClassErrorLogWriter.php
class ErrorLog {
private $logfile = null;
private $writer = null;
public function __construct($file){
if(file_exists($file)){
$this->logfile = $file;
}else{
$this->logfile = $file;
$this->writer = fopen($this->logfile, "a") or die("Unable to open file! ".$this->logfile);
fwrite($this->writer, "New file was createt\n");
fclose($this->writer);
}
$this->loadWriter();
}
private function loadWriter(){
$this->writer = fopen($this->logfile, "a");
}
public function write($text){
$date = $this->getDataFormat();
fwrite($this->writer, $date.": ".$text."\n");
}
public function writeError($text){
$date = $this->getDataFormat();
fwrite($this->writer, $date.": ERROR: ".$text."\n");
}
private function getDataFormat(){
$date = "".date("d-m-Y_H:i:s A")."";
return $date;
}
public function close(){
fclose($this->writer);
}
}
data.php
$AuctualyPath = null;
$CurrentSqlConnection = null;
$CurrentLogWriter = null;
$DefaultPageFile = "defaultPage/defaultPage.php";
staticFunctions.php
function createFile($file){
$writer = fopen($file, "w");
fclose($writer);
}
function checkFiles(){
if(!file_exists("config.cfg")){
createFile("config.cfg");
$CurrentLogWriter->write("New File config.cfg was createt"); <- Error Here, $CurrentLogWriter is NULL
}
}
Application File (Start) must be called a specific function startSite()
function startSite(){
include "datas/php/ClassErrorLogWriter.php";
include "datas/php/data.php";
include "staticFunctions.php";
echo $DefaultPageFile;
$CurrentLogWriter = new ErrorLog(__DIR__."/logs/logFile.log");
$CurrentLogWriter->writer("test");
checkFiles();
}
After I Included Files I checked first if Data.php Variables are valid with printing $DefaultPageFile, then I defined new Logwriter in the Variable $CurrentLogWriter from Data File tryed to write it (it works). After I called Function from other php Class checkFiles() from staticFunctions.php. in the function checkFiles() by writing the Log It returns me Error then $CurrentLogWriter is NULL.
When I define Data.php as static Class so I must always define this Class and saving from date will not work. Can you help me by this Problem?
The w3school documentation for PHP Variables clearly states:
A variable declared outside a function has a GLOBAL SCOPE and can only be accessed outside a function
When using the include statement, your included file is simply copied as-is into the current file. Therefore, your startfile() is the same as
function startSite(){
include "datas/php/ClassErrorLogWriter.php";
$AuctualyPath = null;
$CurrentSqlConnection = null;
$CurrentLogWriter = null;
$DefaultPageFile = "defaultPage/defaultPage.php";
function createFile($file){
$writer = fopen($file, "w");
fclose($writer);
}
function checkFiles(){
if(!file_exists("config.cfg")){
createFile("config.cfg");
$CurrentLogWriter->write("New File config.cfg was createt"); // <- Error
// Here, $CurrentLogWriter is NULL
}
}
echo $DefaultPageFile;
$CurrentLogWriter = new ErrorLog(__DIR__."/logs/logFile.log");
$CurrentLogWriter->writer("test");
checkFiles();
}
Notice that your $CurrentLogWriter was initialized outside checkFiles() and can only be used within startSite() which is relatively global to checkFile().
A quick solution to this will be to pass $CurrentLogWriter as a parameter to checkFile() like this:
function checkFiles($CurrentLogWriter){
if(!file_exists("config.cfg")){
createFile("config.cfg");
$CurrentLogWriter->write("New File config.cfg was createt"); <- Error
// Here, $CurrentLogWriter is NULL
}
}
OR
You can optionally passed $CurrentLogWriter as a reference parameter, so that you can reuse it:
function checkFiles(&$CurrentLogWriter){
if(!file_exists("config.cfg")){
createFile("config.cfg");
$CurrentLogWriter->write("New File config.cfg was createt"); <- Error
// Here, $CurrentLogWriter is NULL
}
}
Then, call checkFile() as you did, but this time with the parameter
checkFiles($CurrentLogWriter);
How about a simple file_put_contents ?
file_put_contents manual
it looks also you get messed up with fopen and fclose.
i would just collect all lines for such a class file and pump it out in a whole.
It is simple fast and error prune. If file_put_contens not work i would have a look also at the file write permissions.
i think you were confused in File Management (fopen and fclose).
in the checkFiles() function the $CurrentLogWriter variable is a local variable that is not defined anywhere,
you either have to pass that variable as an argument like
function checkFiles($CurrentLogWriter){
if(!file_exists("config.cfg")){
createFile("config.cfg");
$CurrentLogWriter->write("New File config.cfg was createt");
}
}
or define it as a global like so
function checkFiles(){
global $CurrentLogWriter;
if(!file_exists("config.cfg")){
createFile("config.cfg");
$CurrentLogWriter->write("New File config.cfg was createt");
}
}
and of course also define the global in the function where the vairable is created
function startSite(){
global $CurrentLogWriter;
...
$CurrentLogWriter = new ErrorLog(__DIR__."/logs/logFile.log");
...
}

Soap server not working in Laravel 5.2

I'm trying to create a soap server in laravel 5.2. This is my code:
Content of SoapController.php:
<?php namespace Giant\Http\Controllers;
class SoapController extends Controller {
public function __construct() {
parent::__construct();
ini_set('soap.wsdl_cache_enabled', 0);
ini_set('soap.wsdl_cache_ttl', 0);
ini_set('default_socket_timeout', 300);
ini_set('max_execution_time', 0);
}
public function server() {
$location = url('server'); // http://payment.dev/server
$namespace = $location;
$class = "\\Giant\\Http\\Controllers\\HelloWorld";
$wsdl = new \WSDL\WSDLCreator($class, $location);
$wsdl->setNamespace($namespace);
if (isset($_GET['wsdl'])) {
$wsdl->renderWSDL();
exit;
}
$wsdl->renderWSDLService();
$wsdlUrl = url('wsdl/server.wsdl');
$server = new \SoapServer(
url('server?wsdl'),
array(
'exceptions' => 1,
'trace' => 1,
)
);
$server->setClass($class);
$server->handle();
exit;
}
public function client() {
$wsdl = url('server?wsdl');
$client = new \SoapClient($wsdl);
try {
$res = $client->hello('world');
dd($res);
} catch (\Exception $ex) {
dd($ex);
}
}
}
class HelloWorld {
/**
* #WebMethod
* #desc Hello Web-Service
* #param string $name
* #return string $helloMessage
*/
public function hello($name) {
return "hello {$name}";
}
}
My wsdl file is: wsdl
And my routes:
Route::any('/server', 'SoapController#server');
Route::any('/client', 'SoapController#client');
And the result I get:
Internal Server Error
:(
I use piotrooo/wsdl-creator to generate wsdl. (There is no problem with that, It is working in laravel 4.2). And I have also tried nusoap and php2wsdl libraries.
My SoapClient is working well. Because it can get service from other soap servers in other urls, But I think my SoapServer can not work well.
I even get no errors in error-log file.
I just figured out wht was the problem:
The problem with log was that i was checking error-log in my www folder while laravel has its own log file. And using that i figured that i have problem with TokenMismatchException. Laravel's CsrfVerifyMiddleware would not letting me to request using soap.
I just added my url to "except" array inside CsrfVerifyMiddleware file.
Do not use two classes in one file
This is my experience from our project in which used Soap
This is SoapServerController . Paste wsdl file in root folder of your project
class SoapServerController extends Controller {
public function service() {
$server = new \SoapServer('http://' . request()->server('HTTP_HOST') . '/yourwsdlfile.wsdl');
$server->setClass('App\Http\Requests\somenamespace\SoapRequest');
$server->handle();
}
}
and in requests create class for requests like this:
class SoapRequest{
public function functionFromWsdl($args if you want) {
$parameters = (array) $args;
return with(new fooClass())->barMethod($parameters);
}
}
and route must be post:
Route::post('webservice','SoapServerController#service');
In laravel 5 all before statements have turned into middlewares (just like what is in django framework). And you need to implement using middlewares.

creating multiple instances of splFileObject

I have a class similar to this
class x {
function __construct($file){
$this->readData = new splFileObject($file);
}
function a (){
//do something with $this->readData;
}
function b(){
//do something with $this->readData;
}
}
$o = new x('example.txt');
echo $o->a(); //this works
echo $o->b(); //this does not work.
it seems if which ever method called first only works, if they are called together only the first method that is called will work. I think the problem is tied to my lack of understand how the new object gets constructed.
The construct is loaded into the instance of the class. And you're instantiating it only once. And accessing twice. Are different actions. If you want to read the file is always taken, should create a method that reads this file, and within all other trigger this method.
I tested your code and it worked normal. I believe it should look at the logs and see if any error appears. If the file does not exist your code will stop.
Find for this error in your apache logs:
PHP Fatal error: Uncaught exception 'RuntimeException' with message 'SplFileObject::__construct(example.txt): failed to open stream
Answering your comment, this can be a way:
<?php
class x {
private $defaultFile = "example.txt";
private function readDefaultFile(){
$file = $this->defaultFile;
return new splFileObject($file);
}
function a (){
$content = $this->readDefaultFile();
return $content ;
}
function b(){
$content = $this->readDefaultFile();
return $content ;
}
}
$o = new x();
echo $o->a();
echo $o->b();
Both methods will return an object splFile.

PHP: Problems using Singleton pattern and understanding __clone method

I am trying to implement the singleton pattern in php like described here in Example #2:
http://www.php.net/singleton
When I run the example code
$singleton = Example::singleton(); // prints "Creating new instance."
echo $singleton->increment(); // 0
echo $singleton->increment(); // 1
$singleton = Example::singleton(); // reuses existing instance now
echo $singleton->increment(); // 2
echo $singleton->increment(); // 3
it allways ends with Fatal Error 'Clone is not allowed.' directly after 'Creating new instance.'
I would expect that there is no reason for php to call the __clone-method.
In another real-life project of mine I want to have a singleton PlayerManager that holds Player-objects in an array (loaded only once in __construct) and has functions like GetPlayers() or GetPlayersByID($id).
In my script I write something like
$pm = PlayerManager::GetInstance();
$p1 = $pm->GetPlayerByID(0);
echo $p1->SomeNumber; //100
$p1->SomeNumber = 200;
$p2 = $pm->GetPlayerByID(0);
echo $p2->SomeNumber; //100 and not 200, as I would expect
Can someome give me some hints how to implement the PlayerManager using the Singleton pattern correct? I'm not sure if it is only a problem with the singleton or also a problem with returning object references...
I'm not quiet sure why you're getting that error (post your singleton class if you want help with that). Though I always preferred this version to the one you're using, it's a bit simpler: http://www.talkphp.com/advanced-php-programming/1304-how-use-singleton-design-pattern.html
So with the above, your code would look like:
class Counter
{
$CurrentValue = 0;
// Store the single instance of Database
private static $m_pInstance;
private function __construct() { }
public static function getInstance()
{
if (!self::$m_pInstance)
{
self::$m_pInstance = new Counter();
}
return self::$m_pInstance;
}
public function increment ($by)
{
$this->CurrentValue += $by;
return $this->CurrentValue;
}
public function getValue ()
{
return $this->CurrentValue;
}
}
And to use:
$counter = Counter::getInstance();
echo $counter->increment(); // 0
echo $counter->increment(); // 1
$counter = null;
$counter = Counter::getInstance(); // reuses existing instance now
echo $counter->increment(); // 2
echo $counter->increment(); // 3
Tell me how that works out for you.

Does this MVC controller code need to be refactored or not?

I am writing an CSV/Excel-->MySQL import manager for an MVC application (Kohana/PHP).
I have a controller named "ImportManager" which has an action named "index" (default) which displays in a grid all the valid .csv and .xls files that are in a specific directory and ready for import. The user can then choose the files he wants to import.
However, since .csv files import into one database table and .xls files import into multiple database tables, I needed to handle this abstraction. Hence I created a helper class called SmartImportFile to which I send each file be it .csv or .xls and then I get then ask this "smart" object to add the worksheets from that file (be they one or many) to my collection. Here is my action method in PHP code:
public function action_index()
{
$view = new View('backend/application/importmanager');
$smart_worksheets = array();
$raw_files = glob('/data/import/*.*');
if (count($raw_files) > 0)
{
foreach ($raw_files as $raw_file)
{
$smart_import_file = new Backend_Application_Smartimportfile($raw_file);
$smart_worksheets = $smart_import_file->add_smart_worksheets_to($smart_worksheets);
}
}
$view->set('smart_worksheets', $smart_worksheets);
$this->request->response = $view;
}
The SmartImportFile class looks like this:
class Backend_Application_Smartimportfile
{
protected $file_name;
protected $file_extension;
protected $file_size;
protected $when_file_copied;
protected $file_name_without_extension;
protected $path_info;
protected $current_smart_worksheet = array();
protected $smart_worksheets = array();
public function __construct($file_name)
{
$this->file_name = $file_name;
$this->file_name_without_extension = current(explode('.', basename($this->file_name)));
$this->path_info = pathinfo($this->file_name);
$this->when_file_copied = date('Y-m-d H:i:s', filectime($this->file_name));
$this->file_extension = strtolower($this->path_info['extension']);
$this->file_extension = strtolower(pathinfo($this->file_name, PATHINFO_EXTENSION));
if(in_array($this->file_extension, array('csv','xls','xlsx')))
{
$this->current_smart_worksheet = array();
$this->process_file();
}
}
private function process_file()
{
$this->file_size = filesize($this->file_name);
if(in_array($this->file_extension, array('xls','xlsx')))
{
if($this->file_size < 4000000)
{
$this->process_all_worksheets_of_excel_file();
}
}
else if($this->file_extension == 'csv')
{
$this->process_csv_file();
}
}
private function process_all_worksheets_of_excel_file()
{
$worksheet_names = Import_Driver_Excel::get_worksheet_names_as_array($this->file_name);
if (count($worksheet_names) > 0)
{
foreach ($worksheet_names as $worksheet_name)
{
$this->current_smart_worksheet['name'] = basename($this->file_name).' ('.$worksheet_name.')';
$this->current_smart_worksheet['kind'] = strtoupper($this->file_extension);
$this->current_smart_worksheet['file_size'] = $this->file_size;
$this->current_smart_worksheet['when_file_copied'] = $this->when_file_copied;
$this->current_smart_worksheet['table_name'] = $this->file_name_without_extension.'__'.$worksheet_name;
$this->assign_database_table_fields();
$this->smart_worksheets[] = $this->current_smart_worksheet;
}
}
}
private function process_csv_file()
{
$this->current_smart_worksheet['name'] = basename($this->file_name);
$this->current_smart_worksheet['kind'] = strtoupper($this->file_extension);
$this->current_smart_worksheet['file_size'] = $this->file_size;
$this->current_smart_worksheet['when_file_copied'] = $this->when_file_copied;
$this->current_smart_worksheet['table_name'] = $this->file_name_without_extension;
$this->assign_database_table_fields();
$this->smart_worksheets[] = $this->current_smart_worksheet;
}
private function assign_database_table_fields()
{
$db = Database::instance('import_excel');
$sql = "SHOW TABLE STATUS WHERE name = '".$this->current_smart_worksheet['table_name']."'";
$result = $db->query(Database::SELECT, $sql, FALSE)->as_array();
if(count($result))
{
$when_table_created = $result[0]['Create_time'];
$when_file_copied_as_date = strtotime($this->when_file_copied);
$when_table_created_as_date = strtotime($when_table_created);
if($when_file_copied_as_date > $when_table_created_as_date)
{
$this->current_smart_worksheet['status'] = 'backend.application.import.status.needtoreimport';
}
else
{
$this->current_smart_worksheet['status'] = 'backend.application.import.status.isuptodate';
}
$this->current_smart_worksheet['when_table_created'] = $when_table_created;
}
else
{
$this->current_smart_worksheet['when_table_created'] = 'backend.application.import.status.tabledoesnotexist';
$this->current_smart_worksheet['status'] = 'backend.application.import.status.needtoimport';
}
}
public function add_smart_worksheets_to(Array $smart_worksheets = array())
{
return array_merge($smart_worksheets, $this->get_smart_worksheets());
}
public function get_smart_worksheets()
{
if ( ! is_array($this->smart_worksheets))
{
return array();
}
return $this->smart_worksheets;
}
}
In a code review I was told that it might be better not to have a helper class like this but to keep the bulk of the code in the controller action method itself. The argumentation was that you should be able to look at the code in a controller action and see what it does instead of having it call external helper classes outside of itself. I disagree. My argumentation is:
you should create a helper class anytime it makes code clearer, as in this case, it abstracts away the fact that some files have one worksheet or many worksheets in them, and allows for easy future extension, if, say, we want to also import from sqlite files or even directories with files in them, this class abstraction would be able to handle this nicely.
moving the bulk of the code from this helper class back into the controller would force me to create internal variables in the controller which make sense for this particular action, but may or may not make sense to other action methods within the controller.
if I were programming this in C# I would make this helper class a nested class which would literally be an internal data structure that is inside of and only available to the controller class, but since PHP does not allow nested classes, I need to call a class "outside" the controller to help manage this abstraction in a way that makes the code clear and readable
Based on your experience of programming in the MVC pattern, should the above helper class be refactored back into the controller or not?
There are two approaches to controllers: make it thin or thick. When I started my adventure with MVC I made a mistake of creating thick controllers - now I prefer make it as thin as possible. Your solution is good in my opinion.
Here is how I would redesigned your code even further:
class Backend_Application_SmartImport {
public function __construct( $raw_files ) {
}
public function process() {
foreach ($raw_files as $raw_file) {
// (...)
$oSmartImportFileInstance = $this->getSmartImportFileInstance( $smart_import_file_extension );
}
}
protected function getSmartImportFileInstance( $smart_import_file_extension ) {
switch ( $smart_import_file_extension ) {
case 'xml':
return new Backend_Application_SmartImportFileXml();
// (...)
}
}
}
abstract class Backend_Application_SmartImportFile {
// common methods for importing from xml or cvs
abstract function process();
}
class Backend_Application_SmartImportFileCVS extends Backend_Application_SmartImportFile {
// methods specified for cvs importing
}
class Backend_Application_SmartImportFileXls extends Backend_Application_SmartImportFile {
// methods specified for xls importing
}
The idea is to have two classes responsible for processing xml and cvs inheriting from a base class. The main class uses a special method to detect how the data should be processed (Strategy Pattern). The controller just passed a list of files to the instance of Backend_Application_SmartImport class and passes result of process method to the view.
The advantage of my solution is that code is more decoupled and you can easily and in a clean way add new types of processing like xml, pdf, etc.
I agree with you Edward.
Your ImportController does what a Controller is meant to do. It generates the list of files for the user to view and act on, it then passes that list to the View for it to display. I am presuming that you have a process action or similar which is handles the request when a user has selected a file, this file is then passed on to the Helper in question.
The Helper is a perfect example of abstraction and entirely justified in its usage and existence. It is not coupled with the Controller in anyway and doesn't need to be. The Helper could be easily used in other scenarios where the Controller is not present, for example a CRON task, a public API which users can call programmatically without your ImportController.
Your right on the ball with this one. Stick it to 'em!

Categories