Getter és Setter metódusok generálása

A PHP-ben az objektumtulajdonságok beállítására van egy beépített lehetőség (__get() és __set() metódusok), azonban ezek nem típusbiztosak, így ajánlott saját getter és setter metódusokat írni a tulajdonságok kezelésére. Viszont ez igen időigényes feladat, és a Zend Studio-n kívül még nem találkoztam IDE-vel, ami megtenné helyettem, úgyhogy írtam egy kódot ennek a folyamatnak az automatizálására.

Ez a leírás elsősorben Windows felhasználóknak lesz hasznos, viszont a kódolás gyorsítására érdemes lehet kipróbálni a scriptet más rendszer alatt fejlesztőknek is.

Hogy teljesen világos legyen mindenki számára, hogy mit értek getter és setter metódusok alatt, íme egy példa:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
namespace valami\tartalom\menedzser;
/**
 * Egy tartalomkezelőben egy leírást reprezentáló osztály
 * @author BlackY
*/
class Content
{
	/**
	 * A leírás címe
	 * @var string
	*/
	private $title;
 
	/**
	 * A leírás beküldője
	 * @var \valami\tartalom\menedzser\felhasznaloKezeles\Felhasznalo
	*/
	private $author;
 
	/**
	 * A leírás szövege
	 * @var string
	*/
	private $text;
 
	/**
	 * A leíráshoz kapcsolt tagok
	 * @var array
	*/
	private $tags;
}

Az adattagokat az adatelrejtés okán priváttá tettük, viszont valahogy el kéne érni őket. Első próbálkozás lehet a __get() és __set() metódusok használata, azonban az nem típusbíztos: ha biztonságos kódot szeretnénk írni, mindig tesztelnünk kéne az author tulajdonság értékét, hogy null-e ill. objektum-e ill. a megfelelő osztály egy példánya-e.

Azonban ha biztosra tudjuk, hogy az author \valami\tartalom\menedzser\felhasznaloKezeles\Felhasznalo objektum (a sok \ az ún. névteret adja meg) akkor a plusz ellenőrzések feleslegessé válnak.

Ugyanez az osztály az összes getter/setter metódussal:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<?php
namespace valami\tartalom\menedzser;
 
class Content
{
	/**
	 * A leírás címe
	 * @var string
	*/
	private $title;
 
	/**
	 * A leírás beküldője
	 * @var \valami\tartalom\menedzser\felhasznaloKezeles\Felhasznalo
	*/
	private $author;
 
	/**
	 * A leírás szövege
	 * @var string
	*/
	private $text;
 
	/**
	 * A leíráshoz kapcsolt tagok
	 * @var array
	*/
	private $tags;
 
	/**
	 * Getter method of the title property
	 * @return string
	*/
	public function getTitle()
	{
		return $this->title;
	}
 
	/**
	 * Setter method of the title property
	 * @param string $value - The new value of the property
	 * @return valami\tartalom\menedzser\Content
	*/
	public function setTitle($title)
	{
		$this->title = (string)$title;
		return $this;
	}
 
	/**
	 * Getter method of the author property
	 * @return \valami\tartalom\menedzser\felhasznaloKezeles\Felhasznalo
	*/
	public function getAuthor()
	{
		return $this->author;
	}
 
	/**
	 * Setter method of the author property
	 * @param \valami\tartalom\menedzser\felhasznaloKezeles\Felhasznalo $value - The new value of the property
	 * @return valami\tartalom\menedzser\Content
	*/
	public function setAuthor(\valami\tartalom\menedzser\felhasznaloKezeles\Felhasznalo $author)
	{
		$this->author = $author;
		return $this;
	}
 
	/**
	 * Getter method of the text property
	 * @return string
	*/
	public function getText()
	{
		return $this->text;
	}
 
	/**
	 * Setter method of the text property
	 * @param string $value - The new value of the property
	 * @return valami\tartalom\menedzser\Content
	*/
	public function setText($text)
	{
		$this->text = (string)$text;
		return $this;
	}
 
	/**
	 * Getter method of the tags property
	 * @return array
	*/
	public function getTags()
	{
		return $this->tags;
	}
 
	/**
	 * Setter method of the tags property
	 * @param array $value - The new value of the property
	 * @return valami\tartalom\menedzser\Content
	*/
	public function setTags(array $tags)
	{
		$this->tags = $tags;
		return $this;
	}	
}

