Skip to content

Latest commit

 

History

History
192 lines (134 loc) · 8.22 KB

CONTRIBUTING.md

File metadata and controls

192 lines (134 loc) · 8.22 KB

Contributing!

Hi! Thanks for contributing!

For the best experience for everyone, there's a few guidelines we'd like everyone to follow.

Code Style

This is a work-in-progress, so feel free to put ideas forward as to coding styles. The current guideline is based on suggestions from team members, and the recommendations from the PHP Framework Interop Group

  • Files are UTF-8 encoded without a BOM (PSR-1)
  • Only use long-syntax php tags: <?php and omit the closing tag at the end of a file. (PSR-1)
  • Don't use PHP versions which are newer than specified in INSTALLING.md - it won't run in production.
  • Files contain a single class definition, and the file is named for the class. Alternatively, they should contain a script, not both. (PSR-1) All new classes should be appropriately namespaced.

Indentation and Braces

  • Four spaces per level of indentation please.

  • Opening braces go on the next line for methods and classes, but on the same line for control structures (PSR-1).

  • Spaces before and after brackets, not inside (PSR-1).

Quick example:

class Foo extends FooBase implements IFoo
{
    public function sampleFunction($a, $b = null)
    {
        if ($a === $b) {
            bar();
        } elseif ($a > $b) {
            $foo->bar($arg1);
        } else {
            BazClass::bar($arg2, $arg3);
        }
    }
    
    final public static function bar()
    {
        // method body
    }
}

Line length and wrapping

Soft limit of 80 chars, hard limit of 120 please. Lines longer than 130 chars make code review on GitHub nasty.

Naming

  • UpperCamelCase for classes. (PSR-1)
  • lowerCamelCase for methods. (PSR-1)
  • UPPER_CASE for constants. (PSR-1)
  • Properties should have names which closely match their backing fields:
<?php
class Foo 
{
    private $foo;
    
    public function getFoo() 
    {
        return $this->foo;
    }
    
    public function setFoo($foo) 
    {
        $this->foo = $foo;
    }
}
  • Interfaces should be called ISomething.

Misc

  • The ternary (?:) operator should only be used where appropriate - short expressions only please!

  • Heredoc/Nowdoc should not be used for output - use templates. Extended SQL statements are OK, but please use appropriate boundary markers, such as SQL;

How-tos

Some how-tos for some of the technologies and libraries we use.

Database

We use PDO for database access, backed by a MariaDB database.

We have some database conventions too:

  • Table names in our database are lowercase singular forms (request, user, ban) of the data they store.
  • The primary key is always a surrogate integer key called PRIMARY referencing a column called id, which is generated by the auto_increment attribute.
  • Foreign keys should be named <referencing table>_fk_<referencing column>_<referenced table>. For example, the request.reserved column is a foreign key to user.id, and the constraint and associated index are named request_fk_reserved_user.
  • The index and constraint for foreign keys should be named identically.
  • Indexes should be named <table>_idx_<columns...>
  • Unique indexes should be named <table>_uidx_<columns...>
  • Check constraints should be named <table>_check_<columns...>
  • Fulltext indexes should be named <table>_ft_<column>

Accessing the database is done either at an entity level through subclasses of DataObject (preferred), or directly through PDO. The DataObject class and it's subclasses implement the active record pattern.

Firstly, you'll need to grab a copy of the relevant database object from somewhere close by. If you're on a page, this is likely $this->getDatabase()

This is a PDO(ish) object which you can do what you need to with.

You can grab entities using the static methods defined on them:

$database = $this->getDatabase();
$request = Request::getById($id, $database);

All queries using data which is not 100% guaranteed to be safe and obviously safe (aka: hard-coded nearby) must be parameterised using prepare(). Don't sanitise the data yourself, pass it as a parameter.

$statement = $database->prepare("INSERT INTO geolocation (address, data) VALUES (:address, :data);");
$statement->bindValue(":address", $address);
$statement->bindValue(":data", $data);
$statement->execute();

Please try to use named parameters, and strongly avoid positional parameters, as named parameters can't be broken by accidental reordering.

Transactions

Transactions are handled automatically by the framework - every page load is run within it's own transaction. If you need to rollback the transaction, then you probably want to throw an exception, but it is possible to manually rollback the transaction. If you do roll it back manually, you need to take responsibility for handling that rollback correctly, and ensuring that future failed statements are also rolled back - aka start a new transaction immediately after rollback.

Database patches

Create a new file in sql/patches based on patch00-example.sql. Please follow the naming scheme already in place, and follow the instructions in the example code.

Pick the next sequential number - this will be your new schema version number. Name the file as patchXX-description.sql, where XX is your version number, and description is a very brief summary of the changes you're making.

Now, open your new file, and you'll see it's full of database code. No need to fear! There's two places you need to touch.

Update line 25, and put your version number in where the 0 is:

DECLARE patchversion INT DEFAULT 0;

Secondly, place all the database update statements you need to make around line 56, where this block is:

    -- -------------------------------------------------------------------------
    -- Developers - put your upgrade statements here!
    -- -------------------------------------------------------------------------

    -- ALTER TABLE foo DROP COLUMN bar;

    -- -------------------------------------------------------------------------

Test your patch by running the entire file against your up-to-date database. It should apply the patch without question, if it does complain it's probably because a) your database is out of date, or b) you didn't pick the next sequential number. When it's applied, verify it's correctly made the changes you need in the database, and update the tool configuration to the correct schema version.

Templating

We use Smarty as a templating engine to handle all output to the web.

Smarty is very powerful, and allows us to put display logic directly within the templates, leaving our PHP code free of display code to cleanly handle the business logic.

No display code should be in the PHP files, everything should be in the templates, and as everything is contained in the templates, escaping for display can be done exactly at the point of display, and it's obvious where data isn't escaped for display. Therefore, no escaping should be done in the PHP code.

We use v3.1.14 of Smarty currently, and it's probably a better idea to get yourself familiar with it from their documentation. Useful sections:

Creating a new page

There's not much to creating a new page, only two things need to happen:

  1. Create your page in the \Waca\Pages namespace as a class extending \Waca\PageBase (or one of it's subclasses). Implement the two required methods: main() and getSecurityConfiguration(). main() should contain your page's logic. getSecurityConfiguration() should return an instance of SecurityConfiguration, please try and use the static instances where possible.
  2. Register your page in \Waca\RequestRouter with an appropriate URL slug.

You'll probably want to use templates, and remember not to use superglobals. Most of the utilities you need should be exposed in PageBase already, and this includes the database ($this->getDatabase())