Wednesday, March 4, 2009

Problems with 404 error, Query string, ASP and international (Hebrew) characters

EDIT: Fixed a typo in the code where I had "QUERY_STRING" instead of "HTTP_URL"

Today I had to move a website for a client. The website is very old, and is written in ASP, and was hosted on a shared host.

The former developer wanted to have SEO friendly URLs but didn't have a URL Rewrite module available.

What he did was making a custom 404 error page, that would redirect the SEO URL and take care of the error if the URL shouldn't be redirected. As part of the script, he took the QUERY_STRING and processed it like so:

Urlstring = Request.ServerVariables("QUERY_STRING")

Response.Write UrlString
Response.Write URLDecode( UrlString )


Long story short, when moved, this code started to result in a very weird behavior. The QUERY string of international URL's turned to "???" question marks. After checking the settings for IIS, and making sure all the correct code pages are set, I found out that the behavior is changing depending if the text was before or after the '?' in the URL.

For instance, the following URL:
http://www.example.com/something.asp?something=something

If you replaced 'something.asp' with international characters, they would turn to question marks, but if you replaced 'something=something' with international characters they would display properly.

After some trial and error the solution was to use "HTTP_URL" instead of "QUERY_STRING" and strip out a part of it, like so:

Urlstring = Request.ServerVariables("HTTP_URL")

Set RegularExpressionObject = New RegExp
With RegularExpressionObject
.Pattern = "\/err404\.asp\?"
.IgnoreCase = True
.Global = True
End With
UrlString = RegularExpressionObject.Replace(UrlString, "")
Set RegularExpressionObject = nothing

Response.Write UrlString
Response.Write URLDecode( UrlString )


Of course, this is just a quick and dirty RegExp example, and I wouldn't advise using RegExp because they're expensive on memory and resources, but I think you're good to go from here.

Any other ideas? Feel welcome to ask questions or comment!

Friday, February 20, 2009

What content would you like to see next?

Hey guys. I was just wondering - what content would you like to see posted next? Is there any particular subject of interest or question on anyone's mind?

Feel free to answer in the comments...

Tuesday, February 17, 2009

Don't wanna be Obama's Elf

How to update a single field in CakePHP

Here's a quick line of code to demonstrate how to update a single field in CakePHP.

Suppose we will be updating a field named 'confirmed' in a model called 'User' with the id of 5. We will then have an array with just that value, like this:


$data = array(
'User' => array(
'id' => 5,
'confirmed' => 1
)
);


We should then pass it on to a save function:

$this->User->save( $data, false, array('confirmed') );


You can RTFM about the save() function here.

Wednesday, February 11, 2009

How to undelete text files on linux

Today was a hard day. Someone accidentally deleted a bunch of files with code containing the last two days' work.

Fortunately, all the files were successfully recovered. I'll share the way that worked here with you.

First of all, the moment you lose your text data, make sure that if you can, you unmount the drive the files were on, so you won't overwrite the lost data.

Then you will have to run a utility called strings. Basically what it does is extracting all string data from it's input, and outputs it to STDOUT or whatever output you specify.

We will tell it to read our partition on the hard drive the lost files were. (Learn more about Linux partitions) and output it to a file on another partition.

So the command should look something like this:
strings /dev/sda1 > /some/path/for/output.txt


