obligatory dev tools post

9/09/2008

I’ve been blogging now for a few months (albeit quite sparsely!) and I think it’s about time I write the obligatory dev tools post. These are tools that I use day in, day out and couldn’t imagine developing without. If you have any which you would like to add, please do so in the comments below…

editor/ide

Texpad – love its flexibility and simplicity. Does everything I need, never crashes and has loads of settings.

ftp

Smart FTP – ok, so you have to pay for it, but it really is the best ftp client out there.

honourable mention

FileZilla – a nice, simple, free solution. Seems to update all the time, which is a bit annoying, but other than that a very nice alternative to smart FTP.

database management

PHPMyAdmin – Was torn between this and Navicat for the main spot, but went for this because it’s free.

honourable mention

Navicat – This has some really nice features and is a lot more enterprisey than PHPMyAdmin, but then you have to pay for it :(

browser

Firefox – at the time of writing Google Chrome has just been released and so far it looks good, however firefox is still number 1. Everything about it just feels right, and the add ons take it to a different level.

honourable mention

Google Chrome – just released, but looks by far the next best thing after firefox, and who knows, could even surpass firefox one day (Tangent: isn’t google developing a browser a bit of a conflict of interests, considering the money they plough into firefox?!)

firefox extensions

Web Developer – the mother of all firefox webdev extensions. If you’re a web developer and you don’t have this plugin, you may as well go home.

Firebug – Another plugin I couldn’t live without. The inspect function is awesome, the javascript console and debugging is incredible, again this is a must have.

HTML Validator – Validates pages as they load and has some nice features like tidying up the html.

Color Zilla – Lets you instantly get any colour from any web page with the handy colour picker.

Screengrab – Allows you to easily take a screengrab of the page, visible portion or just a selection.

YSlow – A plugin for firebug this allows you to see how fast your page is and where you could improve.

Delicious Bookmarks – Allows me to easily bookmark pages and also gives me access to my bookmarks at the click of a mouse.

Bloglines Notifier – A bit naughty this one, but when you love bloglines as much as me it makes sense.

version control

Tortoise SVN – If you’re using subversion then you need to have this gui tool (if working in windows).

regex

Regex Builder – There are loads of online ones, but I like this one for its simplicity and quickness. Shame it requires the .net framework though.

misc

Stickies – Very useful when having to log time. These handy sticky notes sit on your desktop so you can easily record what your working on and between what hours.

Ok, so that’s my list. If you would like to add to it, or feel there’s a tool out there which you can’t believe I don’t use then please point me toward it.

2 Comments

useful function of the day

12/08/2008

An equivalent javascript function to php’s urlencode() is escape()

No Comments

to vi or not to vi

30/07/2008

In the process of setting up a home development server. Basically want a debian box with all the necessary stuff loaded on to it, so any personal projects or freelance stuff I can do on a decent box, rather than develope with a wamp setup.

Anyways, during my setting up of the server, I managed to mess things up, so I couldn’t boot up into it. I knew exactly what the problem was and knew the exact file I needed to change. Luckily, I manged to use the installation disk to boot me into a command line (one which isn’t just readable!).

The command line was limited to only the basics, naturally. However, little did I know that the only editor I could use (or so it seemed) was vi. Now I have never used vi before and can safely say, they I do not plan on using it ever again.

it’s frickin crazy!

I know there are lots of vi fans out there and they would probably berate me (if anybody every manged to find and read this blog!), but it’s the most retarded editor I’ve ever come across. It took me the best part of an hour to remove just two words from a file, an hour!

I’m sure their are perfectly good reasons why it does things in such a crazy way, but it confused the shit outta me. Things I learned from using vi:

  1. to delete a character it is not backspace or delete, oh no, its the letter x
  2. to move the cursor from left to right and up and down its the keys h,j,k and l
  3. You need to be in command mode to navigate around the file, and interactive mode to edit
  4. i will do all i can to ensure I never have to use vi again!

Give me nano, pico, emacs, joe anyday.

3 Comments

redesign season

30/07/2008

Not sure why, but it seems to be the season for a re-design. Two of the sites I visit most have just gone or under a major re-design. The sites in question are last.fm and facebook.

last.fm

last.fm beta
I must say, when I intially saw the beta of last.fm, I was horrified. I feared the worse and it seemed that they had created a really bad version of the facebook design.

Fortunately, the feedback they received must have sunk in, and they changed the design into what greets us today, and I must say that the new design is a cracker.last.fm new design

I love the simplicity, elegance and originality of it. Bravo to the last.fm team. I’m sure I will now be spending even more time on there, when I should be doing work!

facebook

I never really had an opinion on the old facebook design, as it wasn’t much of a design as such, but then isn’t that the beauty of it? The site isn’t really about the design but about the functionality, so it is a good thing if I don’t notice the design. They say in a football match that if you don’t notice the referee then he is having a good game, well I think it’s true for the design for sites like facebook too.

The new facebook continues this trend and tidies some things up quite nicely. I’m still unsure of the home page, as some of the story feeds on the homepage seem a bit lost amongst all the white space. However, I think the other parts of the site really excel under the new design. No longer do I need to go to a profile page, only to be smacked in the face with annoying applications telling me to write on their funwall or become a zombie.

The sectioning of all this information and lumping all the apps together on one page is a welcome and much needed addition. The fact that the feed take prominence on the profile page is good, although I do miss the wall as in just a wall of comments.new facebook design

All in all I think it’s a worthy redesign, but will take some getting used to.

conclusion

I’m sure the owners are facebook and last.fm are glad that my opinion is now out there, how they slept at night not knowing my thoughts on the new design I do not know. However, to sum up, I think that the designs are neat, tidy and clean, which is fine by me. Another site not mentioned in this article (now it has been! :P ) which has recently gone under the design knife, is the bbc website. Despite the web 2.0 gimmickery on the homepage, they also went for a clean, big and easy to read format, and I must say that I’m all for it. I tend to flick through the web at a really fast rate, so anything which makes my experience fast and easy is ok by me.

No Comments

truncate text snippet

17/07/2008

Just a quick snippet to truncate a string to a certain length (in this case 200 chars) and append … at the end of it.

<?=(strlen($my_string > 200) ? substr($my_string,0,200).'...' : $my_string?>

If you are using smarty you can use the somewhat simpler truncate modifier

{$my_string|truncate:200}
2 Comments

rounding money

14/07/2008

What a pain in the ass this turned out to be. When rounding money, how should it be rounded?

Rounding as I know it will round up at .5 and over and down on .4 and under. This works great for most thing, but not so great when dealing with something like cash money.

The problem is that if you are a penny out for one quantity of a product then this can get multiplied depending on multiple quantities. For example:

£75.75 net price is £89.00625 gross price.

This will get rounded to 89.01 pence, then when this gets multiplied for quantities greater than 1, the one pence will get multiplied.

I’m open for best ways to deal with this, what I did was store the gross price and net price for each product, so we have the same gross and net no matter what, and then use this, so if there is any rounding issues it happens only once and does not get multiplied. There may however, be better ways of doing this, I am aware that there is more than one way of rounding, and maybe another method will solve these problems when dealing with money.

1 Comment

dodgy looking source code

4/07/2008

Due to an update to the SyntaxHighlighter Plus plugin, the source code is going to overlap the sidebar for a few days, as I won’t have chance to fix it at all this weekend :\

No Comments

the joys of .net, soap and php

17/06/2008

The first letter in the acronymn SOAP is supposed to stand for Simple. Although that has been altered in the 1.2 version of the protocol, I cannot help but smile at the irony. My first encounter with using SOAP was using php to interface with a .NET web service. I can certainly say that this was anything but simple.

Using PHP 5′s Soap extension it appeared really easy to call the remote methods from the web service and in actual fact, it was. What I found really time consuming and frustrating was the untold .NET quirks which you had to account for and program around. Any documentation out there was poor and if not for the comments on the php.net documentation I would still be struggling with it.

First, a few rules you must stick to:

  • You can only pass objects to the remote methods
  • Parameters must be passed as SoapVar objects

The other thing which took me a while to work out was how to pass the username and password into the header, again the documentation on this was poor. What you need to do is set a SoapHeader object which you then need to set in the client using the __setSoapHeaders method.

Finally, I have put together an example which will hopefully help you get started when you need to access a .net web service through php. The headers stuff is only needed if the web service you are trying to access requires it.

// Specify your wsdl file.
$wsdl = WSDL_URL;

// Create our parameter class, this is needed for .NET compatibilty
class Params {
}

// Create our auth header class, this is needed for .NET compatibility
class AuthHeader{
}

// Instantiate our auth header class and set the username and password
$a = new AuthHeader;
$a->Username = USERNAME;
$a->Password = PASSWORD;        

/* Create our soap headers, this takes the namespace of the web service
    The name of the remote method which is used for authorisation and finally
    the object with the login details */
$headers = new SoapHeader(NAMESPACE, REMOTE_AUTH_METHOD, $a);

// Call try calling the soap client
try {
    $client = new SoapClient($wsdl, array('classmap'=>array('REMOTE_METHOD'=>'Params'),'trace'=>1,'exceptions'=>0));

    // Set the soap headers
    $client->__setSoapHeaders($headers);

   // Build our paramaters for our remote method call
    $p = new Params;

    $p->PARAM1 = new SOAPVar(VALUE, null);
    $p->PARAM2 = new SOAPVar(VALUE, null);

    // Try to call the remote method
    try {

        $result = $client->REMOTE_METHOD($p);

    } catch (SoapFault $exception) {

        echo 'Error: <br />';
        echo $exception->getMessage();
    }

} catch (SoapFault $exception) {
	echo 'Error: <br />';
	echo $exception->getMessage();
}
2 Comments

commidea payment class

9/06/2008

Update: I have a new version of this which uses the SOAP method and integrates 3D secure as well. If you are interested in this, leave a comment and I can send you a copy, if enough people are interested I’ll post it. Cheers.

Having recently integrated the commidea payment gateway into an online shop, I thought I would share the class in which I created to do this. I searched high and wide to find a class that was already out there but could not find one. So hopefully this will prove useful to some of you.

There are a few ways you can use the commidea gateway, you can use SOAP for example. This example uses Curl, so make sure that you have Curl installed on your server.

Please let me know if you find this class useful.

/* Class to deal with commidea payments

        Developed by Ian Jenkins - www.Jenkins-Web.co.uk

 	Usage:

	// Create a new object passing card details into the constructor.  This should be an array of details such as type, number etc.
	$payment_obj = new CommideaPayment($card_details);							

	// Validate the card
	$error = $payment_obj->validateCard();				

	// Ok, if we have no errors we can move on
	if (empty($error)) {		

		// Set the order id/reference
		$payment_obj->setOrderID($order_id);

		// Set the total for the order
		$payment_obj->setOrderTotal($order_total);

		// Make the payment
		$payment_response = $payment_obj->makePayment();					

		// The response will be either true or false, depending on whether the payment was successful or not
		if ($payment_response) {

			// Payment was successful, do what you need to do, probably update order and send the user to a success page.

			// To get the transaction id you can call this method.
			$payment_obj->getTransactionID()

			// If you are going to redirect the user to a success message, you will probably want something like:
			header("Location: /success/");
			exit;

		} else {

			// Payment was unsuccessful, do what you need to do, probably display an error message and give the user
			// a chance to enter alternative payment details.

			$error = 'Sorry, your payment was unsuccessful, please try alternative payment details.';
		}
*/

class CommideaPayment {

	// Define our commidea params
        // Change if not in testing mode
	private $post_url = 'https://testing.commidea.com';
        // Your account id
	private $account_id = 'XXXXXXXXX';
        // Your account number
	private $account_number = 'XXX';
        // Your GUI ID
        private $gui_id = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX';
        // Change to N to take payment as well as authorise
        private $auth_only = 'Y';

	public $card = array();
	public $order_id;
	public $order_total;

	public $transaction_id;

	/* Take in an array of card details, should have the following elements (elements with * are required):
		$card['type']*
		$card['number']*
		$card['security_code']*
		$card['expiry_month']*
		$card['expiry_year']*
		$card['start_month']
		$card['start_year']
		$card['issue']
	*/
	function __construct($card) {

		if (is_array($card)) {
			$this->card = $card;
		} else {
			die('Card details must be an array');
		}

	}

	// Some setters
	function setOrderID($order_id) {
		$this->order_id = $order_id;
	}

	function setOrderTotal($order_total) {
		$this->order_total = $order_total;
	}

	// Some getters
	function getTransactionID() {

		return $this->transaction_id;
	}

	function getCardType() {			

		return $this->card['type'];
	}

	function getCardRef() {

		// Return the card number with stars for all the digits except the last 4.
		return str_pad('', strlen($this->card['number'])-4, 'X').''.substr($this->card['number'], -4);

	}

	/* Validate our card.
		- Ensure card number is long enough and matches the Luhn formula
		- Ensure they have not entered an expired card!
		- Ensure the security code is only 3 digits
	*/
	function validateCard() {

		$err = array();

		// Required fields array fieldname=>label
		$required_fields = array('type'=>'Credit card type',
									'number'=>'Credit card number',
									'security_code'=>'Security code',
									'expiry_month'=>'Expiry month',
									'expiry_year'=>'Expiry year');

		foreach ($required_fields as $field=>$label) {

			// Have they filled in a credit card type?
			if (empty($this->card[$field])) {

				$error[] = '<strong>'.$label.'</strong> is missing.';
			}

		}

		/* Lets check the credit card number now
			- before we do anything, strip whitespace from the card number */
		$card_number = str_replace(' ','',$this->card['number']);

		if (!empty($card_number) && $card_number < 13) {

			$error[] = '<strong>Cred card number</strong> is not long enough.';
		}

		/* Ensure the card number is valid using the Luhn formula */
		if (!empty($card_number)) {

			$is_valid = $this->_validateCreditCardNumber($card_number);

			if (!$is_valid) {
				$error[] = '<strong>Invalid card number</strong>, check your card.';
			}
		}		

		// Ensure card has not expired!
		if (($this->card['expiry_month'] < date(m)) && ($this->card['expiry_year'] == date(Y))) {

			$error[] = 'Credit card has <strong>expired</strong>';
		}				

		// Ensure security code is numeric and only 3 digits
		if ($this->card['securitycode']) {

			if (!is_numeric($this->card['securitycode'])) {

				$error[] = '<strong>Security code</strong> is invalid.';
			}

			if (strlen($this->card['securitycode']) != 3) {

				$error[] = '<strong>Security code</strong> is invalid.';
			}
		}

		// If a maestro card is used then make sure they have also provided an issue number
		if ($this->card['type'] == 'Maestro' && empty($this->card['issue'])) {

			$error[] = '<strong>Issue No.</strong> is missing.';
		}	

		return $error;

	}

	/* Function to attempt to make a payment.  Will post the data to commidea using curl and see what response we get */
	function makePayment() {

		// First lets get our data
		$data = $this->_buildData();

		// Get our response from commidea
		$response = $this->_postData('cctransaction', '/cctransaction.asmx/CardTxn', 'CommideaTransaction', $data['cctransaction']['#data']);				

		// Assume payment was rejected unless told otherwise
		$result = "REJECTED";

		if (!isset($response["xml_array"]["TransactionResult"]) ) {

			die('Curl error');
		} else {

			$result = $response["xml_array"]["TransactionResult"];
		}				

		// Approved - Success!
		if ($result == 'ACCEPTED')  {			

			// Capture Transaction ID
			$transaction_id = $response["xml_array"]["TransactionID"];

			$this->transaction_id = $transaction_id;

			return true;

		} elseif ($result == 'REJECTED') {

			// Payment was rejected :( 

			// Capture Transaction ID
			$transaction_id = $response["xml_array"]["TransactionID"];

			$this->transaction_id = $transaction_id;

			return false;

		} elseif ($result == '0') {

			// Something went wrong...

			// Capture Transaction ID
			$transaction_id = $response["xml_array"]["TransactionID"];

			$this->transaction_id = $transaction_id;

			return false;
		}		

	}

	// Function to build the data we require for the commidea transaction
	private function _buildData() {

		$data = array(
			'payerauth' => array(
				'#page' => "/payerauth.asmx/CardCheck",
				'#xmlroot' => 'CommideaPayerAuthCheck',
			),
			'cctransaction' => array(
				'#page' => "/cctransaction.asmx/CardTxn",
				'#xmlroot' => 'CommideaTransaction',
				'#data' => array(
					'Merchant' => array(
						'GUID' => $this->gui_id,
						'MerchantData' => $this->order_id,
					),
					'TRecord' => array(
						'AccountID' => $this->account_id,
						'AccountNumber' => $this->account_number,
						'TxnType' => '01',
						'Pan' => $this->card['number'],
						'ExpiryDate' => ($this->card['expiry_month']).(substr($this->card['expiry_year'],2,4)),
						'TxnValue' => $this->order_total,
						'CSC' => ($this->card['security_code']),
						//'AVS' => '',
						'Issue' => ($this->card['issue']),
						'StartDate' => ($this->card['start_month']).(substr($this->card['start_year'],2,4)),
						'Reference' => $this->order_id,
						'CNP' => 'Y',
						'ECom' => 'Y',
						'AuthOnly' => $this->auth_only
					),
				),
			),
			'confirmtxn' => array(
				'#page' => "/cctransaction.asmx/ConfirmTransaction",
				'#xmlroot' => 'CommideaConfirmation',
			),
			'serverstatus' => array(
				'#page' => "/serverstatus.asmx/GetServerStatus",
				'#xmlroot' => NULL,
			),
		);		

		return $data;
	}

	/* Using curl, lets post the data to commidea and return the response */
	private function _postData($service, $page, $xmlRoot = NULL, $xml = array()) {

		//Validate the service
	  	if (!in_array($service, array("payerauth", "cctransaction", "confirmtxn", "serverstatus"))) {

	  		die("Invalid Service");
	  	}

	  	//If there IS an XML root, grab the XML using the recursive function below and create an XML output string
	  	if ($xmlRoot && is_array($xml)) {

			$output  = '<?xml version="1.0" ?>' . "\n";
			$output .= '<'.$xmlRoot.' xmlns="https://www.commidea.webservices.com">' . "\n";
			$output .= $this->_getXML($xml);
			$output .= '</'.$xmlRoot.'>';

	  	} else {

			//Otherwise, output is blank.
			$output = '';
	  	}	  		  	

	  	//Set the headers as an array
	  	$header[] = "Host: testing.commidea.com";
	  	$header[] = "Content-type: application/x-www-form-urlencoded";

	  	//If the output is not empty, set the xmlStream
	  	if (empty($output)) {

			$header[] = "Content-length: 0 \r\n";

	  	} else {

			$header[] = "Content-length: ".strlen('xmlStream=' . $output) . "\r\n";
			$header[] = "xmlStream=" . $output;
	  	}	  			  		  	

	  	// Create a curl object
	  	$ch = curl_init();
	  	curl_setopt($ch, CURLOPT_URL, $this->post_url.$page);
	  	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	  	curl_setopt($ch, CURLOPT_TIMEOUT, 10);
	  	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
	  	curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
	  	curl_setopt($ch, CURLOPT_HTTPHEADER, $header);	  		  

	  	// Execute the curl and save the result
	  	$returned = curl_exec($ch);	  

	  	// Lets tidy up the xml a bit
	  	if (curl_errno($ch) == 0) {

			$p = xml_parser_create();

			xml_parser_set_option($p, XML_OPTION_CASE_FOLDING, 0);
			xml_parse_into_struct($p, $returned, $returnedxml, $returnedxmlindex);
			xml_parser_free($p);

			$temp = array();

			foreach ($returnedxml as $key => $value) {

			  if ($value['type'] == 'complete') {

				$temp[$value['tag']] = (isset($value['value']) ? $value['value'] : '');
			  }

			}

			$returnedxml = $temp;

			unset($temp);

	  	} else {

			$returnedxml = array();
	  	}		  		  	

	  	$res = array('curl_error' => curl_errno($ch), 'xml_array' => $returnedxml);	  		  	

	  	return $res;
	}	

	// Validate credit cards using the LUHN formula
	private function _validateCreditCardNumber($cardnumber) {

		/* Credit card LUHN checker - coded '05 shaman - www.planzero.org        *
		 * This code has been released into the public domain, however please    *
		 * give credit to the original author where possible.                    */

		$cardnumber=preg_replace("/\D|\s/", "", $cardnumber);  # strip any non-digits
		$cardlength=strlen($cardnumber);
		$parity=$cardlength % 2;
		$sum=0;

		for ($i=0; $i<$cardlength; $i++) {

			$digit=$cardnumber[$i];
			if ($i%2==$parity) $digit=$digit*2;
			if ($digit>9) $digit=$digit-9;
			$sum=$sum+$digit;
		}

		$valid = ($sum%10==0);

		return $valid;
	}

	/* Recursive XML creator.
		- Pass an array into $data. It loops around each element in the array. If $value is not an array,
		- It prints it out using the $key as the element tag.
		- If $value IS an array, the function calls itself with the $value array as the new tree with an incremented depth.
		- The $depth is used to tab indenting to make the XML human friendly. Option in the future to add an arg to make
			code computer friendly by making it all one line.
		- Returns the xml as a string.
	*/
	private function _getXML($data = array(), $depth = 1) {

		$output = "";

	  	foreach ($data as $key => $value) {

			if (is_array($value)) {

			  $output .= str_repeat("  ", $depth) . "<$key>\n";
			  $output .= $this->_getXML($value, $depth+1);
			  $output .= str_repeat("  ", $depth) . "</$key>\n";

			} elseif (empty($value)) {

			  $output .= str_repeat("  ", $depth) . "<$key />\n";

			} else {

				$output .= str_repeat("  ", $depth) . "<$key>$value</$key>\n";

			}
	  	}

	  	return $output;
	}	

}
2 Comments

obscure credit card numbers

5/06/2008

Handy little function to have when developing a shop. If you wish to store or display obscured credit card numbers. It will replace all number in the credit card number with X’s apart from the last 4.

	function getObscuredCreditCardNumber($card_number) {

		// Return the card number with X's for all the digits except the last 4.
		return str_pad('', strlen($card_number)-4, 'X').''.substr($card_number, -4);

	}
No Comments