Search

Friends

Atomspheric CO2 (PPM)

Archives

Blather

Uptime verified by Wormly.com

21 October 2014

Codeship CI + Symfony 2 + PHPUnit

We've just moved from CircleCI (which was excellent) to Codeship, because we couldn't afford the Github fees any more. It definitely wasn't as easy as CircleCI, and the UI isn't as tidy. But Codeship works pretty well so far. The Setup Commands for a Symfony app go something like this...

phpenv local 5.4
export SYMFONY_ENV=test
mysql  -u $MYSQL_USER -p$MYSQL_PASSWORD -e "CREATE DATABASE test"
unzip -p app/Resources/Tests/test.sql.zip | mysql -u $MYSQL_USER -p$MYSQL_PASSWORD test
echo "xdebug.max_nesting_level = 250" >> $HOME/.phpenv/versions/5.4/etc/php.ini
echo "memory_limit = 512M" >> $HOME/.phpenv/versions/5.4/etc/php.ini
cp app/config/parameters.yml.dist app/config/parameters.yml
sed -i "s/database_user:.*/database_user: $MYSQL_USER/;s/database_password:.*/database_password: $MYSQL_PASSWORD/" app/config/parameters_test.yml

composer install --prefer-source --no-interaction
app/console doctrine:migrations:migrate --env=test --no-interaction

7 June 2013

Setting up CircleCI with Symfony 2.3

