4 |
rodolico |
1 |
By itself, the users class (with a data access class like the included UsersDataSourceMySQLi class) handles basic login/logout/editing functions.
|
|
|
2 |
|
|
|
3 |
The class(es) were, however, designed for extensibility and customization in mind. Some design considerations were made with this in mind.
|
|
|
4 |
|
|
|
5 |
This code uses the ternary and null coelescing shortcuts ?: and ??. I THINK these were introduced in PHP 5.3, but not sure. This code will not work on versions which do not have these shortcuts. See https://www.php.net/manual/en/language.operators.comparison.php
|
|
|
6 |
|
|
|
7 |
=== Basic System ===
|
|
|
8 |
|
|
|
9 |
With no modification, the system will store username, password and two booleans, isAdmin and enabled. The default table is created as
|
|
|
10 |
|
|
|
11 |
<code sql>
|
|
|
12 |
create or replace table _users (
|
|
|
13 |
_user_id int unsigned not null auto_increment,
|
|
|
14 |
login varchar(64),
|
|
|
15 |
password varchar(128),
|
|
|
16 |
isAdmin boolean,
|
|
|
17 |
enabled boolean,
|
|
|
18 |
primary key (_user_id)
|
|
|
19 |
);
|
|
|
20 |
</code>
|
|
|
21 |
|
|
|
22 |
* login is filtered to alpha-numerics and the underscore character
|
|
|
23 |
* password is stored as a hash using PHP's password_hash
|
|
|
24 |
* users with isAdmin set will be able to add/edit other users
|
|
|
25 |
* users with enabled set to false (0) will not be able to log in
|
|
|
26 |
|
|
|
27 |
NOTE: the UsersDataSourceMySQLi class has a public function, buildTable, which will build the table, so installation involves simply calling that function.
|
|
|
28 |
|
|
|
29 |
IMPORTANT: to allow the Users class to work with a wide variety of data types, it does no data access itself. It requires a data access class.
|
|
|
30 |
|
|
|
31 |
Basic use in a script involves instantiating a data access class object, then instantiating a Users class object.
|
|
|
32 |
|
|
|
33 |
Example:
|
|
|
34 |
|
|
|
35 |
<code php>
|
|
|
36 |
<?php
|
|
|
37 |
include_once( 'UsersDataSourceMySQLi.class.php' );
|
|
|
38 |
include_once( 'Users.class.php' );
|
|
|
39 |
session_start();
|
|
|
40 |
$connection = new usersDataSource(
|
|
|
41 |
null,
|
|
|
42 |
null,
|
|
|
43 |
array( 'username' => 'test', 'password' => 'test', 'database' => 'test' )
|
|
|
44 |
);
|
|
|
45 |
//$connection->buildTable( 'admin', 'admin' ); die;
|
|
|
46 |
// ensure we always have a (possibly invalid) instance of user
|
|
|
47 |
if ( ! isset( $_SESSION['user'] ) ) {
|
|
|
48 |
$_SESSION['user'] = new Users( );
|
|
|
49 |
}
|
|
|
50 |
?>
|
|
|
51 |
<html>
|
|
|
52 |
<body>
|
|
|
53 |
<div class="login">
|
|
|
54 |
<?php
|
|
|
55 |
if ( isset( $_SESSION['user'] ) )
|
|
|
56 |
print $_SESSION['user']->HTML($connection);
|
|
|
57 |
?>
|
|
|
58 |
</div>
|
|
|
59 |
</body>
|
|
|
60 |
</html>
|
|
|
61 |
</code>
|
|
|
62 |
|
|
|
63 |
This example is using the UsersDataSourceMySQLi definition of data access (included)
|
|
|
64 |
|
|
|
65 |
If you run it the first time with <code php>$connection->buildTable( 'admin', 'admin' ); die;</code> uncommented, it will build the table. Comment that line out on the next run and you will be presented with a login screen.
|
|
|
66 |
|
|
|
67 |
Class function HTML() displays various things to allow login, then quits displaying anything. Setting $_REQUEST['logout'] = 1 before calling HTML() will initiate a log out which will destroy the session variable
|
|
|
68 |
|
|
|
69 |
=== Full Functionality ===
|
|
|
70 |
|
|
|
71 |
Calling the class method admin and displaying the repeatedly will generate output allowing the user to change their username, password and any other fields which do not have the 'restrict' attribute set to true
|
|
|
72 |
|
|
|
73 |
If the user has admin rights set, it will also display a list of logins and allow you to select one to edit or, add a new user. Editing someone else shows all fields, whether or not the 'restrict' attribute is set to true.
|
|
|
74 |
|
|
|
75 |
=== CSS ===
|
|
|
76 |
|
|
|
77 |
I tried to not put any HTML layout into the code, relying instead on CSS. Everything is supposed to have a class and be wrapped in a <div> with a class. Following are the classes I have in the code (I THINK).
|
|
|
78 |
* login_field = This is the class of all INPUT fields, and div's surrounding them
|
|
|
79 |
* login_form = the class for all <form definitions
|
|
|
80 |
* login_list = the class of the unsigned list (ul) which displays the links to edit other users for admin's
|
|
|
81 |
* login = NOT in code, but used in example as a div around the div around the login area
|
|
|
82 |
|
|
|
83 |
=== Extended Functionality ===
|
|
|
84 |
|
|
|
85 |
First, everything is pretty basic. I tried to limit the number of fields to the absolute minimum, but also set it up to allow additional fields to be added programmatically. The example does this.
|
|
|
86 |
|
|
|
87 |
If you open the class source, you'll find the private member $dbDefinition, which defines everything in the code (I hope). When you create a new instance, you can pass the constructor an array which will be merged with this member, optionally increasing the number and definition of the fields.
|
|
|
88 |
|
|
|
89 |
WARNING: if you add new fields to the Users class, you must also add them to class UsersDataSourceMySQLi. See below
|
|
|
90 |
|
|
|
91 |
Let's add a new field, say we want to store the users e-mail address. In our PHP, create an array as follows:
|
|
|
92 |
|
|
|
93 |
<code php>
|
|
|
94 |
$customFields = array(
|
|
|
95 |
'tables' => array(
|
|
|
96 |
'users' => array(
|
|
|
97 |
'fields' => array(
|
|
|
98 |
'email' => array(
|
|
|
99 |
// == For Users class ==
|
|
|
100 |
// this will be the display label on the form
|
|
|
101 |
'label' => 'E-Mail',
|
|
|
102 |
// the input type to use for data entry
|
|
|
103 |
'html type' => 'text',
|
|
|
104 |
// you can only edit this if an admin and changing someone
|
|
|
105 |
// else' record
|
|
|
106 |
'restrict' => false,
|
|
|
107 |
// will be displayed on a hover in HTML5 (ie, title=)
|
|
|
108 |
'instructions' => 'Enter your e-mail address (WARNING, not verified)',
|
|
|
109 |
// this is entered in an empty box, ie placeholder=
|
|
|
110 |
'hint' => 'E-Mail Address',
|
|
|
111 |
// a regex to run it against to verify it is ok
|
|
|
112 |
'filter' => '/[-_a-z0-9.]+@[_a-z0-9]\.[a-z0-9]/i',
|
|
|
113 |
|
|
|
114 |
// == for Data Source ==
|
|
|
115 |
'dbColumn' => 'email',
|
|
|
116 |
// actual mySQL column type
|
|
|
117 |
'type' => 'varchar(64)',
|
|
|
118 |
// set it to not null if we build the table ourselves
|
|
|
119 |
'required' => false
|
|
|
120 |
)
|
|
|
121 |
)
|
|
|
122 |
)
|
|
|
123 |
)
|
|
|
124 |
);
|
|
|
125 |
?>
|
|
|
126 |
</code>
|
|
|
127 |
|
|
|
128 |
Now, when we instantiate a new object of class Users AND class UsersDataSourceMySQLi, we simply pass this array in.
|
|
|
129 |
|
|
|
130 |
<code php>
|
|
|
131 |
$connection = new usersDataSource(
|
|
|
132 |
null,
|
|
|
133 |
$customFields,
|
|
|
134 |
array( 'username' => 'test', 'password' => 'test', 'database' => 'test' )
|
|
|
135 |
);
|
|
|
136 |
if ( ! isset( $_SESSION['user'] ) ) {
|
|
|
137 |
$_SESSION['user'] = new Users( $customFields );
|
|
|
138 |
}
|
|
|
139 |
</php>
|
|
|
140 |
|
|
|
141 |
Note that since we replicated the basic structure of $dbDefinition in Users and UsersDataSourceMySQLi, we can use the same hash to pass into both; they will store, but ignore values not relevant to them.
|
|
|
142 |
|
|
|
143 |
When the usersDataSource and Users objects are created, $customFields will be merged, with duplicates overwritten by the $customFields value.
|
|
|
144 |
|
|
|
145 |
This is not limited to adding new columns; you can modify the display definitions also, ie how the information is stored on the screen, to some extent.
|