As someone who is new to OOP I have two questions :
How do you pass in variables from forms through classes, ie when you do procedural PHP you would just do something like:
$name = $_POST[‘name’];
So how would I do it in OOP just as a quick guide ?
Does anyone know of a good PHP 5 OOP book for beginners/intermediate users, I can only afford 1 at the moment and they are pretty expensive at £30 each!! ?
There are many different ways to pass user input to objects. I’ll outline 3 possible methods: Procedural, Inheritance and Encapsulation.
First of all, lets start with a base class:
class Person{
protected $Name, $Email;
function __Construct(){
$Name = '';
$Email = '';
}
function setName($newName){
if(preg_match('[A-Za-z\\s]+', $newName)){
$this->Name = $newName;
return true;
}else{
return false;
}
}
function setEmail($newEmail){
if(preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$/', $newEmail)){
$this->Email = $newEmail;
return true;
}else{
return false;
}
}
function getName(){
return $this->Name;
}
function getEmail(){
return $this->Email;
}
}
So, first of all there is putting it in procedurally:
//first, of course, verify the post has been sent, and then:
$Person = new Person();
$Person->setName($_POST['name']);
$Person->setEmail($_POST['email']);
The second way is to extend the object to a form-based object:
class PersonInputLoader extends Person{ //for lack of a better name
public function __Construct(){
parent::__Construct();
}
public function loadInputFromArray($Array){
return ($this->setName($Array['name']) && $this->setName($Array['email']));
}
public function loadInputFromPost(){ return $this->loadInputFromArray($_POST); }
public function loadInputFromGet(){ return $this->loadInputFromArray($_GET); }
public function loadInputFromSession(){ return $this->loadInputFromArray($_SESSION['person']); }
}
//Then, in your main code:
$Person = new PersonInputLoader();
$Person->loadInputFromPost();
Another way is to encapsulate the object within another:
class PersonInputLoader{ //for lack of a better name
public $Person;
function __Construct(Person $Person){
$this->Person = $Person;
}
function loadInputFromArray($Array){
return ($this->Person->setName($Array['name']) && $this->Person->setName($Array['email']));
}
function loadInputFromPost(){ return $this->loadInputFromArray($_POST); }
function loadInputFromGet(){ return $this->loadInputFromArray($_GET); }
function loadInputFromSession(){ return $this->loadInputFromArray($_SESSION['person']); }
}
//Then, in your main code:
$Person = new Person();
$InputLoader = new PersonInputLoader($Person);
$InputLoader->loadInputFromPost();
//Data is now stored in $Person.
There are many, many other ways of doing this - some probably much better. Those are just the ideas that spring to mind at the moment
As for books, well Sitepoint’s PHP Anthology is pretty damn good.
Well, that is to be fair the least OOP way of doing it, but if you’re uncomfortable with the others then, by all means, choose the one you find most appealing.
You would put that code after you have verified that a form submission has been made. Unfortunately the ways to check for form submission differ vastly depending on your site structure and application pathway.
If, for example, you have a single file that checks for all form submissions, you could have:
My post above was not to tell you how to apply it to your situation - as a developer that is solely your job.
Simply apply what I showed you. You’ve started with a base class - User - then feed the information in at the correct point in the application, using whichever method you choose.
WhiteKnight - the script that defines a class, should usually be different to the one that creates an object out of it, and actually uses it. An object is an instance of a class.
So if Process.php is the file that you’ve defined your class, there should be some other script that does something like this:
You can also use constructor with arguments or magic methods:
<?php
class Person
{
public $name;
public $email;
// constructor is a method that is executed when you create
// a new instance of this class (object)
// it can also take arguments
public function __construct($name,
$email)
{
$this->name = $name;
$this->email = $email;
}
// magic method __set helps you access object properties
// with $object->property = 'foo' syntax which probably seems more
// natural to you when you are used to procedural syntax
public function __set($property, $value)
{
$this->property = $value;
}
// the same as __set but this one returns a property
// with echo $object->property suntax
public function __get($property)
{
return $this->property;
}
}
$person = new Person('John Doe',
'john@doe.com');
echo $person->name,
'<br />',
$person->email;
// we can add a new property via magic method __set (called overloading in PHP)
$person->foo = 'bar';
echo '<br />',
$person->foo;
i have found youtube has some simple and good OOP php video tutorial.
Go and look at it.
The video called “professional registration in php” even answers your question
I have read most of the book that is written for oop php,i can say dont buy them for now.They dont teach you PHP OOP rather they just discuss general OOP and some syntax and examples of OOP php which can be read from manual itself…no real examples and application creation…
if you someday decide to invest some money in it,go for lynda php beyond the basics videos…though its techniques are not full proof,it will get you started and provide you confidence atleast to build some application in OOP php
I went and had a search for the above named video, but none came up with that actual title. What did come up though were a series of videos called: “PHP Tutorial: Professional Registration System”
If this is the video tutorial that you mean, I have some issues that I have with how things are done in the video. I hope I’m not being too picky.
[list][]The form uses a table-based layout. for username/password
[]The form method is specified in uppercase, where lowercase is the standard convention
[]It isn’t explained why he prefixes field names with the letter r, for example, ruser, rpassword
[]Session tokens are used, which make it impossible for those without cookies to proceed any further
[]He is using direct references to global variables, such as $_POST, which make it impossible to create an effective test harness
[]MD5 for the password? crypt is considered to be a much better password mechanism
[]The form inputs are filtered with a generic regular expression. filter_input is more effective at this task.
[]The register method assumes that the values being entered are already sanitised. For better protection, the values should be sanitised by the database methods themself, preferably by using the equivalent of prepared statements.
[]The new user is always being inserted into the database. What if the user name already exists?
[]If statements fail to use curly braces for their statements
[]Class methods use echo instead of returning strings back to their caller.
[]Inline line-breaks are used (they’re only for poetry and addresses) when an unordered list is preferable.[/list]
[list][*]The valid_data assumes that the errors array start as being empty. It will fail to return the right value if any errors are added before the valid_data method is called
[*]Several methods returns 0 or 1, when false or true are the intended meaning instead
[*]The process method doesn’t take in to account that the database entry might fail. The process method should not return that the errors array is empty, its return should incorporate the success from the register method as well.
[*]Checking for a valid email, he’s using a simple regular expression, when filter_var or filter_input have proper FILTER_VALIDATE_EMAIL filters already
[*]He’s using a case-insensitive match with eregi as well as a-zA-Z within the expression itself! laugh
[*]The regular expression forces the top-level domain to be 2 to 4 characters. Anyone from a museum or travel top-level domain will automatically fail, for example, info@about.museum
[*]He’s advising that “you do not need to pass any variables, because when you submitted they’re global”. Aargh![/list]
[list][*]He demonstrates that john@com is considered an invalid, but fails to realise that his code will consider john@…com to be valid
[*]He realises that there is no user_exists method, which is good, then just copy/pastes the database method code, including the database connect code, which is bad. Don’t Repeat Yourself is a well-known maxim.[/list]
Yes thats it
You will make a good investigater…lol
any way YES i agree his security methods and style are not full proof ,infact i was about to make a comment there in youtube “OK what actually is professional here in this video”
but having said that i think it answers users first question
How do you pass in variables from forms through classes, ie when you do procedural PHP you would just do something like:
$name = $_POST[‘name’];
So how would I do it in OOP just as a quick guide ?
so i posted it.
Plus youtube has many php oop videos,so it should have given the user place to start and search for other similar videos there.
Most imp thing it is free.
There are not many free OOP PHP videos,even the one i said “lynda php:beyond the basics” is not out of flaw.Infact i dont like the way the author includes all the files in start of every file there,which makes it difficult to be implemented in real world but atleast that videos doesnt limit you to defination of some functions and OOP terminologies.It make a full working php example.
Yes,that true.
So,usually i start by watching tutorial videos first then going to book and then going to manuals.
There are lots of Apress books which has OOP examples
but problems with it
1)They are not meant for beginners(work best for other pro experts who are migrating)
2)They usually dont teach you,rather they show how the author does the particular task(you have to try to learn from there )
3)Some of codes doesnt work when implemented as said
4)They are bulky ,i dont know why do they put so many pages plus it is usually expensive…
5)Now,Modern OOP books teach u specific framework now like cakephp,zend rather than general OOP PHP.(for eg,if u use zend its auth does every thing for you,and starter may misunderstand or may not practice actual OOP php)
Most of the books being written now are still procedural oriented ,may be using some new functions of PHP 6.
Infact when i bought a book called PHP 6 AND MYSQL5,even it was not OOP.The word “php6” should have meant atleast some OOP.
Use standard paragraphs within the form, (or fieldset where appropriate) and use labels that have an implicit association with their field. This allows us to use CSS to style them effectively.
With ruser and rpass, it might be possible that he’s distinguishing them as the registerUser and registerPassword instead, so we’ll use those for less confusion.
I was being too picky, here as there are other ways for the server to track a session. But to make up for it, he need to stop using short tags for his PHP code.
So that we can test the results of different $_POST values, we can either pass them directly to the class, or or provide a means by which they can be obtained, such as from a helper class that is passed to the constructor instead.
As we are likely to want to access post variables and filter them, we can place those methods within a helper class (which follows the DRY principle), instead of having them exist in multiple classes.
PHPUnit and SimpleTest are both very effective testing mechanisms that you can use. The general premise being, write a test that initially fails, then write the code to make it pass.
The comments about this video are continued in the next post.
Here is a simple Helper class (called Helper.php) that I’ve quickly run up for this example. It is capable of being easily expanded to handle array-like values, and other situations as well.
<?php
class Helper
{
private $_post;
private $_session;
public function __construct()
{
$this->_post = $_POST;
$this->_session = $_SESSION;
}
private function getStrippedValue($var, $default)
{
if (get_magic_quotes_gpc() === 1) {
$var = stripslashes($var);
}
if (strlen($var) < 1) {
$var = $default;
}
return $var;
}
public function post($key, $default = '')
{
$value = $default;
if (isset($this->_post[$key])) {
$value = $this->_post[$key];
}
return $this->getStrippedValue($value, $default);
}
public function session($key, $default = '')
{
$value = $default;
if (isset($this->_session[$key])) {
$value = $this->_session[$key];
if (strlen($value) < 1) {
$value = $default;
}
}
return $value;
}
}
?>
In order for the above Helper class to be more generically useful, it would also want a “get” method and a “cookie” method. Both are trivial to add, given the existing code.
Here is the Register class constructor, where we also pass in a $db class that we’ll cover shortly too.
Notice that the username, password and email aren’t stored in the class any more.
The task of the constructor is get the class ready to be used. This is not the place to handle the sanitization of form data.
Here is how the helper class helps us to get the form values, so that they are properly processed in the one place.
The data values being passed to the filter method are now unescaped without magic quotes. This is how they should be, as magic quotes will not exist with PHP6. When the database methods insert the data, it is at that point when the values should be properly handled to protect the database.
crypt is capable of several different types of encryption. Here we are using an always random string as the salt, for better encryption.
Here is the MySQLDB class that I created just for this exercise. If it were to be taken further, it could be with an Interface template, so that other database classes can be created that are accessed in the same way.
<?php
class MySQLDB {
private $_link;
public function __construct($localhost, $user, $password, $database)
{
$link = $this->connect($localhost, $user, $password);
if (!empty($database)) {
$this->selectDB($database, $link);
}
$this->_link = $link;
}
private function connect($localhost, $user, $password)
{
$link = mysql_connect($localhost, $user, $password);
if ($link === false) {
die('Could not connect: ' . mysql_error());
}
return $link;
}
private function selectDB($database, $link)
{
$dbSelected = mysql_select_db($database, $link);
if ($dbSelected === false) {
die ("Can't use $database : " . mysql_error());
}
return $dbSelected;
}
public function escape($value, $type) {
switch ($type) {
case 'text':
$value = mysql_real_escape_string($value, $this->_link);
break;
case 'integer':
$value = intval($value);
break;
default:
throw new Exception('invalid DB parameter type: ' . $type);
}
return $value;
}
public function queryOne($sql) {
$data = false;
$result = mysql_query($sql, $this->_link);
if ($result !== false) {
$row = mysql_fetch_array($result);
if ($row !== false && isset($row[0])) {
$data = $row[0];
}
}
return $data;
}
public function queryRow($sql) {
$data = false;
$result = mysql_query($sql, $this->_link);
if ($result !== false) {
$row = mysql_fetch_array($result);
if ($row !== false) {
$data = $row;
}
}
return $data;
}
public function queryCol($sql, $field_offset) {
$data = false;
$result = mysql_query($sql, $this->_link);
if ($result !== false && $field_offset < mysql_num_rows($result)) {
$data = array();
while (true) {
$row = mysql_fetch_field($result);
if ($row === false) {
break;
}
array_push($data, $row[$field_offset]);
}
}
return $data;
}
public function queryAll($sql) {
$data = false;
$result = mysql_query($sql, $this->_link);
if ($result !== false) {
$data = array();
while (true) {
$row = mysql_fetch_array($result);
if ($row === false) {
break;
}
array_push($data, $row);
}
}
return $data;
}
public function exec($sql) {
$success = false;
$result = mysql_query($sql, $this->_link);
if ($result !== false) {
$success = (mysql_affected_rows() > 0);
}
return $success;
}
}
?>
With the above class, making database queries is a simple matter of calling a single class method.
private function register($username, $encryptedPassword, $email)
{
$db = $this->_db;
$username = $db->escape($username, 'text');
$encryptedPassword = $db->escape($encryptedPassword, 'text');
$email = $db->escape($email, 'text');
$success = $db->exec(
'INSERT INTO users'
. ' (username, password, email)'
. " VALUES ('$username', '$encryptedPassword', '$email')"
);
if (!$success) {
$this->addError('Could Not Process Form');
}
return $success;
}
Notice too that when creating an error, we can make good use of a class method to consistantly perform the job for us.
private function addError($message)
{
array_push($this->_errors, $message);
}
Not only that, but the regular expression that was being used allowed a lot of bad emails to get through.
PHP has built in to it an email filter. We can use that, as well as a string filter for the username and password. These filter types to ensure that the don’t contain any “bad” things, which includes HTML tags.
Instead of echoing out the text from the class, we really should return it instead, so that the caller of the method can decide what they ant to do with the information.
By enclosing the errors in an div called “errors”, we can fiddle about with the paragraph spacing so that we can do things to it, such as give the paragraphs no space at all.
We can easily make the valid data method more accurate by remembering how many errors there were at the start of the method.
A separate method will be used for the counting. This achieves two aims. First it removes the need for the valid data method to kow about the error array, and it makes it easier for us to move addError, countErrors and getErrors to a separate class at some later stage.
The filter_var function has a good FILTER_VALIDATE_EMAIL which returns false if it’s not a valid email address.
At the end, instead of returning 0 or 1, we can be more expressive by returning an equality match, which means the method naturally returns either true or false. The condition is placed in brackets to help remind us about that when we read the code,
private function countErrors()
{
return count($this->_errors);
}
...
private function validData($data)
{
$startingErrors = $this->countErrors();
if (empty($data['username'])) {
$this->addError('Invalid Username');
} else if ($this->userExists($data['username'])) {
$this->addError('Username Already Taken');
}
if (empty($data['password'])) {
$this->addError('Invalid Password');
}
if (filter_var($data['email'], FILTER_VALIDATE_EMAIL) === false) {
$this->addError('Invalid Email');
}
return ($this->countErrors() === $startingErrors);
}
After the process method has retrieved the form values, we really shouldn’t use the error list to determine if there are any errors. Instead, the only true measure on whether the process has been successful or not is when the information is successfully entered into the database.
public function process()
{
$success = false;
...
if ($this->validToken($token) && $this->validData($data)) {
$success = $this->register(
$data['username'],
$encryptedPassword,
$data['email']
);
}
return $success;
}
Before the form is shown on the page, this is the code to bring the different classes together, and to process the form data.
This is neatly resolved now by using filter_var with a filter type of FILTER_VALIDATE_EMAIL
if (filter_var($data['email'], FILTER_VALIDATE_EMAIL) === false) {
This is now resolved by using a separate set of code to work with the database.
The userExists method is:
private function userExists($username)
{
$db = $this->_db;
$username = $db->escape($username, 'text');
$data = $db->queryOne(
'SELECT id'
. ' FROM users'
. " WHERE username = '$username'"
);
return !empty($data);
}
and the check is performed from within the validData function.
if (empty($data['username'])) {
$this->addError('Invalid Username');
} else if ($this->userExists($data['username'])) {
$this->addError('Username Already Taken');
}
While these series of posts weren’t intended to replace the code from the tutorial, that’s what these posts have ended up doing.
It is possible to take the information from these recent posts and recreate the working code for the register page.
At some later stage, the information may even be gathered together in a proper tutoral format.
This topic has been covered at length in the HTML forum and [url=“http://simplebits.com/notebook/2003/09/16/simplequiz-part-vi-formatting/”>other places. An ordered list implies that there is a similar semantic grouping between each list item, and that there is meaning in the ordering of the list, which need not apply between name, password and email address. You would do better by using an ordered list for each paragraph of a section of text, but we know the uselessness of that idea.
A definition list is a good choice though, and warrants further investigation, if it weren’t for the conflict of both the dt and label performing the same but similar job as each other, and there’s the whole issue of the unstyled presentation which must have attention paid to.