[ZF2] ZF becoming more "static", annotation and resources

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

[ZF2] ZF becoming more "static", annotation and resources

Artur Bodera
Hello again.

Reading through all ZF2 proposals and with current classmap autoloader as
a candidate for default autoloader, and with Matthew hinting he'd like to
have
more deployment tools (build your own ZF2 framework by trimming fat),
I've come to an observation.

ZF1 has always appeared to me as one of the "dynamic" frameworks, OO,
with dynamic, on-the-fly configuration. It's slower, but more transparent in
its configuration. It's clunkier than i.e. Cake, but is very "explicit" with
every
component that is created.

An ZF1 "default" application could be built with just a few lines of code
and
all modifications resided inside application configuration (ini, xml,
array). If
I wanted to change DB adapter, I've just modified one line in INI file and
it
worked with every consequent request to the website. If I wanted to add a
module, I could just copy its files into directory and prefix autoloader +
routing would take care of the rest.

With ZF2 I see it's becoming more and more "static". As a metaphor, I'd
point
to Drupal. Since early versions (Drupal 5+) it's been build around a
cascading
tree of classes, modules and hooks. In production mode, it requires to be
"compiled", which takes a few seconds... .but this process effectively
throws
away disabled and unused parts of code. Drupal's got thousands of 3rd party
plugins and modules and this compiling/caching mechanism allows for a
heavily modded Drupal site to serve requests under 50ms, while much simpler
ZF1 app on the same machine (I used) needed 100ms just to get through
bootstrap.

The downside of Drupal, Doctrine and such is, that each time code changes,
it requires recompiling the whole app/suite (i.e. drupal cache
regeneration).


This upside of ZF1 is that it loads and inits everything dynamically. It was
slower,
but this behavior was EXPECTED by me and I found it quite convenient in many
cases. Of course one could argue, that app codebase changes once per 1
million http requests so we (lazy devs) could compile it anyways at
deployment.

There could be a small dispute, but we've seen one side being PHP HipHop
which
compiles the hell of everything and is unusable in development, while
popular op-code
caches (like APC) are a good compromise - so good that APC will be
integrated
into PHP6.


My point being, ZF2 segues in direction of being smarter with its
autoloading,
caching, code generation and inspection. DI and model patterns being
examples of
where framework-assisted code fiddling is not an option. This means, that
ZF2 will
become more similar to Symphony1 than it was to ZF1 and WILL require
developers
to learn command-line tools to regenerate bits and pieces of their
ZF2-driven app.

I think it should be binary, either:

 a) ZF becomes a cached/compiled framework which requires running
    "# zf recompile" several times a day

 b) ZF stays dynamic and does not require command-line operations at all.


I'd like to suggest, in ZF2,  that we make an OPEN, EXPLICIT statement about
that and
educate developers how they should handle ZF2 installation and application
maintenance.
AFTER we're clear about that, we can further optimize ZF2 and use all the
tricks
Drupal, Symphony, Doctrine and other compiled frameworks use.

We should streamline this process and make it as easy as:

  # cd ~/myzfapp
  # zf regenerate
      Analyzing package usage... done
      Analyzing component usage ... done
      Generating classmap... done
      Injecting dependencies... done
      Creating skeletons... done
      Introspecting database schema ... done
      Updating models... done
      Compiling locales... done
      Compiling routes... done
      Cleaning up... done

We could build and expose a compilation-chain to all components that want to
be
accelerated with the caching/regeneration process.






If we had this settled, I'd suggest complementary feature that I've recently
started to love:

  _In-code configuration with annotations_

Now this is both about simplicity, learning curve AND performance. Consider
this short example.

class IndexController extends Action
{
   /**
    * @requires db
    * @requires session
    * @cache(expires="2d")
    * @route /login
    * @page id="login"  label="Login page"  visible=true
    * @viewScript  "login.phtml"
    * @acl  login
    */
   public function loginAction(){ }
}

@requires defines which bootstrap resources are required to complete given
action. This
                allows for lazy, on-demand loading of bootstrap resources
which I'll cheer for
                in separate thread.

@cache allows us to implement true, simple to use page caching, allows for
real static
              caching (ngix, varnish,etc.)

@route defines one or more routes - simplicity

@page injects a page into navigation container

@viewScript and @acl are self explanatory.


With such verbose definition not only we can prototype faster, but we can
optimize the
hell out of it, use code "slugs", classmap  "trimming", resource
dependency calculations etc.
If we are using compilation, it's not a problem to parse through those
annotations and optimize.


What do you think about the whole approach?


--

      __
     /.)\   +48 695 600 936
     \(./   [hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: [ZF2] ZF becoming more "static", annotation and resources

weierophinney
Administrator
-- Artur Bodera <[hidden email]> wrote
(on Wednesday, 10 August 2011, 04:52 PM +0200):
<snip>

> With ZF2 I see it's becoming more and more "static". As a metaphor,
> I'd point to Drupal. Since early versions (Drupal 5+) it's been build
> around a cascading tree of classes, modules and hooks. In production
> mode, it requires to be "compiled", which takes a few seconds... .but
> this process effectively throws away disabled and unused parts of
> code. Drupal's got thousands of 3rd party plugins and modules and this
> compiling/caching mechanism allows for a heavily modded Drupal site to
> serve requests under 50ms, while much simpler ZF1 app on the same
> machine (I used) needed 100ms just to get through bootstrap.
>
> The downside of Drupal, Doctrine and such is, that each time code
> changes, it requires recompiling the whole app/suite (i.e. drupal
> cache regeneration).

One thing to note hear is that this is _deployment_ time compilation,
which is what we're aiming at as well. The code works fine in
development, but you compile for deployment in order to optimise. I
think this is a good tradeoff of developer performance vs operational
performance.

More below...

> This upside of ZF1 is that it loads and inits everything dynamically.
> It was slower, but this behavior was EXPECTED by me and I found it
> quite convenient in many cases. Of course one could argue, that app
> codebase changes once per 1 million http requests so we (lazy devs)
> could compile it anyways at deployment.
>
> There could be a small dispute, but we've seen one side being PHP
> HipHop which compiles the hell of everything and is unusable in
> development, while popular op-code caches (like APC) are a good
> compromise - so good that APC will be integrated into PHP6.
>
> My point being, ZF2 segues in direction of being smarter with its
> autoloading, caching, code generation and inspection. DI and model
> patterns being examples of where framework-assisted code fiddling is
> not an option. This means, that ZF2 will become more similar to
> Symphony1 than it was to ZF1 and WILL require developers to learn
> command-line tools to regenerate bits and pieces of their ZF2-driven
> app.
>
> I think it should be binary, either:
>
>  a) ZF becomes a cached/compiled framework which requires running
>     "# zf recompile" several times a day
>
>  b) ZF stays dynamic and does not require command-line operations at all.

Why binary? Why not a middle ground:

   c) ZF allows dynamic usage that utilizes primarily configuration
      during development, but provides tools for compiling various
      artifacts for purposes of deployment.

We're already targeting this. With the current DI implementation, you
can utilize a variety of strategies: Runtime (which uses Reflection to
create definitions on the fly), Compiled (which allows you to
pre-compile definitions), and Built (which allows you to
programmatically build definitions). Finally, an "Aggregate" definition
allows you to attach any combination of the above -- giving you a
complete story from development to production.

The same can be said of the autoloaders -- you can provide paths to
classmaps (which may be empty during development), in combination with
using the StandardAutoloader configured as a fallback.

The point is, I think we can, and SHOULD, target both ends of the
spectrum.

> I'd like to suggest, in ZF2,  that we make an OPEN, EXPLICIT statement
> about that and educate developers how they should handle ZF2
> installation and application maintenance.  AFTER we're clear about
> that, we can further optimize ZF2

Actually, that's not a viable approach. We need to build the
functionality for code generation/compiling/etc. in _as_ we build
components, because if we don't, we make different architectural
decisions than we would if that's also a requirement.

As an example, in the original DI prototype, I went for a "builder"
approach only. This made it next to impossible to re-use the DI
container with any other approach -- and as such, Ralph ended up
rewriting it entirely to separate out how definitions are created and
stored so that we could have multiple approaches.

If ZF1 taught me anything, it's that it's very, very difficult to
optimize the code base for performance and retain BC *after* the API is
already released. We need to build functionality surrounding performance
into the architecture.

> and use all the tricks Drupal,
> Symphony, Doctrine and other compiled frameworks use.
>
> We should streamline this process and make it as easy as:
>
>   # cd ~/myzfapp
>   # zf regenerate
>       Analyzing package usage... done
>       Analyzing component usage ... done
>       Generating classmap... done
>       Injecting dependencies... done
>       Creating skeletons... done
>       Introspecting database schema ... done
>       Updating models... done
>       Compiling locales... done
>       Compiling routes... done
>       Cleaning up... done
>
> We could build and expose a compilation-chain to all components that
> want to be accelerated with the caching/regeneration process.

What you describe above is exactly the goal -- as a _deployment_ tool.
Something folks can hook into phing, chef, capistrano, etc.

> If we had this settled, I'd suggest complementary feature that I've
> recently started to love:
>
>   _In-code configuration with annotations_

This should be possible, if the designs are clean and allow for it.
I'm not a huge fan of this approach, personally, but I can see some of
the benefits.

> Now this is both about simplicity, learning curve AND performance.
> Consider this short example.
>
> class IndexController extends Action
> {
>    /**
>     * @requires db
>     * @requires session
>     * @cache(expires="2d")
>     * @route /login
>     * @page id="login"  label="Login page"  visible=true
>     * @viewScript  "login.phtml"
>     * @acl  login
>     */
>    public function loginAction(){ }
> }

So, as an example of places where annotations don't work for me...

 * I think routing is something that should be done on the application
   level. You should think about your URLs, and define those... and then
   map them to the appropriate functionality in your app. Defining them
   via annotations can lead to collisions, or difficulty changing them
   to suit your needs.

 * I personally feel ACLs belong in the domain model. Mapping them to
   the controller layer makes re-use outside the MVC harder. That said,
   I could potentially see an argument for using annotations at the
   domain model level for enforcing ACLs.

 * State the requirements via setters and leverage DI instead. :)

What works for me in your example:

 * I like the idea of specifying a view script this way.

 * Caching is also something I typically think of at the individual
   action level, when considering output caching.

But, really, my commentary is off-topic: the point is: if we keep in
mind both performance and configurability as we develop components, we
can address both the needs of rapid application development and
production deployment performance optimization. I think it also makes
adding tools such as annotation usage more possible as it makes the
components more flexible in design (case in point: the DI container),
leading to the possiblity of using tools at either development or
deployment time to automate specific tasks (such as configuration or
code generation).

--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc

--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: [ZF2] ZF becoming more "static", annotation and resources

Wil Moore III
 First, I do agree that this is a bit off-topic; however, I offer my
thoughts as well:


> I think routing is something that should be done on the application level.
> You should think about your URLs, and define those... and then map them to
> the appropriate functionality in your app. Defining them via annotations can
> lead to collisions, or difficulty changing them to suit your needs.


I agree. I used to go back and forth on this as there is something that
seems elegant about defining routes on the action via annotation; however,
it falls down for a large application for the reasons you (Matthew) outline.
Also, it is a bit short-sighted to think of MVC actions as only class
methods as frameworks like Sinatra have illuminated.


> I personally feel ACLs belong in the domain model. Mapping them to the
> controller layer makes re-use outside the MVC harder. That said, I could
> potentially see an argument for using annotations at the domain model level
> for enforcing ACLs.


I personally have found that model ACLs are a bit "too" coupled where ACLs
on the services that expose the model(s) seems to be at just the right
level. Of course, YMMV depending on how you prefer to structure your
applications; thus, this is entirely debatable and situational.

I like the idea of specifying a view script this way.


Agreed.

Caching is also something I typically think of at the individual action
> level, when considering output caching.


A million ways to do it...most are bad. Sometimes I feel like publishing a
poll asking how many developers feel that _bad_ caching has ruined their
life :)
--
Wil Moore III

Best Practices for Working with Open-Source Developers
http://www.faqs.org/docs/artu/ch19s02.html

Why is Bottom-posting better than Top-posting:
http://www.caliburn.nl/topposting.html

DO NOT TOP-POST and DO trim your replies:
http://linux.sgms-centre.com/misc/netiquette.php#toppost
Reply | Threaded
Open this post in threaded view
|

Re: [ZF2] ZF becoming more "static", annotation and resources

Artur Bodera
On Wed, Aug 10, 2011 at 7:17 PM, Wil Moore III <[hidden email]>wrote:

>  First, I do agree that this is a bit off-topic; however, I offer my
> thoughts as well:
>

It's not OT.

Thank you Matthew and Will for elaborating.
I'm very happy for that approach. That should satisfy both speed-freaks and
"architects".
From case to case, opinions differ on whether runtime performance or devel.
performance
is more important. It's best to assume both are critical.



> > I think routing is something that should be done on the application
> level.
> > You should think about your URLs, and define those... and then map them
> to
> > the appropriate functionality in your app. Defining them via annotations
> can
> > lead to collisions, or difficulty changing them to suit your needs.
>

Collisions/overlapping can occur in pre-defined routes as well.
Configuration flexibility depends on the final annotation format. There is
nothing you can't set up in an annotation as compared to INI file.



