(PHP) A Few OO Examples

No, not secret CIA crap.. but just about as tough to crack for most of us. He's got it all, css, html, xhtml, php.. you name it!

Moderator: Zorro

(PHP) A Few OO Examples

Postby Zorro » April 1st, 2011, 8:40 pm

Lately at work, I've been messing with a lot of advanced APIs. I recently became acquainted with a set of APIs for the Endicia “Label Server”. These APIs communicate with Endicia and the USPS to order postage and get shipping labels.

The ultimate goal of using these APIs is to allow our clients who manage online businesses to automatically order postage and print shipping labels for several hundred incoming orders within a matter of minutes; all from their own websites and online control-panels. The shipping details, parcel weight, size, mail class, etc. for each incoming order are sent to Endicia's Label Server APIs with postage already paid. Endicia then sends back a bunch of images (labels), which we then compile into a PDF that can be fed directly to the client's printer (ideally a label printer).

The set of classes I've written that allow PHP to communicate with the Endicia Label Server's APIs totals over 5000 lines of code. Obviously, that's too large to bother posting here. Instead, I've decided to highly-condense the concept of these classes down to a bare minimum in hopes that it might provide the reader with some insight into proper OO techniques.

I'm going to start by posting a “condensed” class by itself, and will then explain each method (i.e. function). I've had to break a few of my own coding standards with this condensed class so that it would be suitable for posting. But for the record, under normal production circumstances I strictly follow the PEAR Coding Standards.

In a nutshell, this class:

    * Takes a bunch of data we provide it.
    * Verifies the data is correct.
    * Cleans the data if necessary.
    * Compiles data into an XML statement.
    * Sends XML statement to the applicable server through cURL's POST method.
    * Parses the cURL response.

Ultimately, our goal would be to send shipment details to the API server, and receive a positive response back that contains our label image (base64 encoded), postage rate, tracking number, etc.

Code: Select all
<?php

class GetPostageLabel
{
    // URL to send cURL request to
    private $_url = 'http://www.example-address-to-api.com';

    // Data to send
    private $_data = array();

    /**
     * Return address.
     */
    public function setFrom($name, $address1, $address2, $city, $state, $postal, $country)
    {
        $this->_data['FromName']     = $name;
        $this->_data['FromAddress1'] = $address1;
        $this->_data['FromAddress2'] = $address2;
        $this->_data['FromCity']     = $city;
        $this->_data['FromState']    = $state;
        $this->_data['FromPostal']   = $postal;
        $this->_data['FromCountry']  = $country;
    }

    /**
     * Recipient's address.
     */
    public function setTo($name, $address1, $address2, $city, $state, $postal, $country)
    {
        $this->_data['ToName']     = $name;
        $this->_data['ToAddress1'] = $address1;
        $this->_data['ToAddress2'] = $address2;
        $this->_data['ToCity']     = $city;
        $this->_data['ToState']    = $state;
        $this->_data['ToPostal']   = $postal;
        $this->_data['ToCountry']  = $country;
    }

    /**
     * Send the address data.
     */
    public function sendRequest()
    {
        // Valid data
        if ($this->_verifyData()) {
            // Scrub data
            $this->_scrubData();

            // Compile XML request
            $request = $this->_compileXML();

            // Execute cURL, send XML request
            $response = $this->_sendCURL($request);

            // Successfully sent
            if ($response) {
                // PARSE RESPONSE
                // RETURN TRUE IF RESPONSE ACCEPTABLE
                // RETURN FALSE IF RESPONSE CONTAINS ERROR
            // Failed to send
            } else {
                return false;
            }
        // Invalid data
        } else {
            return false;
        }
    }

    /**
     * Verify we have all the data we need and that it's correct.
     */
    private function _verifyData()
    {
        if (strlen($this->_data['FromCountry']) != 2) {
            return false;
        }

        // MORE DATA VERIFICATION HERE
        return true;
    }

