February 8th, 2011

Liveblogging Symfony Live

5:35pm, Wednesday

Geez o man, I liveblogged the entire conference! I honestly didn't expect to keep it up for two full days. Glad I did as it helped me focus and learn more than I would have otherwise. Lots of great stuff, some stuff I'd argue with, but all of it fascinating. Looking forward to Symfony Live Paris in March!

Excited about Symfony 2, feeling up to speed and rarin' to go with it in the future. As Fabien announced there will be a stable API pre-release at Symfony Live Paris and a stable release is expected at the end of March.

One last note: please come by the second floor tearoom of the hotel at 7pm to pick up an Apostrophe CMS T-shirt (first-come, first-serve) and take part in our group Apostrophe photo! Thanks.

5:01pm, Wednesday

Fabien

"First option: I talk about Symfony 2 internals, second option: a discussion about Symfony 2. You ask questions and I don't answer."

"So. Internals"

Symfony2 (note no space) circa 2007

He explained it then the same way it works today (in core concept...)

Symfony 2.0 philosophy: "our job is to convert a request to a response. That's all."
interface HttpKernelInterface
{
  @return Response
  function handle(Request $request, ...);
}
"Symfony2 Internals explained with Interfaces."

Symfony 1 configuration files allow quite a lot of flexible overriding...

"An interface is what (or how) unrelated objects (use to) communicate with each other"

Stability, backwards compatibility...

"In Symfony 1 we have exactly 4 interfaces" (meaning that they didn't use the interface keyword very much and he wishes they had used it more)

Symfony2 Components: 112 interfaces!

A list of more than 40 "major" interfaces.

Everything that is declared as an interface can be substituted with another implementation of that interface. No need to have the same base class. So this makes Symfony 2 much easier to mash up with other things than Symfony 1.
interface ControllerResolverInterface
{
  function getController(Request $request);
  function getArguments(Request $request, $controller);
}
So the big picture here is that everything is pluggable in a profoundly flexible way. All the important pieces can be swapped out via DI.

Fabien is showing us the interfaces that match URLs, generate URLs, etc.

No object routes in Symfony 2, they don't need them (in yesterday's talk he explained how routes can be clever enough to figure out an id parameter can be substituted with a slug etc).

Showing the implementation of the Controller name parser

core.view

There is no default implementation in the core. If the controller does not return the response then you can listen to the core.view event and a listener can convert the return value (something that isn't a response) into something reasonable (an invocation of Twig with the specified data).

core.response event is notified when a response is returned. Used for the debug toolbar, edge side includes, etc.

Showing the entire implementation of the MVC framework (OK, the top level workflow) in one slide.

Implementing the EngineInterface lets you provide alternatives to Twig and PHP templating:
interface EngineInterface
{
  function render($name, array $parameters = array());
  function exists($name);
  function supports($name);
}
Fabien creates a small framework in one page using the default implementation of Response and the Framework class

Shows some wild annotation stuff on methods using FrameworkExtraBundle, I think we will fall in love with it

https://github.com/sensio/FrameworkExtraBundle

Question: when will Symfony 2 have a stable release?

"The plan was to release Symfony 2 during the Symfony Live conference in Paris. That won't be the case. The current plan is to have the first release candidate available during the conference. Which is really quite the same. Because the API is frozen (at that point) so we won't wreck things afterwards. Except if there is a big mistake.

Why we are a bit late? In the last couple of months more and more developers are really using Symfony2 for live websites and we have a lot of feedback and contributors. They have a bunch of great ideas they want to implement now. If you have a look at the timeline the activity in the last couple of months has been huge. We are still working on the interface. The way you configure things... we want to make sure this is really what we want. We can expect Symfony 2 final release by the end of March."

4:21pm, Wednesday

Pablo Díez, Doctrator

Doctrator adds behaviors to Doctrine 2

"Principle is to avoid writing LORC" (Lines Of Repetitive Code)

He points out that Docrine 2 is powerful and fast but lacks the behaviors of Doctrine 1

Generates classes and maps them with Doctrine2. You only have to tell it the configuration of the classes (?)
Model\User:
  columns:
    id: { id: auto, type: integer }
    username: { type: string, length: 50 }
This generates your object and lets you get to work.

(Doctrine 2 already can generate your schema from annotations in your class, which means writing your class is writing your schema; and can also generate getters and setters for you. So I'm not sure there's a win here so far)

Doctrator uses Mondator to define your PHP classes.

Tough to type as fast as Pablo is going, but he's covering how Mondator creates methods dynamically based on your schema, basically restoring the Doctrine 1.x workflow.

Doctrator seems to bring back the array accessors.

Core features: ArrayAccess, PropertyOverloading, ActiveRecord, Behaviors.

ActiveRecord gives you back save() I guess? Yuck, I don't want save(), I want flush()

Doctrator uses base class to separate generated code from your code (again, reinventing Doctrine 1)

ActiveRecord can be "good and bad"

"Doctrator uses a global object to save the EntityManager" Static method call. He is breaking Dependency Injection. Why would you want to do that?

OK, on to behaviors. He shows that it brings back Timestampable, adds Ipable (saves the created and updated ip address), Hashable (saves a unique hash in each entity), Sluggable, etc.

To take advantage your classes must be created with Doctratable configuration files. Would be more interested if I could do it with Doctrine annotations.

Some methods for managing order of items: isFirst(), isLast(), getNext(), getPrevious(), swapWith(), moveUp(), moveDown()

Taggable behavior. API is similar to the old taggable behavior in Doctrine 1

(Can this stuff be used with classes not created using his configuration files?)

Doctrator in Symfony 2

DoctratorBundle
doctrator.config:
  extension:
    array_access: false
    property_overloading: false
    active_record: true
    behaviors: true
Etc.

Nice that you can turn off the parts you don't agree with.

ActiveRecord is mandatory for some of the behaviors. Also, your classes must be generated by the configuration files (this was my question), you can't go with a handwritten entity class plus annotations in the usual Doctrine 2 style.

3:11pm, Wednesday

Kris Wallsmith, "Introducing Assetic"

An asset manager for PHP and Symfony 2 in particular

He's at OpenSky, like Jon Wage and crew. He's been doing this a while. He's a member of the Symfony core team. All that.

shopopensky.com

PHP 5.3 + Symfony2 MongoDB + Doctrine MongoDB ODM MySQL + Doctrine2 ORM Less CSS jQuery

"Symfony2 is FAST... But you can still f*** that up"

"We build tools that encourage best practices"

Dependency injection, proper caching with edge side includes, test-driven development, don't repeat yourself, keep it simple stupid (or "SVP")

"If you haven't optimized your frontend, you haven't optimized"

Speed also matters for SEO.

Some good tools: CoffeeScript, Compass Framework, CSSEmbed, Google Closure Compiler, JSMin, LESS, Packer, SASS, Sprockets, YUI Compressor

Modern tools let you declare dependencies on other files in JavaScript

The ones written in PHP (as Stefan told us it does not matter): none? (Not quite, there are PHP LESS and JSMin implementations. They are lessphp and JSMin.php. They are bundled with Apostrophe)

Assetic:

$core = new AssetCollection(array(new FileAsset('/path/to/jquery.js'), new GlobAsset('/path/to/js/core/*.js'))); $core->load();

header('Content-Type: text/javascript'); echo $core->dump();

They also incorporate the YuiCompressorJsFilter to compress JavaScript

Dependency injection is inherent in the design, you inject what you need when constructing AssetCollection

An asset collection has filters around it and can be wrapped in another asset collection. This is neat flexibility and certainly more powerful than what we have in Apostrophe in Symfony 1.4

new SassFilter() brings Sass into the processing of files

You can nest AssetCollections that run through the SassFilter and those that don't, then run the whole shebang through the YuiCompressor. And nothing touches the filesystem until the very end when dump() is called. Very lazy loading, which is good for performance

Asset Factory

You can use a factory object to more easily process lots of JS files without building objects explicitly for each one.

Supports HTTP caching. You can call $core->getLastModified() to compare to $_SERVER['HTTP_IF_MODIFIED_SINCE']. And you still haven't touched the filesystem yet (well, you haven't created any files yet, you have looked at some). This is good with a shared reverse proxy. They also have filesystem caching which is better if you don't have a shared reverse proxy.

Best: Static Assets

You can integrate Assetic with Twig:

