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

Sorry Márcio, I’m currently sat in a meeting so don’t have the time right now but here’s a quick snippet that may help.

You need to pass in the connection, will be back as soon as possible.


<?php
$connection = new Connection();
$command = new HelloCommand($connection);
$response = $command->execute();
echo $response->svID;
?>

Now with this structure where we need to instantiate the connection?

I’m asking this because I’m getting an error…

From the most abstract to the less abstract, so I hope: :slight_smile:

ConexaoEpp.Class.php


class ConexaoEpp
{

    //TO TEST: IF I CHANGE TO PRIVATE WHAT WILL HAPPEN ?
    protected $_filePointer; //according to php documentation, fsocken returns a file pointer. I need to be strict to properly understand.



    public function  __construct($hostname='ssl://mysocketpath.com', $porto=1231)
    {
        $this->_filePointer = fsockopen($hostname, $porto);
    }

    public function write($data, $length = null) //by doing this, we are telling $data is mandatory and $length is optional.
    {
        //writes the contents of $data to the file stream pointed by our file pointer, until length is reached.
        //ternary operator: if $lenght is exactly equal to null, then, return the string length of $data, else, if (it's not NULL) return itself,
        //this means, return the value it already have;
        //WHY DO WE NEED THIS?
        
        //TO TEST: IF I CHANGE TO $length===null WHAT WILL HAPPEN?
        return fwrite($this->_filePointer, $data, null===$length ? strlen($data) : $length);
    }

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

}

EppFrameManager.Class
Note that I needed to change the sendFrame attribute:


class EppFrameManager
{
    //const, because on all application, this value will never change. nice. :)
    const OFFSET = 4;

    //TO TEST: WHY PROTECTED and not PRIVATE - because we will use $_conexao on a descendent class?
    protected $_conexao;

    public function  __construct(ConexaoEpp $conexao)
    {
        //pass the data received to a private property of our object
        $this->_conexao = $conexao;
    }

    /**
     *
     * @param <string> $xml
     *
     * A frame, on this EPP context, is: the sum of a XML command instruction + 4 bytes containing the length.
     * It will be that group that we call frame.
     *
     * So, I believe, sendFrame will receive a $xml and send a frame by using fwrite.
     */
    public function sendFrame($xml)
    {
        //the (only?) way we have to access a const is by calling it staticly on self ? (no $this->OFFSET... ?)
        return $this->_conexao->write(pack('N', strlen($xml)+self::OFFSET).$xml);
    }

    public function getFrame()
    {
        $unpacked = unpack('NLength', $this->_conexao->read(self::OFFSET));

        //the reason why we are removing the FIRST (the length is at the beginning of our frame *) 4 bytes, is to be able to grab only the XML part of it,
        //so, instead of returned right away, the solution I found was, place the remaining binary pack into
        //a variable:

       //* before, on the "old" code, it was the fact that fread was called twice that guarantee to us, that we are accessing the FIRST 4 bytes. What about now? :s
        $binaryPack = $this->_conexao->read((int)$unpacked['lenght'] - self::OFFSET);

        $xml = new SimpleXMLElement($binaryPack);

        return $xml;
    }
}

ComandosEppClass.php


require_once ('EppFrameManager.Class.php');

class ComandosEpp extends EppFrameManager
{

    /**
     *
     * @return <object>
     */
    public function hello()
    {

       //reads the XML send comands from the repository:
       $xmlObj = simplexml_load_file('RepositorioXml/EppHello.xml');

       //pass then into a string so that it can be consumed by sendFrame;
       $xmlString = $xmlObj->asXML();

       //sends the frame (conn->write->fwrite)
       $this->sendFrame($xmlString);

       //receives the frame containing the returned greeting comand:
       $greetingXML = $this->getFrame();

       //return the greeting command on a simpleXMLElement object.
       return $greetingXML;

    }
}

And finally: :slight_smile:
EppHello.php


require_once('Classes/ComandosEpp.Class.php');

$comandos = new ComandosEpp;

$greeting = $comandos->hello();

$serverID = $greeting->svID;

echo $serverID;

When I run it, I’m getting:

Catchable fatal error: Argument 1 passed to EppFrameManager::__construct() must be an instance of ConexaoEppFccn, none given …

I would like to have it working, and then do some tests that I’ve marked and then, follow any suggestions you may have to make it better.

About the error:
Nothing as been passed to EppFrameManager construct.
So, nothing as been passed on EppFrameManager instantiation…
So, nothing as been passed on our Comandos class.
And it’s true, on Comandos (that extends EppFrameManager) nothing about connection is passed.

I thought that THAT could be the issue, so I put it, on ComandosEppClass the following:


public function  __construct(ConexaoEpp $conexao) 
{
        parent::__construct($conexao);
}

But I guess it wrong. Same error occours…
We need to instantiate a connection object and pass it, I understand the error… but where?.. I’m missing some dots…

A little lost here… please have patience…
Márcio

I will be shocked, then almost cry, and try studying it.

Give me some hours, or an half day (i’m on the middle of other things as well, and some questions should arrive).

Thank you very very much!
Márcio


class FrameManager
{
    const OFFSET = 4;
    
    protected
        $_connection;
        
    public function __construct(Connection $connection){
        $this->_connection = $connection;
    }
    
    public function sendFrame($frame){
        return $this->_connection->write(
            pack('N', strlen($frame) + self::OFFSET) . $frame
        );
    }
    
    public function getFrame(){
        $unpacked = unpack('NLength', $this->_connection->read(self::OFFSET));
        return $this->_connection->read(
            (int)$unpacked['length'] - self::OFFSET
        );
    }
}