Note that '/dev/sda1' should be replaced by your partition path, and you should provide a path to where you want the output file to be. (NOTE: AVOID WRITING IT TO THE SAME PARTITION AT ANY COST. THE RESULTING FILE WILL BE HUGE, MORE THAN 10GB, AND YOU DON'T WANT TO OVERWRITE YOUR PRECIOUS DATA!).

After the process finishes (takes quite a while), you should have a HUGE text file that contains ALL of the text from your hard drive, deleted or not.

Now, basically all you need to do is to grep and locate the text you lost inside the whole mass.

Hope it helps you! It sure helped me!

Tuesday, February 10, 2009

Linux - working with filenames and directories starting with a - (dash)

I use SVN daily on my Linux development server, but today I ran into a silly problem that took me some figuring out.

If you try to work with filenames that start with - (dash) such as "-filename.ext", you will get errors such as:
svn: invalid option character: i
Type 'svn help' for usage.


After some looking around, the solution for it was, instead of:
svn rm -filename.ext

To pass '--' as the first argument:
svn rm -- -filename.ext


Hope it helps anyone out there.

Wednesday, June 25, 2008

Do It Yourself - Plugins (PHP Tutorial)

Today I would like to share with you a quick way to have a plug-in engine running at your site. I came up with this concept quickly a few days ago when I needed to make an extensible layer for one of our projects.

Today we are going to achieve some fundamental functionality of a plug-in system. Please note that any of the external classes used in this tutorial's code can be replaced with only a few lines of code. I was too lazy to do so while copy-pasting my code. And of course you can comment and ask for help.

First of all, let's define the functionality we expect from our system:

  • The plugin creator shouldn't have to touch any code other than his plugin
  • There should be minimal installation needed
  • The plugin should be standalone and deployable to any system using our framework without any hassle


Sounds hard? It isn't. First of all, let me walk you through a few key concepts we are going to exercise while creating this system. I am basing all of this on the presumption that you already are familiar with object oriented programming, if you are not, I advise you to fill in the gaps

Static Keyword


So, you are probably familiar with classes and objects, and you know that to use a class, you first must instantiate it using the new keyword.

You also probably know that every time you create an object of the class' type, you are creating all the methods and properties, and they belong to that specific object.

Basically, when you put the keyword static before a property or method definition, you are declaring that this method or property will belong to the class type itself, rather than instances of it (objects), and therefore be common to all the instances of the same class, and be callable without even creating an instance of a class

Let me give you a quick example.

class className{
static private $variable = 25;
function get_variable(){
return className::$variable;
}

function __construct( $inputNumber = null ){

if(!is_null( $inputNumber )){
className::$variable = $inputNumber;
}
}
}

// We create the first instance. Since we don't give a new value, it stays 25, like
// in the initialization
$firstInstance = new className();
// We create a second instance, this time we change the variable to 45
$secondInstance = new className(45);
// We create a third instance
$thirdInstance = new className();


// Since the variable is static, the change we did in the second function affects
// all of them, because the variable belongs to the class itslef, and not a separate
// instance of it
// All the three functions will output: 45
echo $firstInstance->get_variable();
echo $secondInstance->get_variable();
echo $thirdInstance->get_variable();


Now that you know what the static keyword is, let us move to the next thing you need to know before attempting to write a plugin engine.

Dynamic Access & Creation


This is a very controversial feature in PHP. And some will argue (and will be right in a way) that it shouldn't be there (its just like the argument about strongly typed and weakly typed languages). But since the feature is there, why not make use of it?

Simply enough, PHP lets you call functions and classes dynamically. For instance, you can dynamically decide which class to create.

$variable = 'myClass';
$myObject = new $variable();

The code above will create an instance of the class 'myClass'.

Also, PHP lets us call functions and methods dynamically.

$functionName = 'multiply';
// Same as doing: multiply( 5, 2 );
$functionName( 5, 2 );

// Same as doing: multiply( 5, 2 );
call_user_func( 'multiply', 5, 2 );

// Same as doing: $myObject->myMethod( 'some text' );
call_user_func( array( 'myObject', 'myMethod' ), 'some text' );

// Same as doing: myStaticClass::myMethod( 'some text' );
call_user_func( array( 'myStaticClass', 'myMethod' ), 'some text' );


Let's Get Started


Oh, I almost forgot, we were going to build a plugin engine... Well, let's get started.

First of all, before writing any code, I always figure out what I'm going to make. Then we figure out how, and then after I have everything in mind, I start writing some actual code.

The first planning stage is pretending to be a user of our to-be-written module, and making a quick list of how the final product will act.

So for our plugin system it will be:

  • All the plugins reside in a plugins/ subdirectory of our website. Each in it's own directory.
  • There will be a class that handles all the plugins, and registers them to the system.
  • A plugin can be turned off.
  • The plugin installation should be as simple as copying it to the plugins/ directory, and turning it on.


Then, after some thinking, I decided that I will go with this kind of code architecture:

  • Plugin engine is a fake-singleton class (Read some stuff about actual singleton classes. By fake-singleton, I mean that it is an absolutely static class of which you can't create instances, but it isn't a traditional singleton class (which actually has one instance of a class)
  • Each plugin is also a static class. Why did I choose static classes? Code readability and aesthetics mainly. As well as ease of implementation.
  • Each class has a corresponding table entry in MySQL, that tells us if we enabled the plugin or not.
  • There are multiple "hook checkpoints" where plugins can hook in their code.


Plugin Engine


Basically, what I'll do next here is give the final code, copy-pasted right off my application, and then explain what needs explanation. Please note that you can't just copy-paste my code as it won't work right away. You will need to replace a few lines of code. Nothing too bad.

plugin.class.php
This is the heart of the plugin system. It checks the plugins directory for any plugins, validates them with our database, and then registers them to be ran at the hook checkpoints.

// This bit checks if we are currently
// running inside our website. It prevents
// from overly curious people to launch
// this php file from outside of our script
if( !defined( "INPROCESS" ) ){
header("HTTP/1.0 403 Forbidden");
die();
}

// Those two are classes that I wrote
// to simplify access to mysql and other
// databases. You will have to delete those
// two lines, and write your own code
// to access mysql
require_once( 'data.class.php' );
require_once( 'data.mysql.class.php' );


// This is the parent class for all plugins
// It contains a private constructor, that
// basically makes sure we won't have any
// instances of our static classes. Making
// them completely static.
class plugin{
private function __construct(){}
}

// This is the actual plugin class.
class pluginClass{
// This will be the list of active plugins
static private $plugins = array();

// Again, we don't want any instances
// of our static class.
private function __construct(){}

static function initialize(){
// I have those variables elsewhere in
// a config.php file. you can replace
// those with your own
global $config_fullpath;
global $config_username;
global $config_password;
global $config_server;
global $config_database;

$list = array();
// Populate the list of directories to check against
if ( ($directoryHandle = opendir( $config_fullpath . '/plugins/' )) == true ) {
while (($file = readdir( $directoryHandle )) !== false) {
// Make sure we're not dealing with a file or a link to the parent directory
if( is_dir( $config_fullpath . '/plugins/' . $file ) && ($file == '.' || $file == '..') !== true )
array_push( $list, $file );
}
}


// Get the plugin list from MySQL. Note that you will have to replace
// this code with your own, since I'm using classes that I didn't
// include in this article. Fortunately, it won't be too much of a problem.

// Connect to mysql
$mysqlConnection = new mysqlConnection( $config_username, $config_password, $config_server, $config_database );
// We select all the plugins from our database
// Each plugin has it's name stored, and whether
// it is active or not (active = 0 or 1)
$mysqlConnection->prepareQuery( 'SELECT * FROM plugins' );
$results = $mysqlConnection->executeQuery();
$results = $mysqlConnection->resultToObjects( $results );
$newResult = array();



// Create an array: 'plugin name' = 'active' (1 or 0)
foreach ( $results as $result ){
$newResult[$result->name] = $result->active;
}

// Register the active plugins
foreach( $list as $plugin ){
if($newResult[$plugin] == "1"){
pluginClass::register( $plugin );
}
}
}

// Hook the active plugins at a checkpoint.
// You will see exactly how it works later on.
static function hook( $checkpoint ){
// Cycle through all the plugins that are active
foreach(pluginClass::$plugins as $plugin){
if(!call_user_func( array( $plugin, $checkpoint ) ))
// Throw an exception if we can't hook the plugin
throw new Exception( "Cannot hook plugin ($plugin) at checkpoint ($checkpoint)" );
}
}

// Registration adds the plugin to the list of plugins, and also
// includes it's code into our runtime.
static function register( $plugin ){
global $config_fullpath;
require_once( $config_fullpath . "/plugins/$plugin/$plugin.class.php" );
array_push( pluginClass::$plugins, $plugin );
}
}


index.php

// Tell the class it is safe to run
define( "INPROCESS", true );

// My configuration file
require_once( './config.php' );
// Our plugin class
require_once( './include/plugin.class.php');

// Initialize our plugin engine
pluginClass::initialize();
// Create a new hook point called "onLoad"
// Now all the plugins that can be hooked here
// will be ran.
pluginClass::hook( "onLoad" );


A Sample Plugin
A sample plugin will reside in it's own directory inside plugins/. For example plugins/helloworld/. Then we need to go (or have a script that does it automatically. Homework for you...) to our database and add it, with its name there and active = 1. That's it, we're done with installing our plugin!

plugins/helloworld/helloworld.class.php


if( !defined( "INPROCESS" ) ){
header("HTTP/1.0 403 Forbidden");
die();
}
// We inherit the parent class plugin to make sure we will have no accidental instances of this class.
class helloworld extends plugin {
// That's right. We create a function with the name
// of our hook, and walla! We have a plugin hooked at
// onLoad.
static function onLoad(){
print 'Hello World!';
return true;
}
}


That's it. I am sorry for not doing this one step by step, but if you have any questions, you are welcome to comment. Of course, I didn't include the whole code here. You can create an installer that installs classes automatically, you can make sure there is no trash entries in MySQL by disabling the non existent plugins automatically, but one article won't be enough for that.

I hope it got your gray matter all buzzing and running, and now you'll create some awesome plugin systems for whatever project you're working on.

You're welcome to comment!!!