    /**
     * This method is called after we've verified the data. Now let's clean the data.
     */
    private function _scrubData()
    {
        // Convert ZIP+4 (i.e. 20500-0003) to ZIP5 (i.e. 20500)
        $this->_data['ToPostal'] = substr($this->_data['ToPostal'], 0, 5);

        // MORE DATA SCRUBBING HERE
    }

    /**
     * Compile the XML data for the cURL request.
     */
    private function _compileXML()
    {
        $request = '
            <LabelRequest>
                <FromName>' . $this->_data['FromName'] . '</FromName>
                <FromAddress1>' . $this->_data['FromAddress1'] . '</FromAddress1>
                <FromAddress2>' . $this->_data['FromAddress2'] . '</FromAddress2>
                <FromCity>' . $this->_data['FromCity'] . '</FromCity>
                <FromState>' . $this->_data['FromState'] . '</FromState>
                <FromPostal>' . $this->_data['FromPostal'] . '</FromPostal>
                <FromCountry>' . $this->_data['FromCountry'] . '</FromCountry>
                <ToName>' . $this->_data['ToName'] . '</ToName>
                <ToAddress1>' . $this->_data['ToAddress1'] . '</ToAddress1>
                <ToAddress2>' . $this->_data['ToAddress2'] . '</ToAddress2>
                <ToCity>' . $this->_data['ToCity'] . '</ToCity>
                <ToState>' . $this->_data['ToState'] . '</ToState>
                <ToPostal>' . $this->_data['ToPostal'] . '</ToPostal>
                <ToCountry>' . $this->_data['ToCountry'] . '</ToCountry>
            </LabelRequest>
        ';

        // Return XML data
        return $request;
    }

    /**
     * Initiate cURL and send the actual request.
     */
    private function _sendCURL($request)
    {
        // Start cURL
        $handle = curl_init();

        // Prepare cURL
        curl_setopt($handle, CURLOPT_URL, $this->_url);
        curl_setopt($handle, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($handle, CURLOPT_POST, true);
        curl_setopt($handle, CURLOPT_POSTFIELDS, $request);

        // Execute cURL
        $response = curl_exec($handle);

        // Close cURL
        curl_close($handle);

        // Return false or $response (XML)
        return $response;
    }
}

?>


Don't panic, that's only 150 lines of code. I recommend that you copy and paste the code above into your editor/IDE so you can see it with proper syntax highlighting. Then after a few minutes of study it should start to make sense.

Code: Select all
<?php

/**
 * Return address.
 */
public function setFrom($name, $address1, $address2, $city, $state, $postal, $country)
{
    $this->_data['FromName']     = $name;
    $this->_data['FromAddress1'] = $address1;
    $this->_data['FromAddress2'] = $address2;
    $this->_data['FromCity']     = $city;
    $this->_data['FromState']    = $state;
    $this->_data['FromPostal']   = $postal;
    $this->_data['FromCountry']  = $country;
}

?>


setFrom()
setTo()

This is a pretty crippled function, but it'll do for the purpose of this tutorial. We're basically storing everything in the private $_data array of the class. The setTo() function works just like setFrom().

Another option would be instead of storing everything in the $_data array (which you'll see at the top of the class), we could declare a bunch of private variables at the top of the class, like such:

private $_fromName;
private $_fromAddress1;
private $_fromAddress2;
private $_fromCity;
etc...


The reason I've opted for using a single array ($_data) instead of a bunch of variables is so we instantly know that anything inside this array is to be sent through cURL. If I made each item a variable instead of an array key, its purpose as data for the API request could easily get confused with things like the $_url, and about a dozen or so other variables that I've hidden for the sake of keeping this tutorial simple.

Code: Select all
<?php

/**
 * Send the address data.
 */
public function sendRequest()
{
    // Valid data
    if ($this->_verifyData()) {
        // Scrub data
        $this->_scrubData();

        // Compile XML request
        $request = $this->_compileXML();

        // Execute cURL, send XML request
        $response = $this->_sendCURL($request);

        // Successfully sent
        if ($response) {
            // PARSE RESPONSE
            // RETURN TRUE IF RESPONSE ACCEPTABLE
            // RETURN FALSE IF RESPONSE CONTAINS ERROR
        // Failed to send
        } else {
            return false;
        }
    // Invalid data
    } else {
        return false;
    }
}

?>


sendRequest()
_verifyData()
_scrubData()
_compileXML()
_sendCURL()


1. We verify the data using the _verifyData() method.
- a) If the data is invalid, we return false.
- b) If the data is valid, we continue...

2. Scrub the data using the _scrubData() method.
3. Compile the data into an XML statement using the _compileXML() method.
4. Attempt to send the XML statement through cURL to the server using the _sendCURL() method.
- a) If the cURL request failed, we return false.
- b) If the cURL request was successful, we continue...

5. Parse the cURL response to see if the API server sent back any errors or problems.
- a) If we're happy with the API server's reply, we store whatever data we need (i.e. store our tracking numbers, postage cost, label images, etc.) and return true.
- b) If we're not happy with the API server's reply, we return false.

An uber simple usage of what we just made:

Code: Select all
<?php

$label = new GetPostageLabel();
$label->setFrom('Zorro', '555 No Street', 'Morristown', 'TN', '37814', 'US')
$label->setTo('Obamanation', '1600 Pennsylvania Avenue NW', 'Washington', 'DC', '20500-0003', 'US');

if ($label->sendRequest()) {

    $label->putLabel('myfolder/label.png'); // Function not shown in examples
    // Do other stuff

} else {

    $label->getErrors(); // Function not shown in examples

}

?>


Note: Obviously, none of this code will actually “work.” We need a lot more code to actually communicate with the real API.

What You Can Learn From This Example

Any PHP programmer will tell you that the sendRequest() function could also contain all the code of the _verifyData(), _scrubData(), and _sendCURL() functions. But doing that would simply be bad coding. Proper object oriented programming separates code based upon the code's functionality and purpose. Think of The Matrix movies where each “program” had a “purpose” in the overall scope of things.

Summary

    * Each function serves a distinct purpose.
    * We've separated public and private functions correctly (think of private functions as “helpers” to public functions).
    * No public variables inside class. We've used set() and get() functions to insert and retrieve data from the scope of the class.
Research shows that one in three Clinton supporters are just as stupid as the other two.
Zorro
Squad Member
 
Posts: 543
Joined: February 5th, 2011, 12:25 pm

Re: (PHP) A Few OO Examples

Postby BlackCat » January 24th, 2013, 12:21 am

I wish I could take a pill and suddenly know everything there is to know about php.
User avatar
BlackCat
 
Posts: 1674
Joined: February 12th, 2011, 12:05 pm

Re: (PHP) A Few OO Examples

Postby Zorro » January 26th, 2013, 8:47 pm

It's amazing how much I've learned since even writing this terrible article almost two years ago. It takes years and years of learning and experimenting to become a fully competent programmer, and as I've stated many times before, college will NOT help you in this area. For programming, there is no substitute for teaching yourself and experimenting with it hands on.
Research shows that one in three Clinton supporters are just as stupid as the other two.
Zorro
Squad Member
 
Posts: 543
Joined: February 5th, 2011, 12:25 pm

Re: (PHP) A Few OO Examples

Postby BlackCat » January 26th, 2013, 9:01 pm

You have to LOVE it to really hone your skills.

In college, everything is RUSH RUSH RUSH. In my opinion, trial and error -- although a slow process -- is one that yields favorable results.

The other day at my school I realized all of their computers had Notepad++ on it. Awesome.
User avatar
BlackCat
 
Posts: 1674
Joined: February 12th, 2011, 12:05 pm

Re: (PHP) A Few OO Examples