class Connection
{
    protected
        $_resource;
        
    public function __construct($hostname, $port = 1231){
        $this->_resource = 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
        )
    }
}

Will try to fill in the gaps later, after my morning coffee! :smiley:

You should be asking yourself, “Do I need it?” rather than, “Let me know if I shouldn’t use it” .

Leave. It. Alone. :smiley:

Lol. ok.

But I had one already?! :stuck_out_tongue:

I will, try to grab the singleton structure from the above, and passed to this one.

If I should not, because a lot of changes need to be made, let me know about it.
And I will stop immediately.

Here I goooo!!!
Márcio

Just a quick answer, if possible, no point on making a singleton connection class then?

I thought it was a good think to guarantee only one instance of the connection object…

Thanks in advance,
Márcio

I understand. Almost all.

The issue that I’m not yet finding a way out of it, is that, THOSE fwrite and fread actually, on this case, seem that they cannot be so abstract. (maybe then can and I just can’t figure them out how), but… let me give you an example:


public function enviarFrame($xml) //send frame that accepts an XML string
    {
        try
        {

            $bytesEscritos = fwrite($this->_filePointer, pack('N', (strlen($xml)+4)).$xml);

        ...

Here I’m using a fwrite yes, but inside, I’m using a pack with a given format (Big Endian on this case), the length is the length of my string yes… but I need to add those 4 bytes… at the beginning.

The getFrame is not better either…

//this will read the first 8x4 bits = 00000000 0000000 00000000 00000000 and store it in memory.
        $binaryStringPackOfFirstFourBytes = fread($this->_filePointer, 4);

        //this will grab or binaryStringPack (formatted into Big Endian from the server), and unpack into an associative array.
        $unpacked = unpack('Nlength', $binaryStringPackOfFirstFourBytes);

        //this will grab our lenght from our string array key.
        $length = $unpacked['length'];

        //read the remaining of our stream: (minus the 4 bytes that were already readed on the first fread call.
        $remainBinaryPack = fread($this->_filePointer, ($length - 4));


I will give a thought about how can all this (for me at least) complex relations can be split into different objects according to the structure suggested. But do you still believe the scheme provided by you will fit here?

Thanks a lot,
Márcio


 throw new ZOMGException();

Haha! That made my day :wink:

@Oikram: I you want me to explain what I changed let me know, but Anthony’s example is much more refined (and better) so I suggest you study his example :slight_smile:

You rock. :slight_smile:
Do you care to explain what have we done here?
And what have I was not doing there?

:slight_smile:

I mean:

$this->filePoint = false;

Why doing it equal to false?

Have we, from my first code to yours, passed the filepointer of our connection to a property, so that we can access it later by pointing to that property on the instance side?

Can you please elaborate?

trying to understand,
Márcio

I’d be interested in seeing how you’re planning to have these different objects interact, right now, it seems rather pointless having an object wrap just a handle.

Ok. I will try to found when we need a singleton later on then. :slight_smile:

AHHHRRGG!!! :smiley:

That is a question that actually I would like to ask. But I didn’t know how. :smiley:

But now that you’ve mention it… well… I don’t really know what I’m doing… or… better, I’m not 100% sure about it.

The point is to have a connection class, that connection class is doing the connection to the socket. OK.

Then, the abstract class as some methods, it calls the connection on the constructor, then, it will have, as well, a sendFrame and getFrame methods.

Those are methods, will be used by ALL methods on our commands class..
Each command we do, sends and receives frames.

And was those assumptions that make me thought on a structure like this.

I know nothing about factories, interfaces, and so on… so… I was playing with the limited object oriented knowledge that I had. :slight_smile:

But, please, if it makes no sense, or you can thing of on a better way do deal with it, knowing that my OOP knowledge is not good… please say so.

Regards,
Márcio

If you only need the filePointer, try something like this:


$this->connection = Conexao::getInstance();
$filePointer = $this->connection->filePointer;

And in your Conexao class’ __construct() do this:


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


        try
        {
            $this->filePointer = fsockopen($this->_host, $this->_porto);
            if ($this->filePointer === FALSE)
            {
                throw new Exception('error. OMG!!! OMG!!!!.');
            }
        }

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


Well, you’d be better suited having a Connection class that let’s you interact with the pointer.

You could then call Connection::write($data)/Connection::read($length) from another object which provides your frames etc…


class Connection
{
  protected
    $_pointer;
    
  public function __construct($host, $port){
    $this->_pointer = fsockopen($host, $port);
    if(false === is_resource($this->_pointer)){
      throw new ZOMGException();
    }
  }
  
  public function getPointer(){
    return $this->_pointer;
  }
  
  public function write($data){
    return fwrite($this->_pointer, $data, strlen($data));
  }
  
  public function read($length = 0){
    return fread($this->_pointer, $length);
  }
  
  public function close(){
    return fclose($this->_pointer);
  }
}


class FrameManager
{
  protected
    $_connection;
    
  public function __construct(Connection $connection){
    $this->_connection = $connection;
  }
  
  public function writeFrame(){
    $this->_connection->write(/**/);
  }
  
  public function readFrame(){
    $this->_connection->read(/**/);
  }
}

Can you see how the Connection object bears no relevance to your application? It’s too far down the food chain, which means you can use it in another application for say, talking HTTP.

The next level up this ‘food chain’, we increase the knowledge the object knows about the application.

This, is Abstraction - albeit poorly explained! :smiley:

Let’s keep it simple for now. :stuck_out_tongue: