Help on understading behavior - stream resource is passed as an object. Why?

Hello all,

I have this singleton connection class to a socket:


final public function __construct()
    {
        $this->_host = 'ssl://example.hlp';
        $this->_porto = 1231;

        try
        {
            
            $filePointer = fsockopen($this->_host, $this->_porto);

            //this will return a resource of stream type.
            //var_dump($filePointer);

            if ($filePointer === FALSE)
            {
                throw new Exception('error. OMG!!! OMG!!!!.');
            }
            else
            {
                return $filePointer;
            }

        }

        catch(Exception $e)
        {
            echo "Erro de Conexão: " .$e->getMessage();
        }

    }

    public static function getInstance()
    {
        if (self::$_instance === null)
        {
            self::$_instance = new self;
        }

        return self::$_instance;
    }

I have an abstract class like so:

public function __construct()
    {
       
        require_once("Conexao.class.php"); 
        $this->_filePointer = Conexao::getInstance();
       
        //this->_filePointer is an OBJECT not a resource stream anymore.
        //var_dump($this->_filePointer); 
       
    }

I know, even the method calls himself getINSTANCE so an object should be expected but I need a stream not an object, so that I can user $this->_filePointer AS A filePointer on other methods on this abstract class that will use fwrite for example.

What can I do?

Do I need to restructure this class relations somehow?
Can I have any help please. Again. :rolleyes:

K. Regards,
Márcio

Can’t exercise any longer, this needs to be done, either well done, or more or less done. The question is, how do deal with this, knowing that it could be better, that we can do better, but we are just not allowed (by time, money whatever) to do so.

I will give my best to simplify without been scruffy. (not sure if this last term is what I wanted to).

Thanks for your time, and patience,
Márcio

You have to do all this regardless, barring instantiating the objects to do it for you.

Try it in procedural, as an exercise.

Márcio, you need to start small and work your way up. You’re having trouble resolving file paths, understanding how the objects are composed and seem determined to over complicate things.

One of the benefits of OOP is that you can quite easily work on small sections of the application at a time, often without even knowing how any of the other components work.

Use this to your advantage, build an object, test it, then build another that uses it.

Anthony. :slight_smile:

I will reply to myself: No.

Ok. I’ve indeed found that path issue. (forgot to upload a file that contained that path updated. :s) Now the return is no longer that the “file doesn’t exist”.

Actually, nothing is caught by the function I believe.

But, now, after the warnings that I quoted earlier, I have below:

Fatal error: Uncaught exception ‘Exception’ with message 'String could not be parsed as XML

And the line of this Uncaught exception was:

list($this->_connection, $this->_xml) = array ($conexao, new SimpleXMLElement($ficheiro));

Despite the fact that the function didn’t provide the answers… it seems that this Uncaught exception did. :slight_smile:

So it must be related with the fact that we are using a path and we should use a binarypack containing the xml part only… ?

It returns: doesn’t exist. :sick:

If I swear that, indeed, it exists on the given location? Does this count anything?
:eek:

Nop. My last try was just stupid. Passing an object inside a function that creates an object… bah!! :injured:

I will wait for your advice. :shifty:

:smiley: Try this.


class ComandosEpp
{
    protected $_conexao;
    protected $_xml;

    public function  __construct(ConexaoEpp $conexao, $ficheiro)
    {
      if(false === file_exists($ficheiro)){
        throw new Exception($ficheiro . ' does not exist!');
      }
      
      if(false === is_readable($ficheiro)){
        throw new Exception($ficheiro . ' is not readable!');
      }
      
      list($this->_connection, $this->_xml) = array ($conexao, new SimpleXMLElement($ficheiro));
    }

    public function executa()
    {
            $this->_conexao->sendFrame($this->_xml);
            return $this->_connection->getFrame();
    }
}

Until now, I was thinking that, the better code we have, the better we will have a nice abstraction, a nice way to maintain and etc… but, also, on the “view” part of things, put them as simple as possible… those were my thoughts.

So let’s say I have a function called Register domain (on my “view” part that will use this class and that, that function, will need 5 commands to run).

I will need to, inside that function:
require a 5 commandEspecific classes;
Instanciate 5 specific classes;
So we can have 5 commands to do, we have to include 5 files, one connection, instantiate 5 objects, and execute 5 times each of them.