Postby Zorro » January 27th, 2013, 12:36 am

Notepad++ is a nifty little editor if you're working in a Windows environment; I use it for all of my JavaScript and CSS work, since you only need a basic editor for them. I use the Netbeans IDE for all of my other code work.
Research shows that one in three Clinton supporters are just as stupid as the other two.
Zorro
Squad Member
 
Posts: 543
Joined: February 5th, 2011, 12:25 pm

Re: (PHP) A Few OO Examples

Postby BlackCat » January 27th, 2013, 1:12 am

Do you also use http://textmechanic.com ?
User avatar
BlackCat
 
Posts: 1674
Joined: February 12th, 2011, 12:05 pm

Re: (PHP) A Few OO Examples

Postby Zorro » January 27th, 2013, 10:21 pm

Nah. I use "regular expressions" (commonly called regex/regexes) in my editor or IDE when I need to do complex text adjustments.
Research shows that one in three Clinton supporters are just as stupid as the other two.
Zorro
Squad Member
 
Posts: 543
Joined: February 5th, 2011, 12:25 pm

Re: (PHP) A Few OO Examples

Postby BlackCat » January 28th, 2013, 12:03 am

I'm looking for a program that performs various text adjustments such as find & replace a given text, case converter, remove duplicate lines and capitalize a letter after a dash. I'm looking to ditch text mechanic as much as I love it because I don't want to be so dependent on a tool that could disappear tomorrow as it's browser-based. Also, I would feel much better about having an offline program due to it never being updated so that I wouldn't have to change the coding of my scripting programs had a shift in the tool's layout mess everything up.

To elaborate a bit more on the "dash" circumstance I mentioned above, here is what I'm going through.

I'm given a name like JOHN JAMES-RIDER SMITH

A text adjustment of making all text lowercase + the first letter of each word uppercase then reads:

John James-rider Smith

I'm sure there is a program that can capitalize that R. Will "regular expressions" do this and how much does it cost? Thanks.
User avatar
BlackCat
 
Posts: 1674
Joined: February 12th, 2011, 12:05 pm

Re: (PHP) A Few OO Examples

Postby Zorro » January 29th, 2013, 8:23 pm

Regular expressions can do that for sure - they are simply programmatic patterns for matching and replacing strings. They can be used in text editors that support them, as well as programming languages. They come in many flavors depending on the programming language or editor, with the most common syntaxes being POSIX and Perl (most programming languages have regex engines with a Perl-like syntax). I'd link you to a few tutorials on the basics of regex patterns, but it'd likely not be something most non-programmers would wish to learn.

For something simple like capitalizing words, I would recommend you use Notepad++ with the TextFX plugin installed. It'll handle most common search & replace tasks by utilizing regular expressions under the hood (see, us programmers usually handle this complex stuff so you don't have to). In your case, with the TextFX plugin installed, you would simply highlight your text and navigate to TextFX --> TextFX Characters --> Proper Case. This will correctly convert "JOHN JAMES-RIDER SMITH" to "John James-Rider Smith." The TextFX plugin for Notepad++ offers dozens of tools for quick text adjustments (mostly aimed towards programmers' needs).

Regular expressions are powerful, but I would recommend letting something like TextFX handle them for you, unless you're willing to get a headache or two learning the basics of writing your own regex patterns (you can confirm this with Bullet Magnet).
Research shows that one in three Clinton supporters are just as stupid as the other two.
Zorro
Squad Member
 
Posts: 543
Joined: February 5th, 2011, 12:25 pm

Re: (PHP) A Few OO Examples

Postby BlackCat » January 30th, 2013, 12:16 am

Great information, Zorro. Thank a lot! I definitely need to try that.
User avatar
BlackCat
 
Posts: 1674
Joined: February 12th, 2011, 12:05 pm


Return to El Zorro's casa de la Codes

Who is online

Users browsing this forum: No registered users and 26 guests

cron