I've just set up my latest Symfony project on CircleCI and it's working really well. CircleCI is very good, and the staff are amazingly helpful (even though I haven't given them any money yet). I spent a morning trying to set up Travis, without any luck. Seems to be some issue with private repos owned by organisations.

If you're trying to do your parameters the new Symfony 2.3 way, it takes a little extra work. And if your tests need the database, it takes a tiny bit more.

circle.yml

machine:
  php:
    version: 5.4.21
dependencies:
  override:
    - composer install --prefer-source --no-interaction
    - cp app/config/parameters_test.circle.yml app/config/parameters_test.yml
    - app/console doctrine:database:drop --env=test --no-interaction --force
    - app/console doctrine:database:create --env=test --no-interaction
    - app/console doctrine:migrations:migrate --env=test --no-interaction

app/config/parameters_test.circle.yml

parameters:
  database_name: circle_test
  database_user: ubuntu
  database_password: null

Using environment variables didn't work that well for me, because I've got a local testing environment as well. So keeping two versions (local and CircleCI) of the testing parameters file seemed to work better. I'll still probably use environment variables for production, although that would be something else that didn't get tested.

12 March 2013

Integrating Hero Framework accounts with Vanilla Forum using jsConnect

This is something of a hack, but it is pretty quick and easy. I've never used Smarty before, but it's pretty flexible (and slightly evil).

I put this in a Hero template called authenticate.thtml and mapped it to /authenticate. I installed the jsConnect module for Vanilla, set the secret and it was basically done. Seems to work for regular and embedded forums.

{strip}
{assign var=secret value='985d2f9eb57a8b55db3c04c20272bce9308764b0'}
{assign var=client_id value=$smarty.get.client_id}
{assign var=callback value=$smarty.get.callback}

{if $logged_in}
  {assign var=member value=['uniqueid'=>$member.id,'name'=>$member.first_name|cat:' '|cat:$member.last_name, 'email'=>$member.email, 'roles'=>'member']}
  {assign var=empty value=ksort($member)}

  {$member['signature'] = $member|@http_build_query|cat:$secret|@sha1}

  {$member['client_id'] = $client_id}
{else}
  {assign var=member value=['name'=>'', 'photourl'=>'']}
{/if}

{$callback}({$member|@json_encode})
{/strip}

25 June 2012

Slow performance hosting Symfony2 on cPanel/WHM

We've recently set up cPanel on VPS to host our big Symfony2 project. We were developing locally on a MAMP setup which was really fast. For production, we moved initially to a FastCGI shared host which was fairly fast as well. But to get PHP 5.3 we needed to make a few messy fiddles we weren't that happy and they didn't have APC. So we finally moved to a VPS so we could have PHP 5.3 with mod_php and whatever other guff we needed.

However, the system was incredibly slow. Between 50% and 30% of the speed of our local development server (which wasn't anything flash). We tried a bunch of combinations of memcache and APC configurations and then tried a few different Symfony2 options but nothing made much difference. In the end it turned out to be the open_basedir option that is normally on with cPanel accounts. This disables fstat caching, and Symfony2 does a LOT of fstat calls. Turning off open_basedir solved the issue and reduced page generation times from about 1500ms to 600ms. Which may not be an option for everyone.

See https://bugs.php.net/bug.php?id=52312.

21 April 2012

Comic Sans

This is Comic Sans MS. Based on comic book writing the font remains a huge fan favorite. It is used on a surprisingly large number of Web sites of all types. Comic Sans MS was also designed and hinted by Vincent Connare.

Microsoft ClearType Tuner (their emphasis)

17 April 2012

Customising repeated field label names in Symfony2

I spent a while trying to customise the FOSUserBundle reset form. It uses a repeated field for the password, which can't really be tweaked from Twig. I finally figured it out using a Twig base form template. It's a little bit crap.

{% block fos_user_resetting_widget %}
  {{ form_row(form.new.first, {'label':'Password'}) }}
  {{ form_row(form.new.second, {'label':'Confirm Password'}) }}
  {{ form_rest(form) }}
{% endblock %}

More info: Symfony2 Twig Form Theming

22 March 2012

ExpressionEngine Logout Bug

ExpressionEngine has a pretty brittle session mechanism. It has all sorts of devices for making it more secure and harder to spoof. But it causes issues with quite a few of our clients. Today we found a weird problem with what turned out to be a proxy-related issue. Some requests would be be sent through the proxy and some weren't, so the IP address would change. Strangely, no one was ever logged out but some requests would fail (and be redirected back to the login screen with no message).

In system/expressionengine/libraries/Session.php ExpressionEngine includes a check for the IP address when it gets the session data. Commenting out the line for the IP address and turning off IP address checks in the configuration seems to have fixed it for me.

public function fetch_session_data()
{
    // Look for session.  Match the user's IP address and browser for added security.
    $this->EE->db->select('member_id, admin_sess, last_activity')
        ->where('session_id', (string) $this->sdata['session_id'])
        //->where('ip_address', $this->sdata['ip_address'])
        ->where('user_agent', $this->sdata['user_agent']);
...

21 December 2011

Symfony2 invalid type messages

Symfony2 will automatically add validation to entity fields ensuring that values match the database type. For instance, If you try to set an alpha character on a DECIMAL column you will get "This value is not valid" along with other constraint violations. If you manually add a Type Constraint you'll end up with a largely duplicate assertion. However, if you want to set the message on the default constraint, you can't do it in the entity itself. You have to do it in the form class.

$builder->add('amount', 'money', array(
  'label' => 'Default Price', 
  'invalid_message' => 'Price must be a number'
));

I couldn't find that documented anywhere, but it works.

14 December 2011

Preventing ordering out of stock items in Ubercart 3

In template.php or some such...

function site_uc_product_add_to_cart( $variables ) {
  $sku = $variables['form']['node']['#value']->model;
  $stocklevel = uc_stock_level($sku);
  if (is_numeric($stocklevel)) {
    // Stock tracking is active
    if ($stocklevel <= 0) {
      return '<div class="add-to-cart out-of-stock"><button disabled>' . t('Sold') . '</button></div>';
    }
    else {
      return theme_uc_product_add_to_cart($variables);
    }
  }
  else {
    // Stock tracking is not being used for this product, just show the add to cart button as normal
    return theme_uc_product_add_to_cart($variables);
  }
}

17 November 2011

Querying certain subclasses with Symfony2 & Doctrine2

We're using Symfony2's class table inheritance and I was trying to work out how to select only some subclasses using Doctrine2. It turned out to be pretty easy.

SELECT e FROM MyCustomBundle:ParentEntity e WHERE e NOT INSTANCE OF MyCustomBundle:ExcludedChildEntity

In the discriminator map in the super class you refer to the full qualified namespace and class name (\My\CustomBundle\Entity\ExcludedChildEntity) but in the DQL it works with Symfony2's shorthand.

$builder = $repository->createQueryBuilder('entity')
                      ->where('entity NOT INSTANCE OF MyCustomBundle:ParentEntity');

25 September 2011

jQuery DOM Loaded Shortcut

Normally web folks suggest loading Javascript at the bottom of the page, but with something like jQuery it's quite handy to be able to use stuff like $(function() { $('#node').show(); } inline in your pages. It also seems like jQuery ends up being the biggest/slowest critical component on many pages, so waiting for the page to load to doing something often really means waiting for jQuery to load to do something.

So I wrote a small script that performs a tiny, tiny piece of jQuery functionality but allows you to happily load jQuery at the bottom of the page. In my tests it doesn't seem to have any impact on the time deferred stuff takes to fire, but it reduces time taken to display content by at least 200ms. It's not totally beautiful but it's alright. And it wouldn't be that hard I think to emulate all the traditional jQuery ways of deferring execution, so you wouldn't have to change any existing code.

<script>
var $_={
  _callbacks:new Array,
  ready:function(a){this._callbacks.push(a)},
  runDeferred:function(){jQuery.each(this._callbacks,function(a,b){b.call(document,jQuery)})}
}
var $=function(a){$_.ready(a)};

$(function($) {
    $('#elem').hide();
});
</script>

<body>
Page content
...

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script> jQuery(function() { $_.runDeferred() }); // At this point our temporary $() is gone anyway </script>
</body>

This is a simple alternative. You get a simple mechanism for including deferred behaviour inline. It will execute just as fast as it would with regular jQuery call. And you'll save around 200ms on each page load and frequently more. Or at least your content will show up faster. Even when jQuery is totally cached and there's no network operations at all, on my old system it looks like just running the 233kb of uncompressed Javascript that is jQuery 1.6.4 takes about 180ms. That's a delay for content displaying content that really doesn't seem necessary, since while the content is loading you're rarely using jQuery for anything more than deferred behaviour handling.

5 August 2011

Vertical centering of cropped image with jQuery

You can use HTML, CSS and jQuery to vertically center cropped images that you don't know the height of. It's a shame to use jQuery at all, but I think it's necessary. I looked at the CSS clip property and at negative proportional margins (margin-top: -50%; etc.), but unless you know the height of the image I don't think it will work.

<div style="height:100px;overflow:hidden;">
    <img id="crop" src="blah.jpg" style="height:200px;" />
</div>

<script type="text/javascript">
jQuery(document).ready(function($) {
    var image = $("#crop");
    image.css('marginTop', (-(image.height() - image.parent().height()) / 2) + "px");
});
</script>

10 September 2010

Blogger

I've been using Blogger for the last few weeks for our travel blog. Bouncing around inside it I am beginning to suspect that all development on Blogger stopped about 5 years ago. It really is a pretty miserable web app these days. Having spent a little too long pottering about with Posterous, the contrast is a bit embarrassing. If Blogger were released today, as it is today, I think it would have approximately zero users. It's still trading on the name it made back when all web apps sucked and it wasn't the only one.

15 July 2010

Glowworm Bicycles Site

After many, many months of experimentation and development the Glowworm Bicycles site has finally come together. It's been operational for quite a while, but with a bunch of bugs and quirks. It's all done in Drupal and we're using Ubercart for the store. The basically functionality was actually very fast. My impression of Ubercart is very good so far.

It wasn't a free website, but it's a pretty big one. And now I think I am fairly happy with it.

6 May 2010

ArgumentError: comparison of Fixnum with nil failed

Using acts_as_nested_set in ruby and something like taxon.move_to_child_of(parent) you can get this error if your table data is incomplete. You may have functioning nesting through the chain of parent IDs but you need the left and right fields as well. If either the left or right field is null you will get an error like this one for certain move operations. This can easily happen if you've created some dodgy fixtures for instance.

Trying using Taxon.rebuild! to repair the table.

23 December 2009

Slow Rails Tests & fixtures :all

With my latest Rails project I'm using a new version of Rails than I'd been used (my last job was over a year ago). It seemed to me that the tests had got substantially slower. Problem was fixtures :all in the test_helper.rb. I changed that and things got much quicker.

18 December 2009

DEPRECATION WARNING: ActiveRecord::Errors.default_error_messages & attachment_fu

The latest version of attachment_fu will give you this error. You need to replace a line in this file:

vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb:386

+ errors.add attr_name, I18n.translate('activerecord.errors.messages.inclusion') unless enum.nil? || enum.include?(send(attr_name))    
- errors.add attr_name, ActiveRecord::Errors.default_error_messages[:inclusion] unless enum.nil? || enum.include?(send(attr_name))

20 September 2009

Wrap Long URLs with jQuery

This doesn't seem to exist on the internet, so I wrote it. It's not super great, but does the job.

$(document).ready(function () {
  $("a").each(function () {
    var t = $(this).text();
    if(t.length > 30) {
      $(this).text(t.replace(/\./g,". ").replace(/\//g, "/ "));
    }
  });
});

7 September 2009

jQuery InnerFade

I was looking about for a jQuery slideshow sort of plugin for the Justice Tracks Collective site. There seems to be the very popular Cycle Plugin. It's quite posh but a bit basic and only deals with <img> tags. There seem to be a lot of home-made hacks around the place, none of which looked that tremendous to me. I was going to try and write my own hack, but I wasn't really looking forward to it.

Luckily, I found InnerFade, which is posh and simple and works with any sort of nodes. It works great in Firefox. Time will tell if the reason everyone else isn't using it is because it doesn't work in other browsers. Based on my extensive testing with one browser, it's cross-browser support is probably better than the rest of the site.

On another note, I'm enjoying using WordPress for the first time. Still not heaps, but building my own template from very scratch was the best thing I could have done.

6 September 2009

Tougher WordPress

I had to upgrade lousy WordPress today because it got a bit hacked. I gave up upgrading because it felt like I was spending every second day in the shell wgetting, untarring and relinking stuff. I also didn't like the new interface, which annoyed me even more than the one I was using. However, that obviously wasn't a great strategy, so now I've moved everything permanent outside the WordPress directory and written an upgrade script to download the install file and link the stuff into the write places. Hopefully, it will be better. I'll just have to get used to relearning a new UI every time the WordPress fellas get bored and release another one.

0.603 seconds