· 7 years ago · Apr 24, 2018, 01:32 PM
1<?php
2/**
3* every 'data edit' form has one of these - without exeception.
4*
5* This ensures that the form I sent out came from me.
6*
7* It has:
8* 1) A unique @id
9* 2) A date time stamp and a lifetime
10*
11* Can be automatically generated and checked.
12*/
13
14class FormState {
15
16 const MAX_FORM_AGE = 600; // seconds
17
18 const ENC_PASSWORD = '327136823981d9e57652bba2acfdb1f2';
19 const ENC_IV = 'f9928260b550dbb2eecb6e10fcf630ba';
20
21 protected $state = array();
22
23 public function __construct($prevState = '')
24 {
25 if (!empty($prevState)) {
26 $this->reloadState($prevState); // will not be valid if fails
27 return;
28 }
29
30 $this->setNewForm();
31 }
32
33 /**
34 * Generate a new unique id and timestanp
35 *
36 * @param $name - optional name for the form
37 */
38 public function setNewForm($name = '')
39 {
40 $this->state = array();
41 $this->state['formid'] = sha1(uniqid(true)); // each form has a unique id
42 $this->state['when'] = time();
43
44 if (!empty($name)) {
45 $this->setAttribute('name', $name);
46 }
47 }
48
49 /**
50 * retrieve attribute value
51 *
52 * @param $name attribute name to use
53 * @param $default value to return if attribute does not exist
54 *
55 * @return string / number
56 */
57 public function getAttribute($name, $default = null)
58 {
59 if (isset($this->state[$name])) {
60 return $this->state[$name];
61 } else {
62 return $default;
63 }
64 }
65
66 /**
67 * store attribute value
68 *
69 * @param $name attribute name to use
70 * @param $value value to save
71 */
72 public function setAttribute($name, $value)
73 {
74 $this->state[$name] = $value;
75 }
76
77 /**
78 * get the array
79 */
80 public function getAllAttributes()
81 {
82 return $this->state;
83 }
84
85 /**
86 * the unique form id
87 *
88 * @return hex string
89 */
90 public function getFormId()
91 {
92 return $this->getAttribute('formid');
93 }
94
95 /**
96 * Age of the form in seconds
97 * @return int seconds
98 */
99 public function getAge()
100 {
101 if ($this->isValid()) {
102 return time() - $this->state['when'];
103 }
104 return 0;
105 }
106
107 /**
108 * check the age of the form
109 *
110 *@param $ageSeconds is age older than the supplied age
111 */
112 public function isOutOfDate($ageSeconds = self::MAX_FORM_AGE)
113 {
114 return $this->getAge() >= $ageSeconds;
115 }
116
117 /**
118 * was a valid string passed when restoring it
119 * @return boolean
120 */
121 public function isValid()
122 {
123 return is_array($this->state) && !empty($this->state);
124 }
125
126 /** -----------------------------------------------------------------------
127 * Encode as string - these are encrypted to ensure they are not tampered with
128 */
129
130 public function asString()
131 {
132 $serialized = serialize($this->state);
133 $encrypted = $this->encrypt_decrypt('encrypt', $serialized);
134
135 $result = base64_encode($encrypted);
136 return $result;
137 }
138
139 /**
140 * Restore the saved attributes - it must be a valid string
141 *
142 * @Param $prevState
143 * @return array Attributes
144 */
145 public function fromString($prevState)
146 {
147 $encrypted = @base64_decode($prevState);
148 if ($encrypted === false) {
149 return false;
150 }
151
152 $serialized = $this->encrypt_decrypt('decrypt', $encrypted);
153 if ($serialized === false) {
154 return false;
155 }
156
157 $object = @unserialize($serialized);
158 if ($object === false) {
159 return false;
160 }
161
162 if (!is_array($object)) {
163 throw new \Exception(__METHOD__ .' failed to return object: '. $object, 500);
164 }
165 return $object;
166 }
167
168 public function __toString()
169 {
170 return $this->asString();
171 }
172
173 /**
174 * Restore the previous state of the form
175 * will not be valid if not a valid string
176 *
177 * @param $prevState an encoded serialized array
178 * @return bool isValid or not
179 */
180 public function reloadState($prevState)
181 {
182 $this->state = array();
183
184 $state = $this->fromString($prevState);
185 if ($state !== false) {
186 $this->state = $state;
187 }
188
189 return $this->isValid();
190 }
191
192 /**
193 * simple method to encrypt or decrypt a plain text string
194 * initialization vector(IV) has to be the same when encrypting and decrypting
195 *
196 * @param string $action: can be 'encrypt' or 'decrypt'
197 * @param string $string: string to encrypt or decrypt
198 *
199 * @return string
200 */
201 public function encrypt_decrypt($action, $string)
202 {
203 $output = false;
204
205 $encrypt_method = "AES-256-CBC";
206 $secret_key = self::ENC_PASSWORD;
207
208
209 // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
210 $secret_iv_len = openssl_cipher_iv_length($encrypt_method);
211 $secret_iv = substr(self::ENC_IV, 0, $secret_iv_len);
212
213 if ( $action == 'encrypt' ) {
214 $output = openssl_encrypt($string, $encrypt_method, $secret_key, OPENSSL_RAW_DATA, $secret_iv);
215
216 } else if( $action == 'decrypt' ) {
217 $output = openssl_decrypt($string, $encrypt_method, $secret_key, OPENSSL_RAW_DATA, $secret_iv);
218 }
219
220 if ($output === false) {
221 // throw new \Exception($action .' failed: '. $string, 500);
222 }
223
224 return $output;
225 }
226}