Isn’t this to much? Don’t take me wrong, you obviously deserve all my respect, I’m just asking. I’ve not seen enough code on my life… so I’m wondering. :slight_smile:

One step at a time. :stuck_out_tongue:
[B]
Does your XML file contain white-space or a new line prior to the XML?

[/B]SimpleXMLElement will load a file just fine, don’t worry.

No white spaces or new lines on the xml.

:slight_smile:

I’ve changed here.

public function  __construct(ConexaoEpp $conexao, $ficheiro)
    {
        $xmlObj = simplexml_load_file($ficheiro);

        list($this->_connection, $this->_xml) = array ($conexao, new SimpleXMLElement($xmlObj));
    }

Getting:

Warning: simplexml_load_file() [function.simplexml-load-file]: I/O warning : failed to load external entity

:s

But I will calm down… sort of… :D:D:D:D

One day Sir Anthony, really, one day, I will do typos as well while I write some nice code in 5 minutes while on a reunion!!! (:

Conexao.Class.php


class Conexao
{
    protected $_filePointer;

    public function  __construct($hostname, $port)
    {
        $this->_filePointer = fsockopen($hostname, $port);
    }

    public function write($data, $length = null)
    {
        return fwrite($this->_filePointer, $data, null===$length ? strlen($data) : $length);
    }

    public function read($length)
    {
        return fread($this->_filePointer, $length);
    }
}

ConexaoEpp.Class.php


require_once ('Conexao.class.php');

class ConexaoEpp extends Conexao
{
   const OFFSET = 4;

   //Type Hinting on PHP own objects!! SO NICE :)
   public function sendFrame(SimpleXMLElement $frame)
   {
       return parent::write(pack('N', strlen($frame->asXML())+ self::OFFSET) . $frame->asXML());
   }

   public function getFrame()
   {
       $unpacked = unpack('Nlength', parent::read(self::OFFSET));
       
       //note: instead of three lines! Nice number2!  But I must return to three lines to properly comment.
       return new SimpleXMLElement(parent::read((int)$unpacked['length'] - self::OFFSET));
   }
}

ComandosEpp.Class.php


class ComandosEpp
{
    protected $_conexao;
    protected $_xml;

    public function  __construct(ConexaoEpp $conexao, $ficheiro)
    {
        list($this->_connection, $this->_xml) = array ($conexao, new SimpleXMLElement($ficheiro));
    }

    public function executa()
    {
            $this->_conexao->sendFrame($this->_xml);
            return $this->_connection->getFrame();
    }

}

ComandoHello.Class.php

require_once ('ComandosEpp.Class.php');

class ComandoHello extends ComandosEPP
{
    public function  __construct(ConexaoEpp $conexao, $ficheiro = 'RepositorioXml/EppHello.xml')
    {
        parent::__construct($conexao, $ficheiro);
    }
}

EppHello.php


require_once('Classes/ConexaoEpp.Class.php');
require_once('Classes/ComandoHello.Class.php');

$conexao = new ConexaoEpp('ssl://example.com', 3121);

$comandoHello = new ComandoHello($conexao);

$comandoHello->executa();

When I run this last one, I’m getting:

SimpleXMLElement::__construct() [simplexmlelement.–construct]: Entity: line 1: parser error : Start tag expected, ‘<’ not found

I have certified that the XML is uploaded. But I’ve used a relative path. (like on the previous examples).

I’ve changed to an absolute path just to be sure. No changes.

I’ve var_dump $ficheiro and I’m getting the path. And this is the issue.

string(57) “/public_html/DevRegistrar/RepositorioXml/EppHello.xml”

I is expecting an XML, so:
simplexml_load_file should be here somwhere…

$xmlObj = simplexml_load_file('RepositorioXml/EppHello.xml');

So, we can put in on the Hello class, but maybe not, because, it’s not Hello specific, is more command specific… or even more generic…

On our ConexaoEpp.Class.php perhaps.

But where? Before the frame is treated by the constructor? As a property?

Yes, sorry, that was typo. It should indeed be:


    public function execute(){
        $this->_connection->sendFrame($this->_xml);
        return $this->_connection->getFrame();
    }

We’ve extended the base connection object to provide more application specific functions, we could of course create yet another object to pass in an instance of the base object if you wished.

This seemed cleaner.

Nope. :slight_smile:

Ok. That “nop” is here, and I will give another try. :slight_smile:
But before any advance, I must clarify a lot on your provided code.


class Command
{
    protected
        $_connection,
        $_xml;
    
    public function __construct(EppConnection $connection, $file){
        list($this->_connection, $this->_xml) = array($connection, new SimpleXMLElement($file));
    }
    
    public function execute(){
        $connection->sendFrame($this->_xml);
        return $connection->getFrame();
    }
}

The method execute() will use $connection;
Where does $connection come from?
Shouldn’t it be $this->_connection ? Or shouldn’t it come from execute parameters or something?


$connection = new EppConnection('example.org', 1231);

$command = new HelloCommand($connection);
$command->execute();

$command = new GetMeASandwichCommand($connection);
$command->execute();

Re-use the connection. :slight_smile:

Hello is one of many commands that we can request to the server.

A workflow, of commands will be something like this:
loginComand -> createContactComand -> createDomainComand -> logoutComand;
This should not exceed the 30 seconds (or the server will shut down), but I guess that it will be ok.

Each of those commands are, XML instructions, that we need to grab and send some node data before we actually execute the command.

In your usage approach, however, it seems that, for each command we want to do, we need to establish a connection. But the intention was to allow us to make the connection, use as many commands as we need, and then close the connection again. Only one connection it’s allowed.

It was, probably, my bad English that lead us here. And I’m really sorry. :frowning:

Any thoughts? (after the meeting please, I’m feeling quite guilty already).

Best regards,
Márcio

Ok. Object Oriented is really cool. :slight_smile:

I’m still screeching my head about what you said about abstraction and the fact that now, on each command, we need to use and reuse the $connectio

I believe this is something to do with my lack of knowledge about singleton as well…

Each time we reuse $connection we are not getting a new connection each time?

:shifty:

Thanks for making me feeling so ignorant. :confused: :wink:
I will now read the reply.

Thanks.

I’ve added the following on commands construct…

public function ComandosEpp()
    {
       $conn = new ConexaoEpp();

       return $conn;
    }

And it seems to work… :smiley:

But then I need to include the conection on our comandosEPP and…
that seems to miss the Abstraction propose described earlier right ?
We are calling a far away (from the food chain) element into and element almost in front of that food chain…

I’ve noticed you have replied, I will see the reply now. And see what it brings. :smiley:

From your example, we instantiate a connection object, then we passed to the hello commands.
Since ALL comands will need a connection, probably passing it as a constructor of comandos class was not that bad idea? (probably it is… but I’m just asking. :slight_smile: )

I recall that we should use php __construct instead of a function with the same name as the class. I don’t recall why. :S
However, if I use __construct I will have an override. If I don’t use __construct and I use a function the same name as the class, as I did, no override seems to occur.
What’s happening?

Thanks in advance, “see” you later then,
Márcio


$command = new HelloCommand(new EppConnection('example.org', 1231));
echo $command->execute()->svID;


class Connection
{
    protected
        $_pointer;
    
    public function __construct($hostname, $port){
        $this->_pointer = fsockopen($hostname, $port);
    }
    
    public function write($data, $length = null){
        return fwrite(
            $this->_resource,
            $data,
            null === $length ? strlen($data) : $length
        );
    }
    
    public function read($length){
        return fread(
            $this->_resource,
            $length
        )
    }
}

class EppConnection extends Connection
{
    const
        OFFSET = 4;
    
    public function sendFrame(SimpleXMLElement $frame){
        return parent::write(
            pack('N', strlen($frame->asXML()) + self::OFFSET) . $frame->asXML()
        );
    }
    
    public function getFrame(){
        $unpacked = unpack('NLength', parent::read(self::OFFSET));
        return new SimpleXMLElement(
            parent::read((int)$unpacked['length'] - self::OFFSET)
        );
    }
}

class Command
{
    protected
        $_connection,
        $_xml;
    
    public function __construct(EppConnection $connection, $file){
        list($this->_connection, $this->_xml) = array($connection, new SimpleXMLElement($file));
    }
    
    public function execute(){
        $connection->sendFrame($this->_xml);
        return $connection->getFrame();
    }
}

class HelloCommand extends Command
{
    public function __construct(EppConnection $connection, $file = 'usr/anthony/web/epp/frames/hello.xml'){
        parent::__construct($connection, $file);
    }
}

I’m not entirely comfortable with it, but it took 5 minutes and I was in a meeting!