(A félig angol félig magyar dokumentáció rosszul néz ki, de két kattintással generáltam)

Ez utóbbi folyamatot veszi át a következő kód:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
<?php
/**
 * GS-Generator
 * This tool can be used to create getter and setter method stubs for any class.
 * Usage:
 *  php GS-Generator.php "class_file_name" <optinal flags>
 * Optional flags:
 * 	-c = Copy to clipboard instead of printing to file (Windows only, requires MS Word)
 *  -d = Debug mode - create assertions for setter function with native types
 *  -gv [public|private|protected] = The visibility of the getter methods
 *  -sv [public|private|protected] = The visibility of the setter methods
 * @version 1.0 beta
 * @author BlackY
*/
/**
 * The basic level of indention used (default: 1 tabs)
 * @var string
 */
$indention			= str_repeat("\t", 1);
/**
 * The visibility of getter methods
 * @var string
 */
$getterVisibility	= ($index = array_search('-gv', $argv)) !== false ? $argv[$index + 1] : 'public';
/**
 * The visibility of the setter methods
 * @var string
 */
$setterVisibility	= ($index = array_search('-sv', $argv)) !== false ? $argv[$index + 1] : 'public';
/**
 * A flag indicating whether to include assertions in the produces code
 * @var bool
 */
$includeAssert		= in_array('-d', $argv);
/**
 * A list of "black listed" types: those that require no type hinting.
 * Array is not listed here, as arrays are handled another way.
 * @var array
 */
$typeBlackList		= array(
	'int', 'integer',
	'bool', 'boolean',
	'float', 'double', 'real',
	'string',
	'callback', 'scalar', 'var', 'unknown'
);
/**
 * A list of native data types that need no casting
 */
$noCast				= array('callback', 'scalar', 'var', 'unknown', 'resource');
/**
 * The list of type checking functions to use in the assertions.
 * @var array
 */
$typeCheckFunctions = array(
	'int' => 'is_int', 'integer' => 'is_int',
	'bool' => 'is_bool', 'boolean' => 'is_bool',
	'float' => 'is_float', 'double' => 'is_float', 'real' => 'is_float',
	'string' => 'is_string',
	'resource' => 'is_resource',
	'callback' => 'is_callable', 'scalar' => 'is_scalar'
);
/**
 * The name of the input file
 * @var string
 */
