PHP Membership Provider

The basics

Now no Membership Provider would be complete without a User object. And there are shared properties that users and posts, comments etc… all have, and that’s usually an isApproved boolean as well as a createdDate and modifiedDate which could each be named slightly differently, but they all have the same meaning. So instead of duplicating these properties in the other classes, I figured I’d create an Entity class and have it inherited by User et al.

<?php

class Entity {

	public $createdDate;

	public $modifiedDate;

	public $isApproved;
	
	public function __construct( $data = array() ) {
		
		if ( isset( $data['createdDate'] ) ) $this->createdDate = $data['createdDate'];
		if ( isset( $data['modifiedDate'] ) ) $this->modifiedDate = $data['modifiedDate'];
		if ( isset( $data['isApproved'] ) ) $this->isisApproved = $data['isApproved'];
	}
	
	public function Save( $data = array() ) {
		$this->__construct( $data );
	}
}

?>

And User :

<?php

class User extends Entity {
	public $userId;
	
	public $hash;
	
	public $username;
	
	public $password;
	
	public $email;
	
	public $avatar;
	
	public $bio;

	public $isLocked;
	
	public function __construct( $data = array() ) {
		
		parent::__construct( $data );
		
		if ( isset( $data['userId'] ) ) $this->userId = $data['userId'];
		if ( isset( $data['hash'] ) ) $this->userId = $data['hash'];
		if ( isset( $data['username'] ) ) $this->username = $data['username'];
		
		if ( isset( $data['password'] ) ) $this->password = $data['password'];
		
		if ( isset( $data['bio'] ) ) $this->bio = $data['bio'];
		if ( isset( $data['email'] ) ) $this->email = $data['email'];
		if ( isset( $data['avatar'] ) ) $this->avatar = $data['avatar'];
		
		if ( isset( $data['isLocked'] ) ) $this->isLocked = $data['isLocked'];
	}

	public function Save( $data = array() ) {
		$this->__construct( $data );
	}
}

?>

This is pretty rudimentary, but it shouldn’t be hard to modify this to fit other scenarios.

Now we need a class that will allow us to load the configuration stuff and provide us with a relevant scope for our settings. After all, it doesn’t make sense to give a future Role Provider the same settings as a Membership Provider. We may even want to give settings to other classes that don’t need to share configuration settings, so for this, I created an App class that will load it’s own configuration.

<?php

/**
 * Main application class.
 * Most core classes (membership, roles, repositories etc...) can inherit from this
 *
 * @package App
 */
class App extends Base {
	
	
	/**
	 * Class constrctor. All inheriting classes must call this.
	 * 
	 * By creating the PDO instance here, we can use separate databases for certain
	 * classes if necessary, although care must be taken to avoid joins between them.
	 *
	 * @param string $scope Settings scope name for the inheriting class
	 */
	public function __construct( $scope ) {
		
		$config = Config::getInstance();
		
		// Scope specific settings
		$this->props = $config->{$scope};
		
		// Globally accessible settings outside scope
		$data = $config->Database;	// Database settings
		$tables = $config->Tables;	// Table names
		
		// Table names
		$this->usersTable = $tables['tablePrefix'] . $tables['usersTable'];
		$this->rolesTable = $tables['tablePrefix'] . $tables['rolesTable'];
		
		// Create PDO instance
		$this->db = new PDO( 
				$data['dsn'], 
				$data['username'],
				$data['password'] 
			);
		
		$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
		
	}
	
	
	
	
	/********************************
	 * 	Helper Methods		*
	 ********************************/
	
	
	/**
	 * Prepares parameters for UPDATE or INSERT SQL statements.
	 * 
	 * E.G. For INSERT
	 * :name, :email, :password etc...
	 * 
	 * For UPDATE
	 * name = :name, email = :email, password = :password etc...
	 * 
	 * @param string $fields Comma and space delimited (", ") list of fields
	 * @param bool $insertMode Prepares statements for SQL INSERT (defaults to false)
	 */
	protected function SetParamSql( $fields, $insertMode = false ) {
		
		if ( $insertMode ) {
			
			return ":" . implode( ", :", explode( ", ", $fields ) );
			
		} else {
			
			$setField = function( $f ) {
				return "$field = :$field";
			};
			
			$v = array_map( $setField, explode( ", ", $fields) );
			return implode( ", ", $v );
			
		}
	}
}

?>

Now we can get down to the actual MembershipProvider class… but before we do, there’s one little thing to take care of.

The ASP.Net Membership Provider has a MembershipCreateStatus enumeration. I thought, why is this only for member creation? Wouldn’t it be helpful to have an enumeration for most statuses the provider can give rather than simply returning false on failure?

<?php

/**
 * Can't believe PHP doesn't have a proper "enum" type, but oh well...
 */
final class MembershipActionStatus {
	
	const Success = 0;
	
	const InvalidUsername = 1;
	
	const InvalidPassword = 2;
	
	const InvalidEmail = 3;
	
	const DuplicateUsername = 4;
	
	const DuplicateEmail = 5;
	
	const UserRejected = 6;
	
	const PasswordChangeFailed = 7;
	
	const UserUpdateFailed = 8;
	
	const AuthenticationFailed = 9;
	
	
	private function __construct() { }
}

?>

Now I can status a status by simply doing :

$status = MembershipActionStatus::Success;

Of course, this isn’t exactly an ideal workaround, but until there’s actually an elegant implementation of enum in PHP, this will have to do.

Onward to the actual MembershipProvider class

Advertisement

6 thoughts on “PHP Membership Provider

  1. Although I took a short introductory class for php I consider it, and all coding really, something akin to electricity – i.e. it’s not a hobby – only hire a licensed electrician! I love the idea of myself dabbling in code, but I would never think for myself to go whole hog and live with anything as the risks are too daunting. Nonetheless, I sometimes dream of buckling down and learning to code and creating some useful apps for my job or a cool site for my friends or self etc… Maybe someday.

    • Haha! That’s a great analogy :D

      But see there are people who do electrical work as a hobby too. Some people even build Tesla Coils, which can not only kill instantly, but also blow up spectacularly if everything isn’t perfect… but they’re oh-so-much-fun!

      If you’re considering diving in again, all I can say is stay away from w3schools. It’s really, really, bad!

      I think a lot of tutorials out there are just not intro-friendly. And a lot of people are taught with some really ugly, ugly code (in all languages) with no consideration of human nature. I.E. They jump right into programming techniques — some questionable — without covering basics first and that turns off a lot of people.

      We’re not robots… We use robots to work like robots.

      • Interesting as I’ve always heard of the usefulness of w3schools… But then again, maybe they are a reason I don’t bother progressing with coding. Homemade Tesla coils huh…? Now that’s interesting!

  2. I love this idea. I can’t tell you how many times I’ve written my own user authentication scheme, and typically it’s no more than a hard-coded hack (with the necessary input sanitation, of course!) with some super slap-dash session management thrown in. I would absolutely love a standardized, built-in package so I don’t have to rely on super complicated CMS implementations like Drupal / WordPress / CakePHP just to have a robust authentication and user management system.

    • Thanks!

      If you happen to find it useful I’d love to know about it. I’m also thinking of including a standard sanitization class. Probably something based on a class I’ve written before rather than reinventing the wheel.

      It’s pretty silly that we have to write more when we need less!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s