{% assetic 'js/*.coffee', filter='coffee' %} <script src="{{ asset_url }}"></script> {% endassetic %}

Kris is talking about the FormulaLoader class which... I'm not sure I get this bit (:

AsseticBundle: Symfony 2 Integration

The Assetic bundle lets you find your stuff via Symfony bundle names.

In debug mode, you get multiple link tags to individual CSS files so you can see what's going on more easily.

"The symfony asset helper is really, really cool. And you get a lot for free."

You can configure "multiple asset domains." Large sites will typically serve assets up from four different asset hostnames because this gives you more simultaneous connections in typical browsers.

There is also a cache buster. Namely that you change the resource name by adding a query string to it so it is versioned:
app.config:
  templating:
    assets_version: 1.2.3
    assets_base_urls:
      - http://assets1.domain.com
      - http://assets2.domain.com
      - http://assets3.domain.com
Support for PHP templates is coming soon. "Ugly, but you should use Twig."

"Assetic is a killer feature of Symfony 2."

(An alternative is the minify project, which is the basis of the minify support we integrated into Apostrophe. It's a google code project that's been around for a while. Also the lessphp LESS implementation. Pure PHP. By using Symfony's distribution of it you can get it automatically with Symfony 1 projects. But Assetic looks like the wave of the future for Symfony 2 for sure. I pointed out these and Kris's response was that "PHP is not for everything," which is certainly true. I'm not sure raw performance is the key issue for a minifier since once things are minified they stay minified... a slow minifier does slow down development though because you are tweaking often)

2:30pm, Wednesday

Marc Weistroff and Emmanuel Cohen, "nice performance using Symfony 2 cache wrapping a Symfony 1 application"

Their client, L'Express Magazine, www.lexpress.fr. In the top 3 French online news sites, quite famous. So a lot of traffic, so caching is an issue

"Talks about hot topics, talks about hot people..."

The project is a cultural knowledge base. A bridge between hot nes and cultural knowledge. An extension of the main site.

They address performance by using Symfony 2's ESI caching features. They use the Cache-Control: s-maxage header.

Cache issues: finding out what's stale, selective expiration (or validation)

ESI example:

<esi:include src="/movie/dogma/critics" />

Processed by a proxy

Transformed into HTTP request to the application

Response is inserted in place of the ESI tag

(Symfony 2's built-in implementation is available if you can't afford to put a frontend proxy like Varnish into your system now. Actually only Varnish really implements this well right now I think, so your choices are Varnish or the built-in thing... Squid doesn't really get it)

/movie/dogma -> proxy miss -> /movie/dogma, with Cache-Control: s-maxage set

200 seconds later, there is a cache hit so the proxy just returns it because it's new enough, PHP doesn't have to get involved.

How did they get symfony 1 to run within symfony 2?

They had to address reentrance issues. Overriding the super globals ($_REQUEST etc.), creating a fresh new context each time. Replacing sfRenderingFilter by a noRenderingFilter class in factories.yml. Create a Symfony2 response object and return that instead. (Apostrophe would have trouble with this because we sometimes call exit(0) after an AJAX response. We do this thing because Symfony has a tendency to get clever and output stuff, such as the web debug toolbar, that is not wanted and can only cause problems for your AJAX response. But obviously we need a cleaner way to play nice if we choose to use something like this)

"You have to design your app to serve HTML fragments!" Not obvious with Symfony 1. "Don't think about partials or components, think actions! Actions render HTML fragments. Actions must define explicitly the response cache headers." Sounds like a big rework of your app, why not just transition to Symfony 2? I can see that you still save time by not rewriting your entire model layer... you could refactor controller and view code too.

Did this start out as pure Symfony 1 and transition?

I asked. No, they started in May when Symfony 2 was too up in the air to really use yet but the caching bit was solid. So they had a mixed architecture in mind from the beginning.

A neat hack, although they would do it in all in Symfony 2 if they started over today.

1:48pm, Wednesday

"You can write a daemon in PHP but that's not optimal." In PHP 5.3, isn't it a reasonable thing to do at last? I asked. Yes, it is, but it's not the right choice for a daemon that handles hundreds of socket connections. That's a good point although I had success with PerlMUD. You could perhaps do something similar in PHP. Not the way to write a frontend proxy of course.

memcached seems to be in use by a majority of the room. (It was originated by the founder of livejournal.) It's a distributed in-memory cache across many servers.

APC also can give you caching for data (not just automatic caching of bytecode). APC is on a single server but otherwise not unlike working with memcached. (Symfony has built-in sfCache subclasses for both of these, you can substitute them for sfFileCache and gain a lot of performance)

XCache is an alternative to APC. (Is there a bytecode cache that uses files rather than memory? I ask because it would be a candidate solution for rackspace cloud sites, which currently has no bytecode cache, and it really hurts)

Talked about Zend Server 5.0 and its high performance options like job queues and so forth, however that's in their commercial version.

Continuous Integration

Continuous integration is the idea that you can avoid packaging specific "versions" and just keep your project development going in the "trunk"— if you have ferociously consistent unit testing and deal with all bugs now. Continuous integration tools tend to push the testing job to the background on a CI server where it doesn't become something the developer has to wait for.

CruiseControl used to be the big winner, followed by phpUnderControl, a configuration tool for CruiseControl.

Hudson was a Sun CI project, an independent fork is known as Jenkins. @skoop says he set it up in about an hour using http://jenkins-php.org/

Monitoring

Suggests we just google for server monitoring tools. I like aremysitesup.com it's free, cheap for more than three sites and better notification options. Very simple and very effective, has always told us when our major sites were in trouble in a timely way.

@skoop: Zend Server 5.0 also has a monitoring tool

He also points out Nagios as a server status monitoring tool.

Issue Trackers

Jira: a neat issue tracking system, very functional and friendly and configurable... resource usage is really high. Commercial but licenses start from $10 for ten users. Good integration with source control. They offer free licenses for open source projects. (I don't find it super friendly myself)

Trac: written in Python. Good system, built-in wiki, version control integration. No support for multiple projects. (Separate trac installations aren't that hard to achieve but crosslinks would be nice sometimes. Note, we use it for trac.apostrophenow.org, we skinned it nice)

Redmine: issue-tracker that just barely does some project management.

"Two words: common sense. Don't use PHP when it doesn't make sense."

"The elephant is the most important tool for every PHP developer." Awesome slides of hordes of PHP stuffed elephants at the beach. "If you run into a problem, talk to your elephant."

1:39pm, Wednesday

Stefan (@skoop) is telling us about tools outside of the PHP world that we ought to consider using in our PHP work.

"Zend Lucene search (built in PHP) is nice but as the index gets really big it can get really slow." He called on me to say that you ought to call the optimize method nightly from a task, they are really not kidding about that and Zend Lucene works a lot better if you heed it.

But: Apache Solr is certainly faster. That's in Java. A process runs int he background.

There are other neat implementations, like Xapian. Xapian is a C++ extension to PHP that provides search without the need for a background process. You do have to install the extension of course. But apparently it's more work than coding for Apache Solr (once you get past the task of setting up the Solr daemon).

11:45am, Wednesday

Fabien heckles us all for not having read the HTTP specification. Awesome.

http://tools.ietf.org/wg/httpbis/

(I have read some versions of it... once upon a time... honest)

It's exactly the same as the HTTP 1.1 specification but they have tried to document the reality in a readable way.

"Tonight! Not next week! Tonight!"

A little review: the client sends a request to the server, the server sends back a response to the client. "Let's play with HTTP headers"

GET / HTTP/1.1 Host: foobar.com

Talking about the basics of HTTP. Method (GET), URI (/), protcool (HTTP/1.1), headers (Host).

Caching in HTTP.... http expiration, http validation, fresh vs. stale

Caching works only with "safe" methods: GET and HEAD. You can GET things safely over and over. (Lots of people screw this up by using GET requests to do things like deleting items, but those people are wrong. Yes I've been an offender. Of course there are hacks like using cache headers to say "never cache me" or guaranteeing a unique one-time URI, but you should really just use POST for verbs)

Expires headers can't be more than one year in the future.

Mostly useful with static assets (images, css, js) because they can be set to cache with an extremely long expiry time.

A better way: the "Cache-Control" header

header('Cache-Control: max-age=5');

(A thought: if browsers honor the cache, doesn't this make it reasonable to send your assets from PHP after all because you will only have to send them once? Probably not because most visits are from newcomers, drive-by surfers etc. You still need to insulate PHP from delivering assets Apache can deliver on its own... But if you have a frontend proxy like Varnish, then it will cache that first delivery of the assets for *all* users based on the expiry headers. Then you really *can* deliver everything "from PHP," which opens up some fascinating possibilities)

By using ETags you can empower the browser or proxy to ask whether the item being requested still has the same ETag or not, which is a way of saying "*tell* me if it has changed" instead of just not asking until a certain date.

If-None-Match: abcdef -> HTTP/1.1 304 Not Modified

"If the item hasn't been modified (still has the same etag), just tell me. If not give me the full response"

Last-Modified / If-Modified-Sicne

"Expiration allows you to scale as fewer requests hit your server (and client speed is better too). Validation saves bandwidth (and CPU resources, if you can compute the ETag without actually doing all the hard work of making the full response, which is often true if you have things like an updated_at column + id column + class name that you can checksum).

By default you get this with PHP when sessions are present:

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

That's because PHP is trying to make sure cookies are unique to individuals. If the cache were to store them then the cookies would be shared. (What happens when you change the cache headers when generating a response for something like an image but a session cookie is present? Do you need to clobber the cookie somehow?)

There are *many* possible caches between you and the browser. Browser cache, proxy cache, gateway cache, reverse proxy cache, surrogate cache (?), http accelerator (like Varnish).

The point of these is to return content with less overhead in time and space. The catch is to use headers responsibly to make sure they never cache what they shouldn't or fail to cache what they should

Use headers right and you can stack as many proxies as you want (those you know about and those you don't, such as proxies at the corporate gateway of the end user)

Ah here we go. "What if you cannot cache whole pages? What if a page has 'more' dynamic parts?" You can cache the page content but not the login prompt, you can cache the rich text slot but not the feed slot (or not for as long)...

The answer is "Edge Side Includes." There is a standard specification for fetching *parts* of a webpage. A lot like AJAXing in part of a page in concept, but without the browser being involved. This permits the various proxies to request updates of the parts that have expired without requesting the entire page.

Symfony 2 provides a poor-man's implementation in PHP (complete, just not as fast as a dedicated proxy like Varnish) of HTTP cache, so you can use this stuff even if you don't have Varnish (because you are on shared hosting).

"There are only two hard things in Computer Science: cache invalidation and naming things. — Phil Karlton"

If you use validation there is no problem because the server is asked every time (and only has to verify whether the resource has changed). In this scheme there is no "invalidation" because you always have explicit "validation." Use that when there's any chance you'll have to renege and update something sooner rather than later. When there isn't (you hope), use an expires header.

11:00am, Wednesday

Rendering a form!

(Please note: I am leaving the angle brackets off my HTML tags for liveblogging convenience, Twig does not do this crazy thing, don't worry.)
{% for widget in widgets %}
div class="widget"
  h3 {{ widget.name }} /h3
  div class="summary"
    {{ widget.body }}
  /div


{% else %} i No widgets were found /i {% endfor %}
The 'for' statement has an 'else' clause for the case where there are no widgets! Outstanding!

There is a "cycle" function that cycles through the given array items on each consecutive call:

div class="widget {{ cycle(['even', 'odd'], loop.index) }}"

(Interesting, but a little magical. What happens if you nest a block that has its own "cycle" call? Does it get an independent cycle counter?)

OK, it turns out that loop.index is implicitly declared in all for loops. Every nested for loop establishes a new scope for its loop variable (he hopes...) and if you really want the outer one in the inner scope you can 'set' a variable to make it visible.

{% if loop.index is divisibleby(4) %}

Whee! Very readable (though not quite as brief as %). "Even management knows what this does"

Now: pagination...

{% for page in radius(current, 2) %} <-- Ha, I watched him define this function Monday night

That gives you natural numbers surrounding current, 2 below and 2 up... for 5, you get 3, 4, 5, 6, 7.

{{ loop.last ? '' : ':' }}

Output a : only if this is not the last pass through the loop.

Ha, yes, now he's covering that radius doesn't officially exist in Twig. His chance to show us how to create a Twig extension.

I did this in SillyCMS, my alternative Symfony 2 sandbox. You can find my complete example there. His example registers a method, I registered a function. Tip: functions *do* get namespaced, so yes, you really can register them as part of a Twig extension in a Symfony service.

"Because Twig is totally sandboxes, Twig is a perfect fit for a CMS."

Wow, you can easily override the lexer to change the syntax for comments, blocks and variables. But I wouldn't because the Django syntax is reasonably established.

Things he didn't have time to cover and wishes he could: inheritance, macros, subscripts.

10:43am, Wednesday

It's easy to decorate a template with a layout. The template expressly extends the layout template:

{% extends 'layout.html.twig' %} {% block body %} ... stuff ... {% endblock %}

The layout also has a {% block body %} which can be empty or contain default content. The extending template overrides the value of that block. There can be multiple blocks— sidebar, etc. This replaces Symfony slots in Symfony 1.x.

"The template system is meant to express presentation, not program logic." - Django documentation (Twig syntax is borrowed from Django)

Compiles down to a surprisingly comprehensible PHP class file for performance pretty much equivalent to writing it by hand (if you're pretty good).

Going into the Twig syntax. It has two basic forms: {% do something %} and {{ say something }}. The {{ }} syntax is equivalent to PHP echo statements.

This is a great template designer-friendly talk.

Some cool examples:

{{ name | upper }} <--- The | character precedes a "filter," like upper that converts to uppercase {% set foo = 'bar' %} <--- set a local variable in the template

A neat example with the range function and the reverse filter and something nested in a for loop:

{% for i in range(0, 10, 2) | reverse %} * {{ i }} {% endfor %}

That gives you:

* 10 * 8 * 6 * 4 * 2 * 0

Supports hashes and arrays, just like PHP. The syntax is actually JSON/JavaScript style. Here's a foreach statement:

{% for key, day in { "mon": "Monday", "tue": "Tuesday" %} {% endfor %}

Twig has operators like +, and, in, etc., and you can create more (! Wonder how it handles order of operations).

I wonder why Twig has filters instead of plain old function calls? I guess they just like the feel of this:

{{ some_date | date('Y-m-d') }}

It's like Unix pipe syntax of course, which is nice.

10:33am, Wednesday

@weaverryan is telling us "how to be dangerous with Twig." Great slide of his original epic title for the talk.

"I feel like this is what I should have known when I started."

"We have six plugins for every task in Symfony 1 and they all sort of suck. We have to stop this. Let's work together, let's make Symfony great. Rails has great libraries, it's because they work together." Gave Apostrophe a shoutout, reiterated that "we have to stop writing our own CMSes," a point I also made yesterday (with regard to the guy who insists on writing his own CMS as a one-off for his client project).

Works at iostudio, a marketing solutions company in Nashville and Washington DC, 150 employees (and hiring Symfony people...).

Explaining the basics of why templating engines are good. I get that, but I'm glad he's making the point. "Should give me helper functions, keep everything short, put logic elsewhere, support inheritance to dress templates with layouts" (and nest them in Twig's case, yay)

"Why not just render PHP templates?"

It's a hack. There is no template inheritance or it's faked and involves a bunch of ugly output buffering and can't be kept from breaking out of the box if it's coded badly. "PHP templates suck in any global variables or functions available." (True, and we depend on it quite a lot, but if we could inject them more cleanly and less painfully... Twig lets you do that)

Overriding helpers is impossible and/or hacky and/or lame in Symfony 1.

"We need the brevity of templates with the isolation of object-oriented programming." Yup. "Twig actually compiles into a PHP class"

9:48am, Wednesday

A quest for breakfast made me ten minutes late this morning. [Hangs head in shame]

"Symfony 2: from the trenches" (Kris Wallsmith, Jon Wage, more OpenSky guys— Lukas Kahwe Smith doesn't seem to be onstage as originally expected) is addressing dependency injection a lot. They are showing examples of how and why to use it. The key thing here is that you can declare your classes to Symfony as services and easily change your service configuration files later, switching the classes and parameters to be used for those objects. This is a bit like factories.yml in Symfony 1, but drastically better.

The slides for this talk are here and cover a lot of interesting ground, check them out.

They are showing how to retrieve references to objects without database queries. Not entirely sure how this works.

Raw SQL queries are easier in Doctrine 2. You can create a ResultSetMapping that decides how results should be mapped to objects so you can write absolutely any SQL query you wish and map the returned columns to whatever you want.

Showing a simple search engine. The KeywordListener class has a preUpdate method that looks for instances of HasKeywords... one of the big themes of this conference seems to be "MongoDB is everywhere, get used to it."

Performance tips. Dump routes to apache rewrite rules, Apache is faster than PHP. "Cache warming." Do not add optional services to controllers (?). Do minimal work in the controller, let templates pull additional data as needed (lazy loading). use a bytecode cache with ClassCollectionLoader (?).

Apparently there is a task to dump your routes as apache rewrite rules, that is very cool.

Slide of tweet: "here's a New Year's resolution: to *always* work on an existing Symfony2 bundle and never recreate my own." @weaverryan

http://github.com/friendsofsymfony UserBundle and FacebookBundle Liip http://github.com/liip FunctionalTestBundle, MultiplexBundle ViewBundle Sonata http://github.com/sonata-project BaseApplicationBundle

http://symfohub.com

What is a CompilerPass?

Still fuzzy on dependency injection?

http://fabien.potencier.org/article/11/what-is-dependency-injection

If you keep up with Fabien's Symfony 2 repository:

http://docs.symfony-reloaded.org/master/ <-- up to date docs

"Why is Symfony 2 faster than Symfony 1?" "The bottom line of bootup time is as low as it can possibly be (because of lazy instantiation). Also the performance of Doctrine entities is drastically better. ActiveRecord is an inherently flawed design (multiple separate save calls). You want to build it all in memory and flush it all at once (in a transaction). The number one thing is lazy instantiation. Also 5.3 makes it much more performant" (wish this wasn't a word now)

6:06pm

Fabien is showing us the web debug toolbar of Symfony 2.0.

The web debug toolbar is much more powerful. Also at the bottom of the page where it doesn't drive you nuts.

Click a button and you're in the Symfony Profiler which is very impressive looking. You can see all queries in detail a lot more easily.

Aaand "I think that's it," says Fabien.

Wow. Blown away by the annotation stuff and by the naturalness and power of the caching system. Surprised to see so much built-in "smarts" because I had expected "kill the magic" to mean a bit more tedious (but precise) configuraton. But the built-in smarts make great sense.

"The trouble is that Symfony 2 is not stable yet, and the documentation is not quite there yet." We'll get there, especially with this many people newly motivated to help make it happen.

A question about what to do if you can't use a real frontend caching proxy and you want the caching behavior. There is a built-in HTTP cache implementation available which he'll explain tomorrow.

"From Symfony 2 I want two things. First the book, which will teach you how to use Symfony in a very stable way with best practices.

5:24pm

Fabien has fixed his MySQL daemon (:

Schedule change: Symfony 2 and Symfony 1 cooperation/performance talk tomorrow. Konstantin won't be ablie to present on behavior-driven development. Fabien will talk about Symfony 2 internals instead.

Back to our post action:

Symfony 2 knows that when there is a Doctrine class as the parameter and there is an id in the route (the primary key of the post table), it can get the relevant object. If not, @ParamConverter() notation is available to give more information about how to convert from the request attributes to the parameters of the function. (Yay, convention over configuration is back. But what happens if the id is invalid? A 404 I assume, which is pretty reasonable. What happens if you invoke an action from a template to nest content?)

We're looking at templates now:

<a href="{{ url("home") }}">Home</a> <h1>{{post.title}}</h1> <p>{{post.content}}</p>

We can give the index action an explicit route name in the annotation:

@extra:Route("/", "home")

You can annotate the entire controller class, creating a prefix for all route names:

@extra:Route("/blog")

Alex: "can you set @extra:Template for the entire class?" Fabien: "yes, or it will be yes"

Tom: "if I call a the view action with 'render' in a Twig template, does it expect an id or a post?"

(Fabien explained that in Symfony 2, you can call actions from templates to nest them, removing the redundant "partial" and "component" concepts from Symfony 1)

The answer is:

{{ render "AcmeBlogBundle:Post:view" with { 'post': $post }}

Works.

{% render "AcmeBlogBundle:Post:view" with { 'id': 1 } %}

... Also works (:

"The caching on this blog will be huge. So I need some kind of cache"

@extra:Cache("maxage=600")

(On the index action)

Done!

"If you can afford to install varnish for instance, this page will be cached for 10 minutes (by the varnish frontend proxy)."

Let's say we decide to include the index in the view action:

{% render "AcmeBlogBundle:Post:index %}

There is a problem. The blog post can be cached for a day, but the index for only 10 minutes. This is not fixable in Symfony 1. In Symfony 2 it can be fixed:

{% render "AcmeBlogBundle:Post:index with {}, {"standalone": true %}

This is an "edge side include." It means "this piece is standalone, it should be independently cached." And this hint comes through to the frontend proxy so the backend server gets beat up less for parts of the page that don't need updating.

Let's look at a form:
@extra:Route("/create")
@extra:Template
public function createAction()
{
  $form = PostForm::create($this->get('form.context'), 'post');
  $post = new Post();
  $form->bind($this->get('request'), $post);
}
The get method gets an object from (boo) the dependency injection container. In this case it just means you are getting the form context (?).

create.html.twig:
<form action="" method="post">
  {{ form_field(form) }}
  <input type="submit" value="Save" />
</form>
"It's not mandatory to create a form class but I think it's best practice." (You can instantiate Form and add fields to it rather than subclassing)
namespace Acme\Blog\Form;
use Symfony\Component\Form\Form;
class PostForm extends Form
{
  public function configure()
  {
    $this->setDataClass('Acme\Blog\Post');
    $this->add('author');
    $this->add('title');
    $this->add('content');
  }
}
Some entertaining live assistance from the audience getting namespace imports out of the way

The form has appeared!

You can specify an alternate field class by creating a form field instead of just passing the column name:

$this->add(new TextareaField('content'))

return array('form' => $form); is a pretty concise way to set up Twig. Nice.
if ($form->isValid())
{


}
"Is this all working in the PR6 sandbox?" "You need the FrameworkExtraBundle. That gives you param converter and the annotation stuff."

Questions about XML versus YAML for configuration. "We now have validation for YAML. Not completion (XML is good for that)." Not sure why you'd need completion in a format as terse as YAML (:

Decorating a template with a layout:

{% extends "AcmeBlogBundle::layout.html.twig" %} {% block content %} ... {% endblock %}

Twig gets compiled to a PHP class so it is approximately as efficient as a PHP template.

5:00pm

Fabien continues to amaze with his crash course in PHP 5.3, Symfony 2 and radical simplicity.

Asked about the @ sign in:

resource: @AcmeBlogBundle/Controller/

@ resolves a bundle name. He explains that this can come from AcmeBlogBundle or a bundle that explicitly extends it. You can also write plain old file paths but that prevents you from moving things around.

"The next step is to map these classes to the ORM. You can use annotations but you can also use YAML or XML."

Inside class Author, annotation comments now make Doctrine aware of the database column types for the properties. The getters and setters stay the same.

"You can still use post and author without Doctrine, the code is the same (only annotation comments are new)"

(Doctrine 2 works with Plain Old PHP Objects, no need to subclass anything)

We can use a Twig template instead of the simple new Response() method.

Twig's syntax is borrowed from Django, don't reinvent the wheel

You can use annotations to automatically guess what template to use (can I do this for an entire controller with one annotation?)
@extra:Route("/{id}")
@extra:Template
public function viewAction($id)
{
  return array('name' => $id);
}
Now we want a real post:
use Acme\Blog\Post;


@extra:Route("/{id}") @extra:Template public function viewAction(Post $post) { return array('post' => $post); }
But Doctrine 2 knows nothing about this. So we need to bind Doctrine 2 with this post. So we need to configure the mapping int he configuration (config.yml):
doctrine.orm:
  mappings:
    Blog:
      prefix: Acme\Blog
      type: annotation
      dir: %kernel.root_dir%
Now we have a MySQL server error, "that's better" (:

MySQL is not running or misconfigured... ruh roh... "ohhhh" he starts it up (:

It won't start. Oh dear.

He's switching to sqlite on the fly! Good man!

Now he needs to create the table... ./app/console doctrine:schema:create... "No Metadata Classes to process" O NOEZ

"You know what? Let's have a break." We're going to get MySQL launched (:

4:30pm

Fabien Potencier

"I've decided not to talk about... what I'm working on today... because I've been talking about Symfony 2 for two years... and because Symfony 2 is not finished yet... and I've been talking about the architecture and some people think Symfony 2 is really complex. Instead of talking about our architecture, I will demonstrate how you can create a blog with Symfony 2. This is a live demo. We'll see if it works. Probably it won't. Ask any question you want."

Show of hands on people who already have tried Symfony 2, then how many people have it in production. To those people: "I'm sorry. I mean— I've broken backwards compatibility every day. And the last two weeks, I've broken it twice a day. I love you guys because thanks to you Symfony 2 evolves really fast." (They have not declared it stable so this is kosher (: )

"I don't know if Symfony 2 is an MVC framework and really I don't care. What matters is separation of concerns."

"Where does routing belong? M, V or C? I don't care, really. What matters is to separate things."

Showing us the Author class. namespace Acme\Blog; class Author { private $name; public function getName(); public function setName(); public function __toString(); } (Getter, setter, string converter, simple as you'd expect them to be)

It's in a src/Acme/Blog subdir, as the autoloader expects

The Post class is just as simple, with getter and setter for title, content, author. He uses a 'use' statement to import a class so he doesn't have to fully qualify it with slashes.

"If this was actually a bundle—"

"First, I don't know about bundles. Second I don't know about entities. I don't care. (For the moment) I just want to define authors and posts. That's all. At some point I will need to persist those classes to a database but not now."

Shows simple PHP code to use the author and post classes.

"To create a new project you need to download Symfony" (not use a sandbox). "You download Symfony and then embed it into the project, or vice versa. In Symfony 1 it is painful. We have a ten-page tutorial on how to install Symfony the right way. Also the server requirements, etc. Symfony 2 will be different. There will be distributions, like Linux distributions. You don't download the kernel. A Symfony 2 distribution is the kernel, a selection of bundles (framework bundle, doctrine bundle), a sensible default configuration, and probably an installer to avoid the command line interface. "We can imagine a phpBB distribution, a blog one, a CMS one or whatever."

tar zxpf symfony-standard.tgz

"And that's all. It is installed. You don't have to do anything else. It just works"

"You can use YAML, you can use XML. You can use PHP, you can use Twig. You have to make a lot of decisions. With a distribution there are sensible defaults (you do not have to make these decisions)."

Showing the directory structure after extracting Symfony. The src folder is as yet empty, this is where the project specific code will live, separate from vendor (this is a change from PR5)

"I want to import my model to this application. I put them in the src directory."

Adds the src folder to the registerNamespaces call in app/autoload.php

"(In Symfony 1.x) you can create a module or a plugin. In Symfony 2 there is just one way, the plugin way, but they are named bundles. We have bundles because bundles are everywhere (including in your project-specific code). They are first class citizens. Even the core framework is a bundle.

"To create a blog I need to create a bundle. I can use a command line interface:

./app/console (Lots of command line option goodness appears)

./app/console init:bundle init:bundle Acme\\BlogBundle src/

(You need a double slash because \ is the escape character unless it is itself escaped at the command line)

This gives you a default controller under BlogBundle.

In Symfony 2 an action should return a response object. He changed the default to just:

public function indexAction() { return new Response('Hello Symfony2'); }

(He needed use Symfony\Component\HttpFoundation\Response to bring it into the namespace)

Added his bundle to the configuration.

Now he creates a route so you can get there (routing.yml):

blog: resource: @AcmeBlogBundle/Controller/ type: annotation

It works!

"What does type: annotation do?"

"In Symfony 2 everything is explicit. If you want the routes from a bundle to be enabled, you have to say you want to use a resource and that it should look for route annotations (right in the classes). You can also say type: yml and it will look for YML files in a specified directory."

Let's go back and look at those annotations:

class DefaultController extends Controller { /** * @extra:Route("/') */ public function indexAction() { return new Response('Hello Symfony2'); } }

(Wow! the route is pulled from the controller's annotation comment so you can finish your thought all in one place. This has the negative effect that you can't manage the routes in a configuration file intended for nondevelopers, but if that bothers you, you don't have to use this feature. Also you should still be able to override it)

3:32pm

Nils Adermann: PHPBB4: Building End-User Applications With Symfony 2

A fun title font after so much Helvetica.

Note: the 5:30 session has been cancelled. No talk on relational data in the cloud. Bummer. Fabien will speak at 4:30 instead.

PHPBB is more than ten years old! Version 1 was released in 2000. Published on SourceForge. Back then there were few options so it took off quickly. Most were in Perl at the time because it wasn't typically integrated with Apache (yes I know about mod_perl etc. but it wasn't universal).

Applying a "hack" used to mean hand-modifying the phpBB code, etc. "Mods" were basically prepared patches. HTML and PHP were jumbled together. All that bad jazz.

They had a lot of code samples that said "input validation is left as an exercise for the reader." Nobody ever does, so it is a bad idea to offer samples of this kind. Do the simplest validation you can to avoid the problem of code being removed due to lack of understanding.

In version 3.1 they have forbidden BC breaks, rewrites for the sake of code quality, and new features. (Hmmm, what does that leave? 3.1 is a bug fix release even though it's not an x.y.z release?)

In version 4 they plan to start from scratch and use a framework.

"In our opinion it is not acceptable to say 'you can start off with this software, then grow to other software,' because transitions are nearly impossible"

Now looking at the use of Symfony 2. Things they like include the HTTP caching functionality, where a frontend proxy server can request small parts of the page and leave the rest of it cached. Sounds like AJAX but it's not: we're talking about new page fetches from new people, yet we only have to talk to the "expensive" back end PHP server for a small part of the page that actually varies in the timeframe in question. Very cool.

They mix Joomla session storage with Symfony sessions...?

Oh, they integrated Symfony 2 into Joomla, it runs as a part of a Joomla page and because things are so well factored now it all just works. Crazy.

The same stunt can be carried off with Drupal.

They need bundles to be installable as end-user-friendly "plugins:"

"Bundle -> Plugin Bundle"

Installation through web interface, Doctrine2 Entity transformation, user interface for configuration

"We do not want users to have to edit config.yml"

Bundle Package Management

You need a dependency list and an ability to retrieve packages that meet dependencies. This is where Nils and crew are helping out Symfony 2 most at the code level, by creating a package management system for bundles.

(I appreciate the irony of a software package that was once upgraded via a "system" of do-it-by-hand "hacks" becoming the parent of a new package manager for sane distribution of software)

phpBB4 is both a complete Symfony 2 project and a set of bundles that you might use in your own projects. "A few first ones in the next couple of days."

You can help by: contributing to Symfony 2, building reusable bundles, and proposing and implementing ideas at area51.phpbb.com.

"Will there be an upgrade path (from phpBB 3.x)?" "Yes absolutely."

"Do you have a release date?" "The way phpBB works is there are a group of volunteers... no one is being paid to work on phpBB... difficult to make any kind of estimate."

"Will this be integrated into Apostrophe?" That would make a very cool community-driven bundle!

2:58pm

jwage sites using supervisord: sociallynotable.com, which monitors tweets relating to Amazon products and compiles useful info on them

"Why did you use mongodb?" "Look at what you're storing. MongoDB is schemaless and our product data varies widely between suppliers. Different fields in every document. Performance is the other benefit. "Doctrine becomes your schema. It kind of brings the sanity. I can have multiple document classes that map to the same collection... speed of development. No migrations."

Note to self: jwage's dark blue on white slides are the most readable of the day. Make legibility a priority for my Paris slides (:

"I don't want to force every document to have to extend from some base class, it's just easier to copy the ten lines of code (that exclude deleted material) and be done with it."

2:32pm

Jon Wage, OpenSky: Doctrine In The Real World

(Doctrine is the preeminent object-relational mapper, or ORM, for Symfony and indeed PHP in general. Going beyond basics like ensuring everything is safely escaped and hydrating results as objects, Doctrine makes relations between tables easier to express and fetches objects in the correct relationship to one another. Doctrine 2 is a big improvement in both speed and memory usage. Greatly looking forward to this talk)

"Today we're going to talk about Doctrine, specifically some real-world examples"

PHP developer for 10+ years

Works remotely for OpenSky (Manhattan). Previously worked with Fabien. Jon knows Fabien well— I've officially been mispronouncing Fabien's name. Damn. Sorry Fab-ee-un (not Fab*yen*, and certainly not Faybeeun (: )

"We love open source, we have several developers who help build open source projects on our team"

Working on open source at OpenSky: Kris Wallsmith, Jonathan Wage, Jeremy Mikola, Bulat who spoke earlier

"We use both ORM and ODM, mostly ODM, but here's how we use them together"

ODM (Object Document Mapper) is for MongoDB and other NoDB solutions that are not SQL based. they use ORM (Object Relational Mapper) for money-related stuff that actually requires the guarantees of integrity and consistency that MySQL and similar databases provide. (Personally I think those guarantees have value elsewhere... you should go NoDB only when SQL just can't sustain performance for you or you truly don't care about the integrity stuff... but that's me)

"Defining our Order Entity"

Two separate persistent layers, two databases.

(This talk is fast and starts in the middle, trying to catch up)

He's talking about hooking the postLoad event on a Post entity (in the ODM) and fetching the associated product from the relational database using a lazy load in the getter method.

So you can work in a natural way with data that lives in the ODM and data that lives in the ORM and not worry about how that relationship works once the model layer is designed to handle it.

"They play together like best friends"

Your order object is just a regular PHP object. You are never forced to extend a certain class or implement certain patterns (surprised by the latter).

This example is available in detail on his blog at jwage.com

"We don't want to delete any data ever. I like my deletes soft, not hard." Recently Flickr deleted users' photos by accident. Why delete things?

"Instead of deleting, simply set a deletedAt field." (I like SoftDelete but find it hard to work with code that's using a Doctrine behavior to accomplish it because things get weird when the objects actually still exist and have relations...)

SoftDelete Extension for Doctrine MongoDB ODM is available.

A Symfony 2 Softdelete bundle is available

To me the most interesting thing about this talk is the set of assumptions: you already know Doctrine 2 and you are already sold on MongoDB. Which is perhaps true in this room. Fascinating

Restoring a user:

$user = $dm->getRepository('User')->findOneByUsername('jwage'); $user->restore();

(Doesn't that mean that findOneByUsername still finds jwage?

Now he's introducing MongoDB retroactively (: Talking about how MongoDB uses JavaScript queries/commands

"How do we automatically exclude deleted documents?" "I don't want to automatically exclude anything. How do you get them when you do want them? I prefer to be explicit"

So every public-facing query:

$qb = $dm->createQueryBuilder('user')->field('deetedAt')-->exists(true);

So what does the extension do for you? It keeps you (or your team) from really deleting anything, even if you try. You can NEVER delete. And the burden is on the person writing queries to not pull deleted things. Seems a little extreme except that we've reimplemented it more than once and so has everyone else (:

Talking about events to clean up references because MongoDB has no foreign key integrity "ON DELETE CASCADE" stuff

Symfony 2 and Supervisor

http://supervisord.org/

With PHP 5.3 and better memory management you can write background daemons that stick around. Supervisor is a useful python script for keeping other daemons alive (restarting them if they die). This could be nice for things we currently do with cron (with the attendant risk of race conditions and/or needless delay).

You want to send an email when new users register, but you don't want to risk failure because email doesn't work *right now*. So what do you do? You use a tailable mongodb cursor. You tail a NewUser document collection, you insert NewUser documents from your actions, the daemon that is "tailing" the cursor will instantly process the NewUser after it is inserted and dispatch the e-mail. "Tailable cursors" are like tail -f. (What reliability guarantees are there for this? Email is pretty critical)

A "capped" collection has a limit on the # of items in it, on the 10,001st it pushes out the oldest one. A requirement for tailable collections. There is a doctrine console command to make one.

(This is a neat way to do queueing. You could do the same in other ways of course. Symfony 1.4 has the option of queueing emails for later delivery by a task. A SQL table holds the work to be done; actions write to it and the task deletes from it)

2:06pm

The cycle is:

* git fetch (gets what has changed) * git log origin/master ^master (shows you what has changed) * git merge|rebase origin/master (accepts the incoming changes) * (test your stuff, make sure it didn't break with the changes) * git push origin master (push your work up)

git can also have a mailing list contributor workflow in which emails are automatically generated which constitute complete patches and the history of the mailing list is the history of the project. Or something like that.

"There is one clean branch per series, extracting to patch files and mailing them to a mailing list."

Huh? Here's how it works:

* Work on a branch * git rebase origin/master (makes sure a patch will apply cleanly to the current head) * git checkout my-topic-branch * git format-patch -M origin/master (find the common point between this branch and my branch, make a patch of my changes to the master, output is an inbox-formatted email message)

You can include a note in the email.

Mail your series of commits to the mailing list, which you can do via imap with git send-email.

git send-email *.patch

On the receiving end:

git am 0001-limit-log-function.patch Applying: add limit to log function

The author, date, commit message, etc. are part of what this applies, thus more convenient than just applying a patch with 'patch'

Pulling in stuff from personal repositories

You can pull down the objects that are unique to someone else's repository:

git remote add nick git://github.com/nickh/project.git git remote add jess git://github.com/jess/project.git git fetch nick git fetch jess

Then do:

git merge nick/master jess/master

(He's going really fast doing my best here (: )

A pull request:

git request-pull upstream/master origin

This generates an invitation for the upstream person to incorporate your work. It autogenerates an email for you with the information they need to pull the work from you (not a diff containing the actual work).

To respond you have several options:

1. Add them as a remote

git remote add scott git://github.com/schacon/symfony.git git fetch scott

This is useful if you expect to do it again.

2. git pull git@github.com:schacon/symfony.git branchname

This is a one-step way to pull Scott's work without adding Scott for the long term.

His final topic: GitHub. (Symfony 2 is on GitHub)

Alex: "too bad there won't be question time so Tom can ask about alternatives to svn:externals."

github makes it easy to generate pull requests (doesn't make it much easier to accept them though).

I asked about replacing svn:externals when moving to git. He suggested "braid" as an svn external substitute. Braid seems to be a solution for using svn externals *in* git. I was asking about a replacement but didn't want to monopolize too much. I think he probably would have suggested git submodules

But this part of his response would apply regardless:

"PHP does not have a good dependency management system... python has eggs, ruby has gems... PHP has pear but it is not widely used for this... so we use subversion as dependency management but it isn't really a good fit"

My thoughts: that is a very interesting perspective. It would make dependencies Not Version Control's Problem Anymore. But what if the dependencies are themselves continuously integrated, like our 1.5 stable plugin branches? svn:externals deliver that really effectively

Another related question. He mentioned subtree merging, git submodules.

"Are there overhead issues with very large files?" "Yeah, if you have really large files, when you do a clone you get every version of that (large) file (that ever existed). You can store large assets in s3 and save only checksums (using a separate tool Scott made)." You can also do git clone --depth=1 to get a clone that doesn't have the entire history.

Resources: bit.ly/gitcontrib, git-scm.com, gitref.org, progit.org

1:45pm

Git is different from svn in many ways. When you add files it stores checksums of those files. Rather than saving deltas, it saves the entire file again... but if a particular file has not changed, its checksum has not changed, so that file doesn't have to be physically duplicated in that commit.

Not using deltas means that we don't have to be a delta *with relation to* anything in particular. There is one global database of files (many entries per file, one for each modification) and many "pointers" to these so they can be efficiently reused without the need to force a linear view of everything.

When you fetch from someone else all you have to do is copy the relevant items from the database if you don't already have them.

Are commits "changesets" or "snapshots?" They are both, because the checksums are enough information to reproduce the whole thing (assuming you can access the database of checksummed content).

(He kinda jumped from talking about the basics of why you want version control to talking about checksums... could maybe use a middle piece about typical usage)

Ah, here we go: "Scott Chacon's Guide to Making Proper Commits"

You can 'commit' as you go along and 'push' only when convenient. Then you have the ability to roll back easily to several things you did that day and others can see how you got to your endpoint. (Pushing more often is good though so you don't forget to push entirely...)

git diff --check will show you the nature of your modifications, pointing out things like "all you did was change whitespace." Helpful to avoid wasted commits that don't tell anyone anything

Every commit should be a "logically separate changeset." Everything in it should make sense together. (I am a frequent offender here because I will put out a bunch of fires and then say "oh yeah, time to commit." Now I have to cite four different tickets and it's harder to see what I did. I should learn to commit incrementally. Git's speed of local commits would encourage that)

git add --patch

Asks you about each change and whether you want to include it now! Wow, that is awesome.

git-gui is a git tool for seeing your changes and staging those you want to include in a particular commit.

Git Tower is a Mac tool for the same thing.

A commit message should be...

* A short summary of changes (50 chars) * More detailed explanatory text, wrapped to about 72 characters (yes). Remember that the first line is sometimes an email subject line in some monitoring tools

"One branch or Series per Topic." Wait, what? By topic he means a general goal (fix cropping). Not sure how a series differs from a branch

You can use git the way teams (like ours) use subversion, with one repo to which everyone on the team has write access. However git is first come, first serve, the first person to push wins and the second person gets rejected and has to merge what's been done first before pushing again.

If Nick and Scott clone at the same time and then make independent changes and push, the Nick client says "I want to push new stuff," master says "I have master at this checksum," client says "cool I see that in my history so I understand," Nick's client sends the update. When Scott tries to do it he finds out the master has an unfamiliar checksum. "It would be mean of me to crush Nick's changes with git push -f..." so you can 'git fetch' to update from what's on the server (or git pull?) and do 'git merge origin master' to merge it with what you have locally (you are bringing your local into sync with the remote). Now you can 'git push' successfully.

(Wow, git push -f seriously clobbers stuff forever?)

You can also make a branch and commit to that rather than 'master' (which is just a pointer like every other branch). This is another way of getting out of a back and forth commit war with someone (:

"Merge vs. Rebase"

Advantages of Merging

You can undo a merge. You have more data for analysis later. You can do easy continuous re-integration of changes. If you merge every 24 hours, you only have 24 hours of merge conflicts and can solve them in piecemeal as time goes on.

1:30pm

Contributing with Git, Scott Chacon

http://bit.ly/gitcontrib

My own talk went well. Understandably, developers were more interested in the code than the demo. User interface design is relevant because all the clean code in the world won't help you if user experience does not drive your design. In Paris though I will spend even more time on the code and interleave it with relevant pieces of the demo so the relationship is immediately clear.

OK, on to Scott Chacon of Github!

"How many of you have worked at companies without version control?"

Sad, angry hands go up

"Have you used FTP to manage a site with other people?"

*Groans*

"Imagine a world with no version control tools. Can you run a huge, major project with no version control? It is possible. The Linux kernel was managed without version control for many many years." Prior to 2002, before BitKeeper became involved. (This surprises me, I thought they were on CVS or some such thing)

Talks about classic no-version-control ad hoc solutions. Copying tarballs around, generating diffs with the classic 'diff' tool and sending them back to be applied with 'patch'. This is how changes propagated in the open source community before distributed version control systems.

The basic insight of git is that this wasn't terrible in theory, just poorly managed in practice.

So we want something that manages snapshots and makes merging easier. git is that tool.

svn solves a similar problem but only for those who have write access to a central repository. In git you can have your own independent, equally cool clone of a project and send "pull requests" to have your code merged upstream without having write access to anything (of course they have to be approved)

svn has a linear history, version 1, version 2 etc.

11:30am

Time for me to present my Apostrophe talk! Obviously I can't liveblog it (would love it if someone did so in comments). So here are my slides. There will be a substantial live demo where you see the "demo.apostrophenow.com" link. (I'm testing a local copy of course.)
View more presentations from tompunk.

10:50am

Wow, Bulet is a whirlwind of testing goodness

"Refactoring: the code evolution."

"Why use test driven development?" Best test coverage possible. Design is born out of necessity, not forecast. What to avoid: shared fixtures (same objects in multiple tests, persistent objects). External dependencies— slow and uncontrollable. Test code duplication&mash; code needs to be maintained, keep it DRY. Extract creation methods, create test cases per fixture setup. Over-mocking— leads to fragile code, usually is a sign of violation of the Law Of Demeter. (What is over-mocking?)

Emphasing DRY (Don't Repeat Yourself) and You Ain't Gonna Need It (YAGNI). "There is no need to implement ACL if you are asked to write a forum board. Don't optimize the code before it is written, forecasts (of what will be the choke point) are usually wrong anyway."

Avoid magic. Easier to code when the API is not lying. Points the finger at Magento. Dependency Injection helps to get rid of magic. Avoid boolean parameters, they kill readability:

->processOrder(int $orderNumber, boolean $email = true)

Should be

->processOrder(int $orderNumber) ->emailConfirmation(int $orderNumber)

This produces alternative execution flows that all have to be tested and it's easy to miss some.

Inheritance and polymorphism are better than conditionals (replace "switch" with subclasses that override a method differently). The "switch" statement is inherently not object oriented. Random picture of a guinea pig family tree... OK?

One if statement is not terrible, but a recurring if statement for the same thing in many methods means you should have used inheritance. (?)

Many flows of execution equal many test cases per function.

class LoggableMongoCollection is responsible for logging collection interaction, the regular MongoCollection need not know about logging. The methods of LoggableMongoCollection make the logging call before calling the methods of MongoCollection. The 'if' statement decided which subclass to instantiate once at creation time.

"Switches are just grouped ifs." When you have the same "ifs" and switches in all/many of your methods in a class, what you really need is a subclass (or several subclasses) for each of these cases. (This won't help you if the "if" is looking for state that varies over the lifetime of the object. It's only helpful in situations where the condition can be evaluated before the object is made)

Composition over inheritance... (because there is no multiple inheritance in PHP)

Don't decompose objects if there is no need for re-use, YAGNI

A list of resources, Google Tech Talks has a lot of good stuff

"Is it better to use interfaces rather than classes for mocking?" "Yes"

"How do you start unit testing in a large code base that doesn't have it?" "As long as today you added a test... someday you will have full test coverage"

(If you're wondering how to mock a class effectively in PHP, you probably haven't switched to injecting all dependencies into a class yet. Don't call 'new explicitClassName' if you can help it.)

10:30am

Bulet Shakirzyanov of OpenSky talking about unit testing.

Agenda: unit testing, dependency injection, mock objects, test-driven development

Types of testing: "end to end, black boxc testing -" tests are hard to write and run and find bugs easiest to fix (bad markup, wrong CSS rule).

"Functional, wiring testing" - easier to write tests, find easy to medium bugs (cannot connect to database because server is misconfigured)

"Unit tests" - easiest to write, fix logic bugs and hard-to-spot design problems (violation of single responsibility principle, hard coded function calls)

"What is unit testing? A method by which individidual units of software ar tested. The smallest testable part of the application"

Reasons to unit test:

Debugging is time consuming Easy way to know if new code broke old code A unit tests can show a class in action by itself

"Reasons" not to:

"I never make mistakes" The functionality is "trivial" Tests slow me down Management "won't let me"

You are responsible for making sure your code works, the way to do that is testing, make it part of your time at work.

The real reason: programmers don't test because they don't know how.

Test isolation: no need to build a car to test tires. Testing a payment gateway shouldn't screw up something else.

Tests need to be run by every developer, on a variety of platforms and stacks. Tests must not rely on the environment in which they are run.

Speed! Time is money, the more time we spend waiting for tests to run, the more money we waste. Slow tests are bad.

Testable code is clear and easy to follow. You can look at a test to find out what a function is supposed to do. Can replace documentation (!)

"Single responsibility principle" bank account class is only responsible for storing the account balance. Dependency Injection works with this principle, removing hard-coded dependencies so we can inject mock objects and/or switch implementations. Dependencies are in the dependency injection configuration, not hidden in the source coce

Initialization logic can be extracted (dependency injection container - DIC).

We want to avoid long method call chains (all caps!) Law of Demeter: "Only talk to your immediate friends." Bad:

$sku = $product->getStockItem()->getSku();

Good:

$sku = $product->getItemSku();

Product class should have a getItemSku() method.

Why? Because it makes APIs more stable. We only have to change the Product class if we rename the getSku method of the StockItem class.

$logger->getWriter()->getFile()->writeLine('Some Log'); versus $logger->log('Some Log');

"Programmers should avoid writing code that looks like dog.getBody().getTail().wag(); and write dog.expressHappiness(); and let the implementation of the dog decide what this means"

Properties 2 and 3: speed and repeatability

Mock objects. Simulated objects that mimic the behavior of real objects in controlled ways.

Test case -> Tests objects A -> Calls method on object S. But instead we use a mock object S, with certain expectations for inputs and outputs. This allows us to test A independent of S.

By mocking out external dependencies, we can achieve faster and more granular tests (no waiting for "real" results, no inability to test A when S is temporarily busted).

$file = $this->getMock('File'); $file->expects($this->once())...

Functions must become methods— no calling fopen/fwrite because you can't mock them up. Create a File class.

"The best use for mock objects is iterative interface discovery." You have to figure out what the mock object is supposed to support in order to provide the mock object.

Test driven development:

1. Red: write a failing test case, use mock objects to represent classes that are not implemented yet.

2. Green: write minimum code for test to pass.

3. Refactor: now that your tests are green, see what needs to be extracted (create dependencies, remove duplication).

Showing the use of phpunit to implement this process. Tough to read the screen

"Mock only what you own... and what you don't own, own." Try not to mock external classes like Doctrine directly. Instead wrap it in something of your own so you can have a stable API to mock.

10:00am

"Symfony 1.x to Symfony 2.x." He's going to show how to get going with the Symfony 1.x (?) sandbox on Amazon EC2 which he's a big fan of. Shows the YAML configuration files etc.

Showing familiar Symfony 1.x configuration concepts: app.yml, settings.yml, routing.yml. Reviewing how to set up a route that maps to a Symfony action, allowing you to change the way the URL behaves without recoding the action that handles it.

He's basically pointing out that Symfony 1.4 best practices were already close to what Symfony 2 makes universal.

Showing the Symfony 2 requirements check page— heh. We borrowed it and adapted it for the Apostrophe requirements check page. He mentioned the intl extension, which is a big sticking point, MAMP PHP and Apple Snow Leopard PHP both don't have it. But reinventing it is an ugly task.

sandbox/app/config/config.yml: showing how to do similar configuration tasks in Symfony 2. Pointing out you still can have separate environments, a routing.yml file, etc. Routes are just a little different: hello: pattern: /hello/{name} defaults: { _controller: HelloBundle:Hello:index } Controllers are a lot like actions classes, with the nice touch that you can have named parameters that match URL parameters (but you don't have to, it's possible to get them from the request to avoid coupling the way the route is written to the controller code).

Showing the TWIG template language. TWIG is fantastic. Our front end guys will be so much happier.

The web debug toolbar has picked up a lot of steam. "It gets archived into an sqlite database." Whee! You can go back and get a look at what happened on previous requests.

Pointing out that configuration files can also be yml, xml or PHP.

Resource filenames like hello.html.twig are great because you can specify both the templating language and the content type.

"A community of Symfony specialists." punkave.com slide! knplabs.com out of France. A whirlwind of additional peeks at sites. Mentions symfonyexperts.com, where you can pay to get Symfony questions answered "if the mailing list isn't fast enough for you." (People ask on Stack Overflow too)

"Having a large community of specialists means more contributors and better services. The community moves the project faster."

"Join the community!" Head for github.com/symfony.

"Everyone is hiring!" This is absolutely true. P'unk Avenue has grown by a factor of two in the last year and we are not atypical.

"Enjoy the rest of Symfony Live, and I'll see you guys in Paris!"

9:51am

On to Symfony 2.x!

Learning from experience. Not backwards compatible, probably never will be. Developed from scratch for performance and flexibility. PHP 5.3 is the new minimum version, they use namespaces and closures (lambdas— Dustin must be a Lisp guy).

(1.x was) "too coupled together." 2.x is decoupled, a collection of independent components. At the same time there is a framework that provides a full stack and a clear way to get going building a typical web app.

Symfony 2.x is managed with git, not svn.

Symfony 2.x evolved the philosophy: event dispatcher, dependency injection container, design by interface, less magic ("explicit over implicit"). (I need to ask how that plays with "convention over configuration," and whether you can have both)

"Don't reinvent the wheel." They are using Doctrine 2, Zend Framework 2 (logging component), and PHPUnit 3.5.x (for unit testing).

"More flexibility." Configuration in YAML, XML, or PHP. Templating in Twig or PHP. Doctrine or Propel ORM (they are nudging people toward Doctrine I think). Caching based on the HTTP cache spec so it can leverage proxies, ACL security built in, asset management will be built in (the Assetic toolkit).

NoSQL via Doctrine 2 (MongoDB, CouchDB), better testing with PHPUnit (includes support for Selenium, nice; also code coverage reporting).

GitHub rather than SVN, better management of the core team, hackdays sprints and meetups oh my!

"To get involved, fork the project, make a minor change, and make a pull request." Github is so disruptively easy to contribute with

Symfony 2 documentation updated during this weekend, which was a hackday weekend here in SF.

Plugged Symfony 2 Bundles, a website that gives an overview of Symfony 2 bundles that have been published via github. Symfony 2 bundles replace "plugins" in 1.x. (In Symfony 2 you organize code into bundles all the time, not just for redistribution to other people)

shopopensky.com, a social shopping site, is the first major Symfony 2 production site in the US. He also plugged control.servergrove.com, servergrove's great new hosting control panel built in Symfony 2.

9:45am

The Symfony 1.2 era

"The original Symfony 2!" Yes, definitely, a lot of backwards compatibility breaks.

Introduction of the event dispatcher, the forms, caching and tasks sub frameworks, Swiftmailer as a Sensio-sponsored project.

Community began to mature— dedicated release and community managers. Plugins, patches, documentation and translations are responsibly managed. Before that there was "no clear domain ownership in the community."

Independent community plugins development took off at this point (this is when Apostrophe got seriously underway in its first form as pkContextCMS). Independent community evangelism got underway.

Dustin met Fabien in 2007 at the first Symfony Camp. Before that many of the core team had never met.

Jobeet was the second full blown sample application (with a month-long advent calendar of tutorial chapters documenting its development)

The path to Symfony 1.4... a stable and tested version of Symfony, with deprecated code removed, and long term support guarantees (1.0 also had three years of support). This is still the stable version used for most production projects today (although some people are already using 2.0 in production, they know they will have to recode a lot of things until the API is stable).

Documentation translated into six languages. Symfony Live and Symfony Day conferences. Symfony Live 2009 was the first major Symfony conference. (We attended for the first time in 2010)

Talking about Symfony meetups. We should revive the Philly meetup

Slide of Apostrophe! Yay!

9:36am

Dustin Whittle getting underway with "The Path to Symfony in the United States."

He's been in the Symfony game since 2005. He began contributing plugins and the like back to the community and came to the attention of Yahoo which hired him in 2006. He became an internal Symfony evangelist. Built Yahoo Answers and other Symfony sites at Yahoo.

He will be the head of Sensio's new San Francisco office (Sensio is the Symfony parent company, Fabien Potencier's place of business; Sensio is based in Paris).

Symfony was originally open-sourced in 2005, it was their internal framework before they chose to open it up.

Symfony "0.x" developed the core philosophy... don't reinvent the wheel, KISS, don't repeat yourself, do automated testing, write quality documentation, favor convention over configuration.

Slide about things inspired by other platforms in Symfony 0.x/1.x: django (forms and widgets were an inspiration), prado (i10n and i18n), doctrine, propel, prototype, TinyMCE, perl, Rails, even Cocoa's event system.

Symfony 0.x defined the problem: MVC, Yaml configuration files, ORM/CRUD/admin generator, routing, forms security, i18n, caching. Making it developer friendly— logging, exceptions, debug toolbar.

Start of an open source community. It took 5-6 years to build a large enough US community for a US conference (this room is pretty full, quick math gives me about 130 people).

The book "The Definitive Guide to Symfony" made a big difference. So did the Askeet sample application and the "advent calendars" in which tutorial chapters were released daily.

answers.yahoo.com was the first major Symfony project at Yahoo, it was launching just as they hired Dustin.

On to Symfony 1.2

9:15am

I'm liveblogging from the Symfony Live conference, except of course for my own talk today at 11:30 PST on Apostrophe. I'll be updating this post frequently during the day. Symfony, for those who have been coding under a rock, is the preeminent PHP web app development framework.

Conference is hosted at Microsoft's offices. Bruno Terkaly, our host here, is giving a five-minute spiel about the web platform installer. (Honestly it's very good, they should have talked about their PHP accelerator at some point during the conference.) Symfony will be available through the web platform installer some time this week.

He's having a little trouble with Fabien's name.

The wifi is great once you're on it, the login is realllly slow.

Now Fabien is welcoming us to the conference.

First up: Dustin Whittle on Symfony in the US and the history of Symfony in general.
Check out another article
January 31st, 2011
U-Turns $1