$fileName = null;
/* Find the input file in the arguments */
foreach($argv as $arg) {
	if(basename(__FILE__) != basename($arg) && file_exists($arg) && is_readable($arg)) {
		$fileName = $arg;
	}
}
if(!$fileName) {
	die('Input file not specified or not found/readable!');
}
chdir(dirname($fileName));
/** Get the list of classes that need getter/setter methods */
$old_classes = get_declared_classes();
if(!include_once($fileName)) {
	die('Input file inclusion failed.');
}
$new_classes = get_declared_classes();
$classes = array_diff($new_classes, $old_classes);
if(!$classes) {
	die('No class definition in input file!');
}
/* Generate the methods */
foreach($classes as $class) {
	$reflection = new ReflectionClass($class);
	$source		= '';
	foreach($reflection->getProperties() as $property) {
		$name		= $property->getName();
		$docComment = $property->getDocComment();
		$type		= 'unknown';
		if(preg_match('/@var\s+(.*)\b/i', $docComment, $regs)) {
			$type = $regs[1];
		}
		$hasMethods = $reflection->hasMethod('get' . $name) && $reflection->hasMethod('set' . $name);
		$nonProtected = $property->isPublic(); 
		$isStatic = $property->isStatic();
		if($hasMethods || $nonProtected || $isStatic) {
			continue;
		}
		/* Getter method */
		$source .= $indention . '/**' . PHP_EOL;
		$source .= $indention . ' * Getter method of the ' . $name . ' property' . PHP_EOL;
		$source .= $indention . ' * @return ' . $type . PHP_EOL;
		$source .= $indention . '*/' . PHP_EOL;
		if(strtolower($type) == 'bool' || strtolower($type) == 'boolean') {
			$source .= $indention . $getterVisibility . ' function is' . ucfirst($name) . '()' . PHP_EOL;
		} else {
			$source .= $indention . $getterVisibility . ' function get' . ucfirst($name) . '()' . PHP_EOL;
		}
		$source .= $indention . '{' . PHP_EOL;
		$source .= $indention . "\t" . 'return $this->' . $name . ';' . PHP_EOL;
		$source .= $indention . '}' . PHP_EOL . PHP_EOL;
		/* Setter method */
		$source .= $indention . '/**' . PHP_EOL;
		$source .= $indention . ' * Setter method of the ' . $name . ' property' . PHP_EOL;
		$source .= $indention . ' * @param ' . $type . ' $value - The new value of the property' . PHP_EOL;
		$source .= $indention . ' * @return ' . $reflection->getName() . PHP_EOL;
		$source .= $indention . '*/' . PHP_EOL;
		$source .= $indention . $setterVisibility . ' function set' . ucfirst($name) . '(';
		$cast = '';
		$assert = ''; 
		if(in_array(strtolower($type), $typeBlackList)) {
			$source .= '$' . $name;
			if(!in_array(strtolower($type), $noCast)) {
				$cast = '(' . $type . ')';
			}
			if($includeAssert && isset($typeCheckFunctions[strtolower($type)])) {
				$assert = $indention . "\t" . 'assert(\'' . $typeCheckFunctions[strtolower($type)] . '($' . $name . ')\')' . PHP_EOL;
			}
		} elseif(strtolower($type) == 'array') {
			$source .= 'array $' . $name;
		} else {
			$source .= $type . ' $' . $name;
		}		
		$source .= ')' . PHP_EOL;
		$source .= $indention . '{' . PHP_EOL;
		$source .= $assert;
		$source .= $indention . "\t" . '$this->' . $name . ' = '. $cast . '$' . $name . ';' . PHP_EOL;
		$source .= $indention . "\t" . 'return $this;' . PHP_EOL;
		$source .= $indention . '}' . PHP_EOL . PHP_EOL;
	}
	$filename = dirname(__FILE__) . DIRECTORY_SEPARATOR . basename($reflection->getName() . '_generated.php');
	file_put_contents($filename, $source);
	if(class_exists('COM') && in_array('-c', $argv) && count($classes) == 1) {	
		try {
			$com = new COM('Word.Application');
			$com->Visible = false;
			$com->Documents->Add();
			$com->Selection->TypeText($source);
			$com->Selection->WholeStory();
			$com->Selection->Copy();
			$com->Quit(false);
			unlink($filename);
		} catch(Exception $x) {
			try {
				$com->Quit(false);				
			} catch(Exception $x) {}
		}
	}
}

A feladatunk össz-vissz annyi, hogy a fenti – első – Content osztálynak megfelelően leírjuk az osztályunkat: a docCommentekben legyen ott a @var direktíva.

Ezután – mindent alapbeállításon hagyva – a kód a

php GS-Generator.php myClassFile.php

utasítást kiadva létrehozza a metódusok forráskódját és elmenti az <osztálynév>_generated.php névvel létrehozott fájlokban. (Ha a bemeneti fájlban több osztály van, több ilyen fájl jön létre). Ezt copy-pastelve az osztály törzsébe, kész is vagyunk.

A kód automatikusan generál type hinteket az osztályokhoz és a tömb típushoz, a natív típusokat pedig az elhelyezésük előtt converálja (castolja) a megfelelő típusra. Ezzel az egyetlen probléma az lehet, ha egy tulajdonság vagy egy objektum vagy null értéket vehet fel (a fenti példában például már törölve lett a cikk írója vagy egyéb okból nem elérhető), a null érték miatt a PHP hibát ad. Ezt áthidaló bevezettem a var “típust”, ezt használva nincs castolás és típus súgás (sajnos típus biztonság sem). (Ezeket a setterek érdemes kézzel átírni, hogy is_nul() és instanceof-al eldöntsék, helyes-e az érték, mielőtt beírják az értéket) Újra átolvasva a PHP manualt észrevettem a kitételt, hogy a

<típusnév> $valtozo = null

paraméter-definíció esetén adható null érték a változónak. Az oldal végén levő új változat már használja ezt a lehetőséget.

