I just found this presentation which says a lot of the things I stand for. Passion is the most important ingredient in a startup. With passion and the right connections, you are almost home safe. Of course you have to pick the right thing to be passionate about, but that's another story.
Ok, this is a little bit late news, but Seedcamp has released the application guide for 2008 applications. The applications are due August 10 and I really encourage anyone with an idea and a great co-founder(s) to apply. Even if you are not interested in applying for the "competition" I think it would be good for everyone with an Internet business to look at the questions and answer them for your own sake. If you ever will be looking for funding, those are the questions you will have to be able to answer.
I think the key is to write really concise answers. Don't write marketing material. Write short answers that your grandma would understand.
Seedcamp's new promotion video interviews a bunch of great entrepreneurs and investors and asks them a couple of very interesting questions. After seeing the video I wanted to give my opinions on the matter from the point of view of being a fairly fresh entrepreneur that won the Seedcamp competition last year with my company Tablefinder. So here it goes:
1: What is an entrepreneur?
To me an entrepreneur is someone that does not consider work a burden. My job is partly doing exactly what I would be doing in my spare time. So thus I work a lot. Another key factor for me is passion. You have to see the sparks flying out of the eyes and ears of the entrepreneur as he or she is trying to solve the problem.
2: What are investors looking for?
This is obviously not the easiest question to answer for be but I will answer from what I've experienced works and what doesn't.
The single most important factor is your 30 second pitch. And the most important part about the pitch is that it doesn't have to describe all of your business. I made the mistake in the beginning because I wanted the investors to understand everything about my idea and why it was/is so great. Those type of conversations tend to be very detailed and long and complicated, and clearly not suitable for an elevator pitch.
Another thing I've learned is that the pitch is not about to create something that sounds nice and really smart gives metaphors on all of the cool functionality your idea will have. It is rather about simplifying everything to just its core. If you cant say your main thing in one sentence, you are not enough focused, or you are trying to explain to much of the idea.
So from my experience investors are looking for a really clear idea that you can express in a few sentences, and that has a big proven market.
All investors talk about that they think the team is the most important thing. And I agree fully, but the point is, if you and your team is really good, you still need to be able to pitch your idea. And if you even get to have a meeting with one of the bigger investors, you probably already have made a pretty good impression as an entrepreneur.
Your team is not always about bragging about all the stuff you have accomplished, but sometimes passion is enough. If really love solving the problem you are working on, and make that rub off on people around you, I believe you have come very far towards what investors are looking for.
3: How do you find investors that are right for me?
This one is very important, but also very tricky. It clearly depends on what stage your company is in. I made a big mistake, but got invaluable pitch-training in the process, by pitching Tablefinder to all of the big investors in London, when we clearly, with hindsight, needed angel funding. The problem for us was that the business had changed directions a couple of times before Seedcamp and the company was more mature than the business idea. In the end it is always important to have investors that share the same vision as you. They should be people that are well connected within your area of operation, and should be willing to help you grow your business.
An investor is going to be a very close partner for quite some time, so it is as important to screen your investors as it is for them to screen you. Unfortunately sometimes or many times we entrepreneurs don't have the luxury of being picky. But when I will do it again, I sure will put my self in a financial situation where I will be able to be picky.
4: What is it like to be an entrepreneur?
Now this one I know. And I can't emphasize this enough: it takes passion and a whole lot of patience. To be an entrepreneur is doing what you love everyday, and not getting tired of working. Many of my friends think I work too much, but is it really work if you love what you do?
I might not love every moment and aspect of my work, especially working without any income what so ever for 6 months is not the best kind of fun. But the rewards of solving a problem and building your business when nobody thought you would make it, that is what I love. The satisfaction of doing something that nobody else could do, and being respected for doing so is unbeatable.
So, in the video they talk about getting through "the first tough months". I don't think that is the toughest part at all. But the big problems come when you try to build your business after you have launched. If the business doesn't start rolling straight away, and you won't get funding until it does. That is when it is tough. But also the most rewarding if you can push through. Seth Godin's book The Dip was a great source of inspiration for me when things looked like we were out of business. We were in the dip, but now I finally feel we are moving past it.
5: What is the biggest mistake you've made?
I've made so many during the last 2 years, but the one I think stands out the most is that I wanted to do too much. I wanted everyone to have our solution because "everyone who eats could use our service", which is true but not really a healthy customer definition for the business. I also wanted everyone to have the best features already there once we launched. This ended up delaying our launch with many months. And now with hindsight, I would like to say as Paul Graham of Y Combinator says:
"If sitting down for a couple of hours to come up with a company motto delays your launch by a couple of hours, it is probably not worth it"
So when I do this again, I will launch as quickly as possible with one solid problem solving feature and nothing more. Additional features can always be added afterwards.
One thing I learned at Seedcamp was to think of "outcomes" rather than "features". What does the user really want to accomplish? And are we producing that outcome? To me it has worked in the simplification process, not only for features, but also for the pitch.
6: Lessons I've learned along the way?
When you are running with your startup in 200 km/h in one direction you are picking up a lot of experience along the way. The problem is that if you don't stop to think, you won't be able to apply what you've learned until you've run past your objective.
Having gone through a really tough period where I had to focus on bringing in money just to pay the bills, it was like a took a step back, slowed down, and everything became clear to me. Almost a euphoric moment when you get a weight lifted of your shoulders because "It can't get any worse" and that there is no pressure for you to produce results because people have stopped believing.
I never stopped believing and I feel I've been rewarded already. Everything I learned at Seedcamp and through all of my pitches I've made, good and bad, I can now apply, and start to pick up speed once again to get even more experience. Hopefully next time I will settle down to understand what I've learned it will be for better reasons.
I've already mentioned above many of the lessons learned for me. But I think one that I haven't talked about is trust your instincts.
I think one of my largest mistakes was when winning Seedcamp, I thought that everyone around me knew better about my business than me, because of their experience in other fields. And I thought it was all done now since the people around me would "make it happen". Nobody but you make it happen!
Meeting great entrepreneurs and investors has been invaluable for me and I wish everyone could do that, but never think that they know exactly what is right for you, and never expect that they should make any decisions for you. You are the one that knows most about your business, so try to take in the advice, even though it is hard when running 200 km/h, but trust your gut feeling about your decisions and stick with them.
7: Europe or the valley?
As I've never been in the valley I can't really say much about it, but I must say I'm envious of the entrepreneurial environment. As Michal Arrington at Techcrunch say in the video, starting a business in the US gives social credit, but in Europe, many people would look at you and think you were crazy for quitting your government job for starting a business.
This mentality is exactly what Seedcamp is about, which is also something I strongly believe in. We need to utilize and recycle entrepreneurial experience in Europe, and help each other succeed. This reason alone is why I took the initiative to start OpenCoffee in Gothenburg as there are not many places for entrepreneurs helping entrepreneurs.
8: What are the classic mistakes start-ups make time and again?
For us, one of the main problems has been focus. As Saul Klein said on Demo day:
Find your mountain!
Meaning that you have to pick one peak to climb, and not take on all at once. This is very difficult, but from what I understand now, a good measurement of how focused your idea is, is if you can define it with just one short, clear, and understandable sentence.
Another problem on the same theme is trying to pinpoint your core customer. I mentioned this above, but the answer to the question "Who is you customer?" is not "Anybody can use this service!" but rather a specified demographic which you know a lot of details about.
I just started using Twitter yesterday, and I can't believe how late I am to the party. I'm feeling really lame that I'm not such an early adopter as I thought I was. But I guess, now that I've started, it's really frustrating not to have a bunch of followers that I want to tell stuff. So feel free to start following me at http://twitter.com/andefred
I'm still trying to catch on to the blogging train and even though I strongly believe that blogging will make you more productive, I'm not so good at practicing what I preach. well well... maybe this twittering will make it easier for me, and as I again move more towards business I will be blogging more, and hopefully start blogging more on Tablefinder as well.
On the May 8, STING's Innovation & Technology Award will be given to the top technology companies that apply to the competition. The winners will get a nice 150,000 SEK (€16,000) in pricemoney, but more importantly the winner will be direct qualified to the final of this years Seedcamp. Last year I was lucky to be among the winners, and I have nothing but good things to say about the experience.
When working in a venture capital financed company and trying to get stuff going really fast really early, the most important thing to have is a good network of contacts. Once you know the right person at the right company, things get way easier. Seedcamp is about giving you the opportunity to create such a network, whether you win or not.
So if you are a Swedish company I strongly recommend applying to both STING's competition, but also to Seedcamp directly.
Lately I have received some requests on how Zend Layout can be used while not using the default templating system in Zend Framework (ZF), but instead using Smarty as a templating engine. So here is a write up on how to do so. The way to implement this may vary, so this is just one way of doing it.
This "tutorial" requires that you have at least some sense on how ZF works and a little bit on MVC in general.
1) Get Zend Studio for Eclipse as this will help A LOT on structuring your applications in a good way.
2) Document folder structure:
Having a good document folder structure will also help you build clean and maintainable applications. I try to mimic the ZF coding standard and naming conventions as much as possible. Here's my setup:
In order for things to work with Smarty and Zend Layout, we need to set som extra variables and inflector scripts so that view scripts will be resolved and parsed appropriately.
/**
* ezApplicationBase - A Zend Framework implementation with Smarty and Zend Layout
*
* Bootstrap file for mysite.com
*
* @author Anders Fredriksson (http://anders.tyckr.com)
* @version 0.1
*///three folders "above" index.phpdefine('ROOT_DIR', dirname(dirname(dirname(__FILE__))));
define('APP_DIR',dirname(dirname(__FILE__)));
set_include_path('.' . PATH_SEPARATOR . APP_DIR . '/lib/' . PATH_SEPARATOR . APP_DIR . '/application/default/models/' . PATH_SEPARATOR . ROOT_DIR . '/shared/lib/' . PATH_SEPARATOR . get_include_path());
//This requires that your Zend library lies in ROOT_DIR/shared/lib///make classes autoload without doing requirerequire_once('Zend/Loader.php');
Zend_Loader::registerAutoload();
if(defined('ENV') !== TRUE){define('ENV','production'); //change staging to production to go to production settings}$config = new Zend_Config_Xml(APP_DIR . '/config/config.xml', ENV);
Zend_Registry::set('config',$config);
//init session$session = new Zend_Session_Namespace($config->session_name);
Zend_Registry::set('session',$session);
Zend_Db_Table::setDefaultAdapter(Zend_Db::factory(Zend_Registry::get('config')->database));
/**
* Init the Smarty view wrapper and set smarty suffix to the view scripts.
*/$view = new EZ_View_Smarty($config->smarty->toArray());
//use the viewrenderer to keep the code DRY//instantiate and add the helper in one go$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
$viewRenderer->setView($view);
$viewRenderer->setViewSuffix($config->smarty->suffix);
/**
* Set inflector for Zend_Layout
*/$inflector = new Zend_Filter_Inflector(':script.:suffix');
$inflector->addRules(array(':script' => array('Word_CamelCaseToDash', 'StringToLower'),
'suffix' => $config->layout->suffix));
// Initialise Zend_Layout's MVC helpers
Zend_Layout::startMvc(array('layoutPath' => ROOT_DIR.$config->layout->layoutPath,
'view' => $view,
'contentKey' => $config->layout->contentKey,
'inflector' => $inflector));
$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory(array('default' => '../application/default/controllers',
'blog' => '../application/blog/controllers',
));
$front->throwExceptions(true);
//enable logging to default.log$writer = new Zend_Log_Writer_Stream(APP_DIR.'/data/log/default.log');
$logger = new Zend_Log($writer);
//give easy access to the logger
Zend_Registry::set('logger',$logger);
try {$front->dispatch();
} catch(Exception $e){echonl2br($e->__toString());
}
5) The Smarty implementation of Zend_View. For this I want to give Naneau credit as it is mostly the same as the implementation he did a while back.
EZ/View/Smarty.php
/**
*
* @since Dec 7 2007
* @author Anders Fredriksson
*//**
* Zend View Base Class
*/require_once'Zend/View.php';
/**
* Smarty templating engine
*/require_once'Smarty/Smarty.class.php';
/**
* @category
*/class EZ_View_Smarty extends Zend_View_Abstract {/**
* Smarty object
* @var Smarty
*/
protected $_smarty;
protected $_plugins;
/**
* Constructor
*
* Pass it a an array with the following configuration options:
*
* scriptPath: the directory where your templates reside
* compileDir: the directory where you want your compiled templates (must be
* writable by the webserver)
* configDir: the directory where your configuration files reside
*
* both scriptPath and compileDir are mandatory options, as Smarty needs
* them. You can't set a cacheDir, if you want caching use Zend_Cache
* instead, adding caching to the view explicitly would alter behaviour
* from Zend_View.
*
* @see Zend_View::__construct
* @param array $config ["scriptPath" => /path/to/templates,
* "compileDir" => /path/to/compileDir,
* "configDir" => /path/to/configDir ]
* @throws Exception
*/publicfunction __construct($config = array()){$this->_smarty = new Smarty ();
//smarty objectif(! array_key_exists('compileDir', $config)){
throw new Exception ('compileDir must be set in $config for ' . get_class($this));
}else{$this->_smarty->compile_dir = $config['compileDir'];
}//compile dir must be set$this->_smarty->debugging = true;
if(array_key_exists('configDir', $config)){$this->_smarty->config_dir = $config['configDir'];
}//configuration files directory
parent::__construct ($config);
//call parent constructor$this->_plugins = new EZ_View_Smarty_Plugin_Broker($this);
$this->registerPlugin(new EZ_View_Smarty_Plugin_Standard());
}publicfunction setCompilePath($dir){$this->_smarty->compile_dir = $dir;
return$this;
}/**
* Return the template engine object
*
* @return Smarty
*/publicfunction getEngine(){return$this->_smarty;
}/**
* register a new plugin
*
* @param EZ_View_Smarty_Plugin_Abstract
*/publicfunction registerPlugin(EZ_View_Smarty_Plugin_Abstract $plugin,$stackIndex = null){$this->_plugins->registerPlugin($plugin, $stackIndex);
return$this;
}/**
* Unregister a plugin
*
* @param string|EZ_View_Smarty_Plugin_Abstract $plugin Plugin object or class name
*/publicfunction unRegisterPlugin($plugin){$this->_plugins->registerPlugin($plugin);
return$this;
}/**
* fetch a template, echos the result,
*
* @see Zend_View_Abstract::render()
* @param string $name the template
* @return void
*/
protected function _run(){$this->strictVars(true);
$vars = get_object_vars($this);
foreach($varsas$key => $value){if('_' != substr($key, 0, 1)){$this->_smarty->assign($key, $value);
}}//assign variables to the template engine$this->_smarty->assign_by_ref('this', $this);
//why 'this'?//to emulate standard zend view functionality//doesn't mess up smarty in any way$path = $this->getScriptPaths();
$file = substr(func_get_arg(0), strlen($path[0]));
//smarty needs a template_dir, and can only use templates,//found in that directory, so we have to strip it from the filename$this->_smarty->template_dir = $path[0];
//set the template diretory as the first directory from the pathecho$this->_smarty->fetch($file);
//process the template (and filter the output)}}
My additions are basically the possibility to add plugins to Smarty without putting them in the Smarty lib directory. This is needed so that a nicer implementation of ZF's view helpers can be used.
6) Smarty plugin loader
EZ/View/Smarty/Plugin/Standard.php
class EZ_View_Smarty_Plugin_Standard extends EZ_View_Smarty_Plugin_Abstract {publicstaticfunction layoutFunction($params, &$smarty){$section = $params['section'];
$toReturn = "";
try {//oppress errors
@$toReturn = $smarty->_tpl_vars ['this']->placeholder('Zend_Layout')->$section;
return$toReturn;
} catch ( Exception $e){return$toReturn;
}}publicstaticfunction headTitleFunction($params, &$smarty){$method = (isset($params['method'])) ? $params['method'] : - 1;
unset($params['method']);
$args = (isset($params['args'])) ? self::parseArgs($params['args']) : null;
$toReturn = "";
try {//oppress errorsif($method != - 1){
@$toReturn = $smarty->_tpl_vars ['this']->headTitle()->$method($args);
}else{$toReturn = $smarty->_tpl_vars ['this']->headTitle();
}return$toReturn;
} catch ( Exception $e){return$toReturn;
}}publicstaticfunction headScriptFunction($params, &$smarty){$method = (isset($params['method'])) ? $params['method'] : - 1;
unset($params['method']);
$args = (isset($params['args'])) ? self::parseArgs($params['args']) : null;
$toReturn = "";
try {if($method != - 1){//oppress errorsif(is_array($args)){//$toReturn = call_user_func('$smarty->_tpl_vars["this"]->headScript()->$method()',$args,true);//print "args src= ".$args['src'];//TODO: fix args usage$toReturn = $smarty->_tpl_vars ['this']->headScript()->$method($args['src']);
}else{
@$toReturn = $smarty->_tpl_vars ['this']->headScript()->$method($args);
}}else{$toReturn = $smarty->_tpl_vars ['this']->headScript();
}return$toReturn;
} catch ( Exception $e){return$toReturn;
}}publicstaticfunction jsLocaleFunction($params, &$smarty){$method = (isset($params['method'])) ? $params['method'] : - 1;
unset($params['method']);
$args = (isset($params['args'])) ? self::parseArgs($params['args']) : null;
$toReturn = "";
try {if($method != - 1){//oppress errorsif(is_array($args)){//$toReturn = call_user_func('$smarty->_tpl_vars["this"]->headScript()->$method()',$args,true);//print "args src= ".$args['src'];//TODO: fix args usage$toReturn = $smarty->_tpl_vars ['this']->jsLocale()->$method($args['src']);
}else{
@$toReturn = $smarty->_tpl_vars ['this']->jsLocale()->$method($args);
}}else{$toReturn = $smarty->_tpl_vars ['this']->jsLocale();
}return$toReturn;
} catch ( Exception $e){return$toReturn;
}}publicstaticfunction headStyleFunction($params, &$smarty){$method = (isset($params['method'])) ? $params['method'] : - 1;
unset($params['method']);
$args = (isset($params['args'])) ? self::parseArgs($params['args']) : null;
$toReturn = "";
try {if($method != - 1){//oppress errorsif(is_array($args)){//$toReturn = call_user_func('$smarty->_tpl_vars["this"]->headStyle()->$method()',$args,true);//print "args src= ".$args['src'];//TODO: fix args usage$toReturn = $smarty->_tpl_vars ['this']->headStyle()->$method($args['src']);
}else{
@$toReturn = $smarty->_tpl_vars ['this']->headStyle()->$method($args);
}}else{$toReturn = $smarty->_tpl_vars ['this']->headStyle();
}return$toReturn;
} catch ( Exception $e){return$toReturn;
}}publicstaticfunction parseArgs($args){if(preg_match_all("/([^\\[^\\]]+)/", $args, $matches)){$params = explode(',', $matches[1][0]);
$toReturn = array();
foreach($paramsas$p){
@list($key, $value) = explode('=>', $p);
$toReturn[preg_replace("/[\\' ]+/", "", $key)] = preg_replace("/[\\' ]+/", "", $value);
}return$toReturn;
}return$args;
}/**
* Smarty block function, provides gettext support for smarty.
*
* The block content is the text that should be translated.
*
* Any parameter that is sent to the function will be represented as %n in the translation text,
* where n is 1 for the first parameter. The following parameters are reserved:
* - escape - sets escape mode:
* - 'html' for HTML escaping, this is the default.
* - 'js' for javascript escaping.
* - 'url' for url escaping.
* - 'no'/'off'/0 - turns off escaping
* - plural - The plural version of the text (2nd parameter of ngettext())
* - count - The item count for plural mode (3rd parameter of ngettext())
*/publicstaticfunction tBlock($params, $text, &$smarty){$text = stripslashes($text);
// set escape modeif(isset($params['escape'])){$escape = $params['escape'];
unset($params['escape']);
}// set plural versionif(isset($params['plural'])){$plural = $params['plural'];
unset($params['plural']);
// set countif(isset($params['count'])){$count = $params['count'];
unset($params['count']);
}}// use plural if required parameters are setif(isset($count) && isset($plural)){$text = EZ_Language::getInstance()->translatePlural($text, $plural, $count);
}else{// use normal$text = EZ_Language::getInstance()->translate($text);
}// run strarg if there are parametersif(count($params)){$text = self::strarg($text, $params);
}if(! isset($escape) || $escape == 'html'){// html escape, default$text = nl2br(htmlspecialchars($text));
}elseif(isset($escape)){switch($escape){case'javascript' :
case'js' :
// javascript escape$text = str_replace('\'', '\\\'', stripslashes($text));
break;
case'url' :
// url escape$text = urlencode($text);
break;
}}return$text;
}/**
* Replaces arguments in a string with their values.
* Arguments are represented by % followed by their number.
*
* @param string Source string
* @param mixed Arguments, can be passed in an array or through single variables.
* @returns string Modified string
*/publicstaticfunction strarg($str){$tr = array();
$p = 0;
for($i=1; $i < func_num_args(); $i++){$arg = func_get_arg($i);
if(is_array($arg)){foreach($argas$aarg){$tr['%'.++$p] = $aarg;
}}else{$tr['%'.++$p] = $arg;
}}returnstrtr($str, $tr);
}}
This file is basically to allow for view helper function calls that are needed to get Zend Layout to work properly. The Smarty compiler has a limitation of just allowing one level of function calls {$this->someHelper()} works, but {$this->someHelper()->someHelperFunction()} does not work. Zend Layout requires you to do $this->placeholder("Zend_Layout")->section to output the data inside the layoutsection "section". With my helper the syntax for that would be {layout section='section'}
The other helpers in there are some of the standard helpers that comes with Zend View. I kept them in there to give you an idea of what can be done.
The main purpose for using Zend Layout with Smarty is that you can give your site a clean look without having to duplicate code.
7) The default template for the layout. I use YUI Grids CSS to get a good clean standard css layout.
<xml version="1.0" encoding="UTF-8" />
{$this->doctype()}
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang="en" lang="en">
<head>
{headTitle}
{headScript}
{$this->headLink()}
<link rel="stylesheet" href="http://yui.yahooapis.com/2.4.1/build/reset-fonts-grids/reset-fonts-grids.css" type="text/css">
{headStyle}
{$this->headMeta()}
{*debugging turned on as a default during development *}
{if $debugging}
{debug}
{/if}
</link></head>
<body>
<div id="custom-doc" class="yui-t2">
<div id="hd" class="header">{$this->placeholder("header")}</div>
<div id="bd">
<div id="yui-main">
<div class="yui-b">
<div class="yui-g">
<!-- YOUR DATA GOES HERE -->
{layout section="content"}
</div>
</div>
</div>
<div id="leftColumn" class="yui-b"><!-- YOUR NAVIGATION GOES HERE -->
{$this->placeholder("leftColumn")}
</div>
</div>
<div id="ft">{$this->placeholder("footer")}</div>
</div>
</body>
</html>
Get some action to render into a section, eg. "leftColumn".
For this to work with a Zend Layout section you need to set the responseSegmentName of that action, which is done with:
Doing this will probably put you in a rough spot when you try to make calls to specific actions from within other actions or preDispatch. To avoid this there is a simpler method that I just switched to.
To render action "testAction()" from OtherController into a "section" leftColumn for instance, and to do that from another action call "indexAction()" we do the following:
publicfunction indexAction(){$this->view->placeholder('leftColumn')->set($this->view->action('test','Other'));
$this->view->someVar = "this will get rendered into the index.tpl";
}
And that pretty much wraps it up for now.
Please feel free to ask me any questions.
Update: I guess it will help more if I post the code for the Smarty plugin helpers
EZ/View/Smarty/Plugin/Abstract.php
abstract class EZ_View_Smarty_Plugin_Abstract {
protected $_functionRegistry;
protected $_namingPattern ='/([a-zA-Z1-9]+)(Function|Block)$/';
/**
*
*/function __construct(){}/**
* @return array
*/publicfunction getClassFunctionArray(){$type = get_class($this);
$methods = get_class_methods($this);
foreach($methodsas$value){if(preg_match($this->_namingPattern,$value,$matches)){$this->_functionRegistry[$matches[1]] = $type."::".$value;
}}return$this->_functionRegistry;
}/**
* change the default naming pattern for functions that should be mapped to smarty functions
*/publicfunction setNamingPattern($pattern ='/([a-zA-Z1-9]+)(Function|Block)$/'){//"/([a-zA-Z1-9]+)Function$/";$this->_namingPattern = $pattern;
}}
EZ/View/Smarty/Plugin/Broker.php:
/**
* EZ_View_Smarty_Plugin_Broker
*
* This class registers smarty plugins with the current smarty view
*/class EZ_View_Smarty_Plugin_Broker {/**
* Array of instance of objects extending EZ_View_Smarty_Plugin_Abstract
*
* @var array
*/
protected $_plugins = array();
protected $_view;
/**
*
* @param EZ_View_Smarty $view
*/publicfunction __construct($view){$this->_view = $view;
}/**
* Register a plugin.
*
* @param EZ_View_Smarty_Plugin_Abstract $plugin
* @param int $stackIndex
* @return EZ_View_Smarty_Plugin_Broker
*/publicfunction registerPlugin(EZ_View_Smarty_Plugin_Abstract $plugin,$stackIndex = null){if(false !== array_search($plugin, $this->_plugins, true)){
throw new EZ_View_Smarty_Exception ('Plugin already registered');
}$stackIndex = ( int )$stackIndex;
if($stackIndex){if(isset($this->_plugins [$stackIndex])){
throw new EZ_View_Smarty_Exception ('Plugin with stackIndex "' . $stackIndex . '" already registered');
}$this->_plugins [$stackIndex] = $plugin;
}else{$stackIndex = count($this->_plugins );
while(isset($this->_plugins [$stackIndex])){
++ $stackIndex;
}$this->_plugins [$stackIndex] = $plugin;
}ksort($this->_plugins );
$this->reLoadPlugins();
return$this;
}/**
* Unregister a plugin.
*
* @param string|EZ_View_Smarty_Plugin_Abstract $plugin Plugin object or class name
* @return EZ_View_Smarty_Plugin_Broker
*/publicfunction unregisterPlugin($plugin){if($plugin instanceof EZ_View_Smarty_Plugin_Abstract){// Given a plugin object, find it in the array$key = array_search($plugin, $this->_plugins, true);
if(false === $key){
throw new EZ_View_Smarty_Exception ('Plugin never registered.');
}unset($this->_plugins [$key] <