creating multiple instances of splFileObject - php

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.

Related

PHP returns object as null occasionally and unintentionally

We have PHP code in production that sometimes fails with "Call to member function on null", although the same code path executes fine several times before that in one invocation. We have a test that reproduces the error consistently at the same run of the loop.
I already proved that the object gets created correctly in the factory even if it gets returned as null. The factory method must not return null in any case, as indicated in the DocBlock. This question is not related to nullable return types or something like that.
The process does not exceed memory or runtime limitations and I already tried turning off the garbage collector, but no luck. The error happens both in PHP 7.0 and 7.3 on Debian, did not try on other versions or operating systems.
I am not allowed to paste the real code here, but I wrote a simple mockup to explain in more detail. Please keep in mind that this demo code will not result in the error, it is just meant to show the general structure of the program that runs into this fault.
// Three placeholder classes with common methods
class Bender
{
public function common()
{
echo "Bend, bend!" . PHP_EOL;
}
}
class Clamper
{
public function common()
{
echo "Clamp, clamp!" . PHP_EOL;
}
}
class Worker
{
public function common()
{
echo "Work, work!" . PHP_EOL;
}
}
// abstract class with static factory to produce objects
abstract class MomCorp
{
/**
* Factory to create one of several objects
*
* #param string $name
* #return Bender|Clamper|Worker
*/
public static function factory($name)
{
$type = self::managementDecision($name);
switch ($type)
{
case "bender":
$robot = new Bender();
break;
case "clamper":
$robot = new Clamper();
break;
default:
$robot = new Worker();
}
// optional QA works flawlessly here, object is fine all the time!
// $robot->common();
return $robot;
}
public static function managementDecision($name)
{
// irrelevant magic happens on $name here
return "bender";
}
}
foreach (['Rodriguez', 'Angle-ine', 'Flexo', 'Billie'] as $newName)
{
echo "$newName: ";
// The next two lines break after some loops - why?
// The perfectly functional object in factory gets returned as null
$bot = MomCorp::factory($newName);
$bot->common();
}
// SAMPLE OUTPUT
// Rodriguez: Bend, bend!
// Angle-ine: Bend, bend!
// Flexo: Bend, bend!
// Billie: Call to a member function common() on null
Has anyone experienced the same and has any hints on what might cause such an error and how to fix it?

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");
...
}

class_exists when autoload flag is false

So, as part of a project, I was considering building a flagging system. The idea behind this would be a cron job that runs daily to determine whether each of a series of flags still applied to a specific object (and if so, save that flag data for the object).
// code stub
$flags = $this->getFlags();
foreach($flags as $flag)
{
$className = 'Svc_Flags_'.$flag->flag_code;
if(class_exists($className, false)
{
(new $className())->setFlag();
}
}
And right now, in the dummy code for that class, I have a constructor that echos a simple text message, and the function setFlag() that echos a different text message.
<?php class Svc_Flags_Test extends Svc
{
public function __construct()
{
echo 'construct<br/>';
}
public function setFlag()
{
echo 'set flag<br/>';
}
}
Now, this doesn't work. By that, I mean that I am not seeing either echo.
However, if I do this:
// code stub
$flags = $this->getFlags();
foreach($flags as $flag)
{
$className = 'Svc_Flags_'.$flag->flag_code;
(new $className())->setFlag(); // This is the added line of code
if(class_exists($className, false)
{
(new $className())->setFlag();
}
}
I get the constructor echo, and the setFlag() echo TWICE.
Why is this happening? Now, I'm pretty sure I could just wrap part of this in a try/catch block to get past any errors if a class isn't there, but I'm curious as to why it doesn't seem to find the class unless I explicitly call it before the if statement.

PHP: Load a class in a class

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;
}

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.

Categories