A natív adattípusokon (bool, int, float, string) kívül használhatjuk még a scalar (bármelyik skaláris érték) illetve a callback (hívható) típusokat. Az elkészült docCommentekben találkozhatunk még az unknown típussal, ezt használja a rendszer, ha nem volt típus jelölve a tulajdonságnál.A lap végén levő frissített kód hiányzó típusmegadás esetén nem hoz végre settert/gettert.

Azokhoz a tulajdonságokhoz, amelyek már rendelkeznek getter és setter függvénnyel vagy (bármilyen oknál fogva) publikusak nem hoz létre metódusokat, így egy új tulajdonság felvitelekor csak az azok kezelését biztosító függvényeket hozza létre.

A fentieken felül még néhány további opciót is kezel a rendszer, ezek parancssori paraméterrel kapcsolhatók:

  • -gv [public|protected|private] – A getter metódusok láthatóságát állíthatjuk be
  • -sv [public|protected|private] – A setter metódusok láthatósága
  • -d – Debug mód: assert()-ek hozzáadása a kódhoz, így a natív adattípusok is ellenőrzésre kerülnek. Az éles szerverre kerülés előtt generáljuk újra a metódusokat a kapcsoló nélkül!
  • -c – Csak Windows rendszeren, telepített Microsoft Word-del (csak ez utóbbi COM objektumain keresztül tudok kommunikálni a vágólappal. Ha tudsz jobb megoldást, szólj!)! Az elkészült kódot nem fájlba menti, hanem a vágólapra másolja

Utóbbi opció azért került bele, hogy a php fájlok helyi menüjéhez hozzá tudjak rendelni egy új elemet, ami legenerálja a metódusokat és azok kódját a vágólapra teszi. Ezt a következő képpen tehetjük meg: indítsuk el a regedit-et. A bal oldali fában keressük meg a HKEY_LOCAL_MACHINE \ SOFTWARE \ Classes \ .php \ Shell kulcsot. (Ha valamelyik nem létezik, jobb klikk a szülőn, új, majd kulcs).

Itt hozzunk létre egy kulcsot azzal a névvel, amilyen nevet a helyi menüben látni szeretnénk. Nálam egy “Metódusok generálása GSGenerator-ral (Assert: On)” ill. “Metódusok generálása GSGenerator-ral (Assert: Off)” lett (két külön kulcs). A létrehozott kulcson belül hozzunk létre egy új kulcsot “Command” néven, majd kattintsunk rá.
A jobb oldalon az (Alapértelmezett) értékre kattintva írjuk be a parancsot, amit futtatni szeretnénk:

"C:\A php útvonala\php-win.exe" "C:\A GSGenerator.php fájl útvonala" [flagek] "%1"

A [flagek] helyére természetesen behelyettesítve a fentebb írt paramétereket. Nálam a két kulcs a következőképp néz ki:

Assert: On

"C:\Program Files\xampplite\php\php-win.exe" "C:\Documents and Settings\Rendszergazda\Asztal\GS-Generator.php" -c -d "%1"

és

Assert: Off

"C:\Program Files\xampplite\php\php-win.exe" "C:\Documents and Settings\Rendszergazda\Asztal\GS-Generator.php" -c "%1"

Remélem másnak is hasznos lesz ez a rövid script és leírás. Jó munkát!

Módosítás:
Ahogy azt már fentebb írtam a kivágott részek után, készítettem egy frisebb verziót (letisztultabb lett a kódja stb.)
A paraméterézében is történt néhány változás (ehhez mérten a Registry-be írt paramétereket is változnak).

Az új használat (<fájlnév> az a fájlnév, amelyen a generálót elmentettük):

php <fájlnév> [<kapcsolók,paraméterek>]

Az új kód működik minden parancssori paraméter nélkül is, ha így indítjuk, a standard inputról olvassa a forráskódot és a standard outputra küldi ki az eredményt.
A –input <fájlnév> kapcsoló használatával elérhetjük, hogy a standard input helyett a fájlnév tartalmát értelmezze.
A -f kapcsoló segítségével az elkészült kódokat fájlba írja ki, ezek elnevezesét a –template “fájlnév_sablon” paraméterrel módosíthatjuk (ebben az osztálynév (a névtér nem) is behelyettesítésre kerül a %1$s helyekre).

