Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit 1554d1cddd50efd0e853003867acf3b0e2088e7f @juzna committed Aug 12, 2012
@@ -0,0 +1,2 @@
+composer.lock
+vendor/
@@ -0,0 +1,5 @@
+{
+ "require": {
+ "nette/nette": "2.0.*"
+ }
+}
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * Converts #attr(foo) into getter and setter method
+ *
+ * @author Jan Dolecek <juzna.cz@gmail.com>
+ */
+class GettersEnhancer implements \Enhancer\IEnhancer
+{
+
+ public function enhance($code)
+ {
+ $code = preg_replace_callback('~#attr\((\w+)\)$~m', function($match) {
+ $name = $match[1];
+ $ucName = ucfirst($name);
+
+ $snippet = "
+ private \$$name;
+
+ public function get$ucName()
+ {
+ return \$this->$name;
+ }
+
+ public function set$ucName(\$$name)
+ {
+ \$this->$name = \$$name;
+ return \$this;
+ }
+ ";
+
+ return str_replace("\n", ' ', $snippet); // remove newlines so that it doesn't break line numbers
+ }, $code);
+ return $code;
+ }
+
+}
@@ -0,0 +1,35 @@
+# Example 01 - getters and setters
+
+Very simple *enhancer* generates a) property, b) getter and c) setter.
+
+
+## Sample
+This experimental code
+
+```php
+class Movie
+{
+ #attr(name)
+}
+```
+
+will get enhanced into something like this:
+```php
+class Movie
+{
+ private $name;
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ return $this;
+ }
+}
+```
+
+See `test.php` demo that it really works.
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Movie model class
+ *
+ * @author Jan Dolecek <juzna.cz@gmail.com>
+ */
+class Movie
+{
+ #attr(name)
+ #attr(rating)
+
+ public function __construct($name, $rating)
+ {
+ $this->name = $name;
+ $this->rating = $rating;
+ }
+
+}
@@ -0,0 +1,22 @@
+<?php
+require_once __DIR__ . '/../bootstrap.php';
+
+// Setup enhancer
+require_once __DIR__ . '/GettersEnhancer.php';
+\Enhancer\EnhancerStream::$enhancer = new GettersEnhancer;
+
+
+// Register autoloader
+$loader = new \Enhancer\Loaders\ClassLoader;
+$loader->register(true);
+$loader->add('', __DIR__ . '/classes');
+
+
+/***************** tests *****************j*d*/
+
+
+$movie = new Movie("The Dark Night Returns", 10);
+var_dump($movie->getName(), $movie->getRating()); // methods generated by enhancer
+
+$refl = new ReflectionClass('Movie'); // the methods are real
+print_r($refl->getMethods());
@@ -0,0 +1,6 @@
+# PhpEnhancer examples
+
+Each example is in one directory. It contains:
+- **custom enhancer** (`FooEnhancer.php`) which alters PHP code somehow
+- **demo classes** (`classes`) with experimental syntax, which will be converted by *enhancer*
+- **runner** (`test.php`) which set-ups the enhancer and runs some tests
@@ -0,0 +1,20 @@
+<?php
+// Register enhancer stream wrapper
+use Enhancer\EnhancerStream;
+
+/** @var \Composer\Autoload\ClassLoader $loader */
+$loader = require_once __DIR__ . '/../vendor/autoload.php';
+$loader->add('Enhancer', __DIR__ . '/../src/');
+
+
+// Load and start PHP enhancer
+//require_once __DIR__ . '/../src/Enhancer/EnhancerStream.php';
+//require_once __DIR__ . '/../src/Enhancer/Enhancer.php';
+
+// NOTE: do this in a particular example
+// EnhancerStream::$enhancer = new Enhancer\Enhancers\DemoEnhancer;
+
+// Add new wrapper
+if ( ! stream_wrapper_register('enhance', 'Enhancer\\EnhancerStream')) {
+ throw new ErrorException("Unable to register enhancer stream");
+}
@@ -0,0 +1,48 @@
+<?php
+
+namespace Enhancer;
+
+/**
+ * Stream wrapper which enhances PHP code before execution
+ * NOTE: You must set static $enhancer variable when registered!
+ *
+ * @author Jan Dolecek <juzna.cz@gmail.com>
+ */
+class EnhancerStream
+{
+ /** @var IEnhancer */
+ public static $enhancer;
+
+ private $filename;
+ private $buffer;
+ private $pos;
+
+ public function stream_open($path, $mode, $options, &$opened_path)
+ {
+ $this->filename = substr($path, strlen("ehnance://"));
+ $this->buffer = self::$enhancer->enhance(file_get_contents($this->filename));
+ $this->pos = 0;
+ return true;
+ }
+
+ public function stream_stat()
+ {
+ return array(
+ 'size' => strlen($this->buffer),
+ );
+ }
+
+ public function stream_read($count)
+ {
+ $ret = substr($this->buffer, $this->pos, $count);
+ $this->pos += $count;
+ return $ret;
+ }
+
+ public function stream_eof()
+ {
+ return $this->pos >= strlen($this->buffer);
+ }
+
+
+}
@@ -0,0 +1,22 @@
+<?php
+
+namespace Enhancer\Enhancers;
+
+/**
+ * Enhances PHP code
+ * Tastes best with EnhancerStream
+ *
+ * @author Jan Dolecek <juzna.cz@gmail.com>
+ */
+class DemoEnhancer
+{
+
+ public function enhance($code)
+ {
+ // here comes all the magic
+ $code = preg_replace('/(new\s+\w+)(<\w+>)/', '\\1 /* \\2 */', $code);
+
+ return $code;
+ }
+
+}
@@ -0,0 +1,21 @@
+<?php
+
+namespace Enhancer;
+
+/**
+ * Enhances PHP code
+ * Tastes best with EnhancerStream
+ *
+ * @author Jan Dolecek <juzna.cz@gmail.com>
+ */
+interface IEnhancer
+{
+
+ /**
+ * Get enhanced code
+ * @param string $code
+ * @return string
+ */
+ public function enhance($code);
+
+}
@@ -0,0 +1,21 @@
+<?php
+
+namespace Enhancer\Loaders;
+
+use Composer;
+
+/**
+ * Composer's autoloader with enhancing enabled
+ *
+ * @author Jan Dolecek <juzna.cz@gmail.com>
+ */
+class ClassLoader extends Composer\Autoload\ClassLoader
+{
+
+ public function findFile($class)
+ {
+ $ret = parent::findFile($class);
+ return $ret ? "enhance://$ret" : $ret;
+ }
+
+}
@@ -0,0 +1,51 @@
+<?php
+
+namespace Enhancer\Loaders;
+
+use Nette;
+
+/**
+ * Nette's RobotLoader with enhancing enabled
+ * FIXME: doesn't work because of private properties :(
+ *
+ * @author Jan Dolecek <juzna.cz@gmail.com>
+ */
+class RobotLoader extends Nette\Loaders\RobotLoader
+{
+ // copied whole method just for one change
+ public function tryLoad($type)
+ {
+ $type = ltrim(strtolower($type), '\\'); // PHP namespace bug #49143
+ $info = & $this->list[$type];
+
+ if ($this->autoRebuild && empty($this->checked[$type]) && (is_array($info) ? !is_file($info[0]) : $info < self::RETRY_LIMIT)) {
+ $info = is_int($info) ? $info + 1 : 0;
+ $this->checked[$type] = TRUE;
+ if ($this->rebuilt) {
+ $this->getCache()->save($this->getKey(), $this->list, array(
+ Cache::CONSTS => 'Nette\Framework::REVISION',
+ ));
+ } else {
+ $this->rebuild();
+ }
+ }
+
+ if (isset($info[0])) {
+ Nette\Utils\LimitedScope::load("enhance://" . $info[0], TRUE); // NOTE: here is the change!
+
+ if ($this->autoRebuild && !class_exists($type, FALSE) && !interface_exists($type, FALSE) && (PHP_VERSION_ID < 50400 || !trait_exists($type, FALSE))) {
+ $info = 0;
+ $this->checked[$type] = TRUE;
+ if ($this->rebuilt) {
+ $this->getCache()->save($this->getKey(), $this->list, array(
+ Cache::CONSTS => 'Nette\Framework::REVISION',
+ ));
+ } else {
+ $this->rebuild();
+ }
+ }
+ self::$count++;
+ }
+ }
+
+}
@@ -0,0 +1,23 @@
+<?php
+
+namespace Enhancer\Utils;
+
+use Nette;
+
+/**
+ * Simple tokenizer for PHP.
+ *
+ * @author David Grudl
+ */
+class PhpParser extends Nette\Utils\Tokenizer
+{
+
+ function __construct($code)
+ {
+ $this->ignored = array(T_COMMENT, T_DOC_COMMENT, T_WHITESPACE);
+ foreach (token_get_all($code) as $token) {
+ $this->tokens[] = is_array($token) ? self::createToken($token[1], $token[0]) : $token;
+ }
+ }
+
+}

0 comments on commit 1554d1c

Please sign in to comment.