> I agree. I used to go back and forth on this as there is something that
> seems elegant about defining routes on the action via annotation; however,
> it falls down for a large application for the reasons you (Matthew)
> outline.
> Also, it is a bit short-sighted to think of MVC actions as only class
> methods as frameworks like Sinatra have illuminated.
> I personally have found that model ACLs are a bit "too" coupled where ACLs
>
on the services that expose the model(s) seems to be at just the right
> level. Of course, YMMV depending on how you prefer to structure your
> applications; thus, this is entirely debatable and situational.
>
>
I'm sorry, but if we are in the process of easing the learning curve, then
annotations
are not only elegant, but very easy to explain. Of course designing a
"large-from-the-start" application, explicit definitions and configuration
are the most
"appropriate" way to do things, but again - you usually bring this up
together with
words like "large application" or "YMMV" or "ORM".


Large applications are rarely easy to disect, rarely friendly to debug and
extend.
I agree ZF should help large-application-developers by supplying best
practices
nicely packaged in functional components and "enterprise-grade
architectural
design". But large applications don't have the same requirements as smaller,
agile or more website-oriented projects.

With this in mind, it's optimistic to think that only 10% of applications
built with ZF
are, or will become, "large" applications. One of the goals of ZF2 (as per
wiki) is
making the framework easier to use. This means ideas like:

 * annotation-based configuration to speed up learning and expose basic
functional aspects without having to explain the inner-workings of
framework.
 * easy, configurable caching that will fit (pareto-like) 80% of (small)
projects and will noticeably speed them up.
 * default routes to get started ( + very easy to configure additional ones)
 * zero-configuration functionality for components (agreeing with M.Fowler
concept of having a functional component with smart, runtime defaults, while
allowing for detailed, explicit configuration)
 * self-optimization (implicit code generation/caching, compilation, etc.)
 * out-of-the box security (filtering, validation, auth, acl), possibly
annotation based etc.
 * more out-of-the-box functional magic (and of course easy to disable)

I'd measure the ease-of-use as the time needed to set up hello world
application and
then be able to extend it. As I've been studying new frameworks I've found
out that
there are some that seem obfuscated at first, but get easier every hour...
and there are
some that are as easy as pie at start, but will take ages to figure out
extension points
or MVC structure.

I believe annotation engine could be pluggable. Because it's in MVC nature
to be pluggable,
components could extend annotation parsing in MVC to expose even more
methods.

We could have i.e. (rough ideas):

 * @locale env, browser, $lang - to set up locale detection or use request
param for that
 * @filter  name=alnum
 * @validate name=StringLength(3,30)
 * @viewRenderer  php, json, xml     -- use resolver to figure which one
 * @viewRenderer  xml     -- force type
 * @serverMethod foo
 * @tool  foo  name|n=s age-i   --- enable action call from zend tool
 * @db-transaction
 * @multidb read


I like the idea of specifying a view script this way.
> Agreed.
> Caching is also something I typically think of at the individual action
> > level, when considering output caching.
> A million ways to do it...most are bad. Sometimes I feel like publishing a
> poll asking how many developers feel that _bad_ caching has ruined their
> life :)
>


ACL strategy should be a developer's decision. No one cares what I or you
prefer.
Same as with MVC implementation, selection of adapters and so on. This
means, that framework
should support all types of ACL integration and developers will choose what
fits them
best (as a compromise of performance and ease-of-use to granularity).

Smaller apps could go with:

class FooController extends Action {
  /**
  * @acl
  */
  public function barAction {}
}

... this would check authenticated user against "mvc:foo:bar" resource in
ACL.


class FooController extends Action {
  /**
  * @acl barGroup
  */
  public function barAction {}
}

... this would check authenticated user against "barGroup" resource in ACL,
as it could
refer to a number of assets (including model).


class FooController extends Action {
  /**
  * @acl-allow  admins
  * @acl-deny  Bob
  * @acl-deny  \App\Assertion\Inactive
  */
  public function deleteeverythingAction {}
}

.... this is injecting ACL rules without having to name the resource.

/**
* @acl-role
* @acl-parent users
*/
class User extends Model { }

/**
* @acl-role
* @acl-parent users
*/
class FoodCritic extends User { }


/**
* @acl-resource
* @acl-parent fruit
*/
class Banana extends Model {

  public function setUser(User $u){ }

  /**
  * @acl-allow FoodCritic
  */
  public function eat(){ }

}

.. self explanatory. hhmm.. gateway?



A.