A láthatóságot ezentúl a –getter <public|protected|private> és –setter <public|protected|private> paraméterekkel állíthatjuk.
Megadhatjuk, hogy hányszoros behúzást használjon a rendszer az –indention <szám> paraméterrel, továbbá az –indentionString “<string>” paraméterrel beállíthatjuk, hogy az egy szintű behúzás mit jelentsen (space és tab indentálás így cserélgethető). Így például a négy szóközzel való, két szintű indentálást a

php <fájlnév> –indentionString "    " –indention 2

utasítással érhetjük el.
Természetesen megmaradt az assert hívások és az automatikus castolás lehetősége is, ezeket a -a ill. -ac kapcsolókkal érhetjük el. Hiba – vagy figyelmezetések – esetén a kód a standard error csatornára ír ki üzeneteket, ha ezt el akarjuk tüntetni, a -s kapcsolót állítsuk be. Végül a korábbi verzióból megismert vágólapra másolás is visszatért, ezt a -c kapcsoló állításával kapcsolhatjuk be. Ha a -c és -s kapcsolót is használjuk, sikeres lefutás végén egy felbukkanó ablak értesít arról, hogy végzett. (Ez esetemben hasznos, a fenti registry módosítással ez lett az alapvető akció a .php fájl típusoknál, így az Eclipse helyi menüjében az Open With -> System Editorral tudom futtatni, ezért kellett az értesítő)
Fontos változás az előző verzióhoz képest, hogy ez a fájlban csak egy osztályt kezel (elkerülve hogy egy teljes keretrendszert megpróbáljon átírni), így vagy csak egy osztályt deklaráljunk a fájlban, vagy használjuk a –class <osztálynév> paramétert.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
<?php
/**
 * Generate Getter and Setter methods
 * This utility can be used to create getter and setter methods for any class.
 	* -s (Silent mode)
 	* -f (Output file instead of writing to STDOUT)
 * -c (Copy source code instead of writing to STDOUT)
 	* -a (Add assertions)
 	* -ac (Add casts)
 	* --template (The file naming scheme)
 	* --input (Input file)
 	* --getter <private|protected|public> - The visibility of the getter functions
 	* --setter <private|protected|public> - The visibility of the setter functions
 	* --indention - The number of tabs before each line
 	* --indentionString - The string used for indention
 * @author Szigeti Márton Levente <blacky@invitel.hu>
 * @package ggsm
 */
$indent = cliParameter('indentionString', "\t");
$getterMethodDocCommentTemplate = explode(PHP_EOL, <<<TEMPLATE
/**
 * Getter method of the %1\$s property 
 * @return %2\$s The value of the property
*/ 
 
TEMPLATE
);
$setterMethodDocCommentTemplate = explode(PHP_EOL, <<<TEMPLATE
/**
 * Setter method of the %1\$s property
 * @param %6\$s%4\$s%2\$s \$%1\$s%5\$s The new value of the %1\$s property
 * @return %3\$s The instance containing the property 
*/
 
TEMPLATE
);
$getterMethodTemplate = explode(PHP_EOL, <<<TEMPLATE
%2\$s function %4\$s%1\$s()
{
{$indent}return \$this->%3\$s;
}
 
 
TEMPLATE
);
$setterMethodTemplate = explode(PHP_EOL, <<<TEMPLATE
%2\$s function %1\$s(%5\$s%4\$s\$%3\$s%6\$s)
{
{$indent}%7\$s\$this->%3\$s = %4\$s%8\$s$%3\$s;
}
 
 
TEMPLATE
);
 
/** No editable parts below */
 
 
function getValidator($type)
{
	if(!isNative($type)) {
		return '';
	} else {
		$validators = array(
			'bool' => 'is_bool', 'boolean' => 'is_bool',
			'int' => 'is_int', 'integer' => 'is_int',
			'float' => 'is_float', 'double' => 'is_float', 'real' => 'is_float',
			'string' => 'is_string', 'object' => 'is_object',
			'number' => 'is_numeric', 'callback' => 'is_callable');
		if(isset($validators[strtolower($type)])) {
			return $validators[strtolower($type)];
		}
		return null;
	}
}
 
function getCast($type)
{
	if(!isNative($type)) {
		return '';
	} else {
		$casts = array(
			'bool' => '(bool)', 'boolean' => '(boolean)',
			'int' => '(int)', 'integer' => '(integer)',
			'float' => '(float)', 'double' => '(double)', 'real' => '(real)',
			'string' => '(string)', 'object' => '(object)');
		if(isset($casts[strtolower($type)])) {
			return $casts[strtolower($type)];
		}
		return null;
	}	
}
 
function isNative($type)
{
	return in_array(strtolower(extractBaseType($type)),
		array(
			'bool', 'boolean', 'int', 'integer', 'float', 'double', 'real',
			'string', 'object',
			/* Pseudo-types */ 'number', 'callback'
		));
}
 
function extractType($comment)
{
	if(preg_match('/@var\s+([&\\\\\w?]+)/', $comment, $regs)) {
		return $regs[1];
	}
	return null;
}
 
function extractBaseType($type, $classNamespace = null)
{
	$type = trim($type, '?&');
	if(!is_null($classNamespace)) {
		if(dirname($type) == $classNamespace) {
			$type = basename($type);
		}
	}
	return $type;
}
 
function isReference($type)
{
	return $type{0} == '&';
}
 
function isNullable($type)
{
	return substr($type, -1) == '?';
}
 
function cliParameter($parameter, $default = null)
{
	global $argv;
	$parameter = strtolower($parameter);
	foreach($argv as $index => $argument) {
		if(strtolower($argument) == '--' . $parameter) {
			if(isset($argv[$index + 1]) && $argv[$index + 1]{0} != '-') {
				return $argv[$index + 1];
			}
		}
	}
	return $default;
}
 
function cliFlagSet($flag)
{
	global $argv;
	foreach($argv as $argument) {
		if($argument == '-' . $flag || $argument == '/' . $flag) {
			return true;
		}
	}
	return false;
}
 
function println($message, $file = STDOUT)
{
	if(!cliFlagSet('s')) {
		fwrite($file, $message . PHP_EOL);
	}
}
 
 
function ifExit($condition, $message = null, $code = null)
{
	if($condition) {
		if(!is_null($message)) {
			println($message, STDERR);
		}
		exit($code);
	}
}
 
function gsErrorHandler($errno, $errstr, $file, $line)
{
	println(sprintf('%3$s[%4$d]: (%1$d) %2$s', $errno, $errstr, basename($file), $line), STDERR);
}
 
$inputFile = cliParameter('input', null);
$workingDirectory = getcwd();
if(!$inputFile) {
	println('Starting in interactive mode (Hit CTRL+Z to quit)', STDERR);
	$sourceCode = '';
	while(!feof(STDIN)) {
		$chunk = fread(STDIN, 8192);
		$sourceCode .= $chunk;
	}
} else {
	$condition = !(file_exists($inputFile) && is_readable($inputFile));
	ifExit($condition, sprintf('The specified input file can not be found: %s', $inputFile) ,1);
	chdir(dirname($inputFile));
	ini_set('include_path', '.' . PATH_SEPARATOR . ini_get('include_path'));
	$sourceCode = file_get_contents($inputFile);
}
$classesBefore = get_declared_classes();
ifExit(eval('?>' . $sourceCode) === false, 'Source code could not be parsed', 2);
chdir($workingDirectory);
$classesAfter = get_declared_classes();
$classes = array_values(array_diff($classesAfter, $classesBefore));
ifExit(count($classes) == 0, 'Source code did not contain class declarations. Terminating.', 4);
$class = $classes[0];
ifExit((count($classes) > 1) && ($class = cliParameter('class', false)) === false, 'Source code contained more than one class declaration. Use --class <className>. Terminating.', 5);
/* Prepare templates */
$function = create_function('&$line', '$line = rtrim(str_repeat(cliParameter("indentionString", "\t"), cliParameter("indention", 1)) . $line);');
array_walk($getterMethodDocCommentTemplate, $function);
array_walk($setterMethodDocCommentTemplate, $function);
array_walk($getterMethodTemplate, $function);
array_walk($setterMethodTemplate, $function);
$getterMethodDocCommentTemplate = implode(PHP_EOL, $getterMethodDocCommentTemplate);
$setterMethodDocCommentTemplate = implode(PHP_EOL, $setterMethodDocCommentTemplate);
$getterMethodTemplate = implode(PHP_EOL, $getterMethodTemplate);
$setterMethodTemplate = implode(PHP_EOL, $setterMethodTemplate);
/* Preparing templates end */
$reflection = new ReflectionClass($class);
$properties = $reflection->getProperties();
$propertiesSorted = array();
foreach($properties as $property) {
	$propertiesSorted[$property->getName()] = $property;
}
ksort($propertiesSorted);
$properties = $propertiesSorted;
/* Create source code */
$source = '';
$useStatements = '';
$name = explode('\\', $reflection->getName());
$classNamespace = version_compare(phpversion(), '5.3.0') ? implode('\\', array_slice($name, 0, count($name) - 1)) : null;
foreach($propertiesSorted as $property) {
	$setterName = 'set' . ucfirst($property->getName());
	$getterName = 'get' . ucfirst($property->getName());
	if(($reflection->hasMethod($setterName) && $reflection->hasMethod($getterName)) || $property->isPublic()) {
		continue;
	}
	$type = extractType($property->getDocComment());
	if(is_null($type)) {
		continue;
	}
	$name = $property->getName();
	$baseType = extractBaseType($type, $classNamespace);
	$classType = extractBaseType($reflection->getName(), $classNamespace);
	$getterVisibility = cliParameter('getter', 'public');
	$setterVisibility = cliParameter('setter', 'public');
	$cast = cliFlagSet('ac', false) && isNative($type) ? getCast(extractBaseType($type)) : '';
	$referenceSign = isReference($type) ? '&'  : '';
	if($referenceSign) {
		$cast = '';
	}
	$nullStart = isNullable($type) ? '[' : '';
	$nullEnd = isNullable($type) ? ' = null] '  : '';
	$nullDefault = isNullable($type) ? ' = null' : '';
	$typeHint = '';
	if(!isNative($type)) {
		$namespace = dirname(extractBaseType($type));
		if(strtolower(extractBaseType($type)) != 'array' && $namespace != '.' && $classNamespace != $namespace) {
			$useStatements .= 'use ' . extractBaseType($type) . ';' . PHP_EOL;
		}
		$typeHint = basename(extractBaseType($type)) . ' ';
	}
	$assertion = cliFlagSet('a', false) && isNative($type) ? "assert('" . getValidator(extractBaseType($type)) . "(\$" . $property->getName() . ")');" . PHP_EOL . str_repeat(cliParameter('indentionString', "\t"), cliParameter('indention', 1) + 1) : '';
	$source .= sprintf($getterMethodDocCommentTemplate, $name, $baseType);
	$source .= sprintf($getterMethodTemplate, $getterName, $getterVisibility, $name, $referenceSign);
	$source .= sprintf($setterMethodDocCommentTemplate, $name, $baseType, $classType, $referenceSign, $nullEnd, $nullStart);
	$source .= sprintf($setterMethodTemplate, $setterName, $setterVisibility, $name, $referenceSign, $typeHint, $nullDefault,  $assertion, $cast);
}
$source = $useStatements . PHP_EOL . $source;
if(cliFlagSet('f')) {
	$template = cliParameter('template', '%1$s_generated.php');
	file_put_contents(sprintf($template, extractBaseType($class, $classNamespace)), $source);
} elseif(class_exists('COM') && cliFlagSet('c')) {
	try {
		$wordCom = new COM('Word.Application');
		$wordCom->Visible = false;
		$wordCom->Documents->Add();
		$wordCom->Selection->TypeText($source);
		$wordCom->Selection->WholeStory();
		$wordCom->Selection->Copy();
		$wordCom->Quit(false);
		if(cliFlagSet('s')) {
			$message = 'Getters and setter copied to clipboard';
			$title = 'Finished';
			$scriptCom = new COM('WScript.Shell');
			$scriptCom->Popup($message, 0, $title, 0);
		} 		
	} catch(Exception $x) {
		try {
			$wordCom->Quit(false);
		} catch(Exception  $x) {}
		exit(5);
	}
} else {
	echo $source;
}
exit(0);

1 hozzászólás

  1. Ez a __get() __set() helyettesítés eléggé ki lett vesézve, bár biztosan tömör és száraz sokaknak, mégis többet ad, mint a legtöbb példa ;) thx

HOZZÁSZÓLOK A CIKKHEZ

Kérjük, írja be véleményét!
írja be ide nevét