Tipsy is an MVW (Model, View, Whatever) PHP micro framework inspired by AngularJS. It provides a very lightweight, easy to use interface for websites, rest apis, and dependency injection.


If you prefer, you can download a Single Page version of the docs.


Getting Started

1. Include Tipsy

See Installation for more information.

composer require tipsyphp/tipsy
2. Create a new instance of Tipsy
use Tipsy\Tipsy;
$tipsy = new Tipsy;
3. Add a route / page

Routes can be closures, classes, or class instances. Here we create a route using a closure, and assign the internal $Scope and $View properties.

$tipsy->router()
    ->when('/', function($Scope, $View) {
        $Scope->user = 'Devin';
        $View->display('home');
    });
4. Create a View

All views are PHTML View Templates. Create a file called home.phtml

<div class="content">
    <h1>Welcome <?=$user?>!</h1>
</div>
5. Start Tipsy

Once you have defined your stuff you can start the process like below.

$tipsy->run();

For more detailed examples see Examples.


Server Config

Apache Mod Rewrite

Tipsy will either read your $_SERVER['REQUEST_URI'], or a $_REQUEST['__url'] variable. In order to tell Apache to forward all pages to your index file, create a .htaccess file. Note that the ?__url=$1 is optional.

RewriteEngine on
RewriteCond %{REQUEST_FILENAME}     !-f
RewriteCond %{REQUEST_FILENAME}     !-d
RewriteRule ^(.*)                   index.php?__url=$1 [L,QSA]

Nginx

You will need an nginx config file that looks something like this

location / {
    try_files $uri @rewriteapp;
}
location @rewriteapp {
    rewrite ^(.*)$ /index.php last;
}

PHP Short Tags

If you are using templates, I highly recommend enabling PHP short tags. It will save you lots of time when working with variables. For more information, see PHTML View Templates.

To enable short tags, add the following line to your php.ini

short_open_tag = On

Or you can add it using an Apache .htaccess

php_value short_open_tag On

Installation

See Installing Composer if you have not already installed composer.

Run the following command:

composer require tipsyphp/tipsy --prefer-dist

Or omit the --prefer-dist if you want to get the tests directory as well.

If you prefer you can add it directly to your composer.json file and then run composer install

"require": {
    "tipsyphp/tipsy": "dev-master"
}

Include Tipsy using composer's autoload

require_once __DIR__ . '/../vendor/autoload.php';
use Tipsy\Tipsy;

Read Server Config for more info on how to setup your web server.


Installing Composer

Install Composer if you haven't already. Composer is a really easy to use PHP dependency & package management tool. I prefer to install globally so you can use the command composer from anywhere in your terminal.

On OSX

cd /usr/local/bin
curl -sS https://getcomposer.org/installer | php
mv composer.phar composer

Everyone else can Download Composer here, and Read how to install.


App

The Tipsy app can be accessed either by it's static methods, object methods, or a combination of both.

Object

$t = new Tipsy\Tipsy;
$r->router()->home(function() {
    echo "it's good to be home";
});
$t->run();

Static

Tipsy\Tipsy::router()->home(function() {
    echo "it's good to be home";
});
Tipsy\Tipsy::run();

or

use Tipsy\Tipsy;
Tipsy::router()->home(function() {
    echo "it's good to be home";
});
Tipsy::run();

Combining both

You probably only need this if you are doing weird stuff.

use Tipsy\Tipsy;
$tipsy = Tipsy::app();
$tipsy->router()->home(function() {
    echo "it's good to be home";
});
Tipsy::run();

App Route Shorthand

The App object exposes and forwards the get, post and when methods to its $this->router() method for shorthand route creation. All arguments passed theses functions are passed on to the router.

$app->get('user/:name', function($Params) {
    echo $Params->name;
});

or

$app->when([
    'route' => 'user/:name',
    'method' => 'get',
    'controller' => function($Params) {
        echo $Params->name
    }
]);

See Routes for more information.


Config

Tipsy accesses much of its configuration from a single config object

Accessing config

Base object

var_dump($tipsy->config());

Database hostname

echo $tipsy->config()['db']['host'];

Setting the config

You can set the config in a config file, multiple config files, or at any point during execution.

Execution

$tipsy->config(['view' => [
    'path' => 'views',
    'layout' => 'awesome-layout'
]]);

Include Files

Tipsy supports both INI and YAML file formats. YML parsing requires a yml parsing library. You can install one using symfony/yaml from Packagist, or the yaml_parse_file function using PECL.

// Tell tipsy to read the config files
$tipsy->config('production.ini');
$tipsy->config('plugins.yml');

// use wildcard selectors
$tipsy->config('config/*.ini');
$tipsy->config('config/*.yml');
$tipsy->config('config/*');

Example configs

You can combine both INI and YML and it will Recursive Replace the config arrays.

In

db:
  user: username
  pass: password
  driver: mysql

view:
  path: views
[db]
host=localhost

[view]
layout=default

Out

{
  "db": {
    "user": "username",
    "pass": "password",
    "driver": "mysql",
    "host": "localhost"
  },
  "view": {
    "path": "views",
    "layout": "default"
  }
}

Routes

Methods

The router supports any amount of routes, methods, and can be chained.

$tipsy->router()->get('hello', function() {
    echo 'World';
});

See Route Methods for more information. For more control on routes see Advanced Routing.


Controllers

A route controller can be a closure function, class name, tipsy controller, class instance, or anonymous class.

$tipsy->router()->when('some/page', new class() extends Tipsy\Controller {
    public function init() {
        echo 'Page';
    }
});

See Controller Types for more information.


Route Params

Routes accept colon delimited params, as well as regex routes.

$tipsy->router()->when('user/:name', function($Params) {
    echo $Params->name;
});

See Route Params for more information.


Method Chaining

The router methods return the router object to allow chaining.

$tipsy->router()
    ->home(function() {
        echo 'Sup home boy';
    })
    ->when('cats', function() {
        echo 'Every kind of cat.';
    })
    ->otherwise(function() {
        echo '404';
    });

Dependency Injection

All controllers support Dependency Injection of both User Defined Services and Built in Services.

$tipsy->router()
    ->when('user/:id', function($Params, $View) {
        $View->display('home', ['user' => $Params->id]);
    });

See Dependency Injection for more information.


Route Aliases

Routes can be mapped from one route to another.

$tipsy->router()
    ->when('item/edit/:id', function($Params) {
        echo $Params->id;
    })
    ->alias('item/:id/edit', 'item/edit/:id');

See Route Aliases for more information.


Routing URLs

Tipsy will either read your $_SERVER['REQUEST_URI'], or a $_REQUEST['__url'] variable.

For more information on how to set up routing, see Server Config


Route Methods

The router object sends all method calls through the when method using either a 1 or 2 parameter call.

When

Accepts 2 parameters: route and controller. Used for defining a route that matches any method.

$tipsy->router()->when('hello',function() {
    echo 'World';
});
Home

Accepts 1 parameter: controller. Used for specifying the home page. This is equivalent to specifying an empty '' or '/' when method.

$tipsy->router()->home(function() {
    echo 'Honey, Im home!';
});
Get, Post, Put, Delete, or other methods

Accepts 2 parameters: route and controller. Used for defining a route with a specific method. You can use absolutely any method. Even made up ones.

$tipsy->router()->post(function() {
    echo 'Yay! Posting stuff!';
});
$tipsy->router()->bacon(function() {
    echo 'This is a bacon method!';
});
Otherwise

Accepts parameter: controller. If no match is found, the otherwise function will be called. Used for default, or 404 pages.

$tipsy->router()->otherwise(function() {
    echo '404';
});

Route Controller Types

Route controllers can be closure functions, class names, controllers, class instances, or anonymous classes.

Closure Function
$tipsy->router()
    ->when('/about', function() {
        echo 'This is us!';
    });
Class Name
// Define the Class
class ClassController extends Tipsy\Controller {
    public function init() {
        echo 'This is a class';
    }
}

// Set the route
$tipsy->router()
    ->when('about', [
        'controller' => 'LibraryController'
    ]);
Controller
// Define the Controller
$tipsy->controller('InternalController', function() {
    echo 'This is an internal Controller';
});

// Set the route
$tipsy->router()
    ->when('about', [
        'controller' => 'InternalController'
    ]);
Class Instance
// Define the Controller
class InstanceController extends Tipsy\Controller {
    public function init() {
        echo 'This is a instance';
    }
}

// instantiate object
$instance = new InstanceController;

// Set the route
$tipsy->router()
    ->when('about', [
        'controller' => $instance
    ]);
Anonymous Class
$tipsy->router()
    ->when('some/page', new class() extends Tipsy\Controller {
        public function init() {
            echo 'Page';
        }
    });

Route Params & Regex

Routes accept params similar to Express Routes.

$tipsy->router()->when('user/:name', function($Params) {
    echo $Params->name;
});

Route Aliases map params based on the Param Name

$tipsy->router()->alias('group/:project/user/:name', 'user/:name');

Routes can also accept a full regex. When a regex is used, params is an array of matches.

$tipsy->router()->when('/^api\/user\/(.*)\/details$/i', function($Params) {
    echo $Params[1]; // the user name of the service
});

Regex is especially useful when catching files that need to render.

$tipsy->router()->when('/\.scss$/i', function($Request) {
    echo $Request->path(); // the requested file
});

Route Aliases

Aliases allow you to map one or multiple existing routes to new routes. Params are also mapped. Each From Param is mapped to the To Param you provide.

$tipsy->router()
    ->when('item/edit/:id', function($Params) {
        echo $Params->id;
    })
    ->alias('item/:id/edit', 'item/edit/:id');
$tipsy->run();

When requesting item/1/edit it will echo 1.


Aliases also take over the methods of the parent, so you can have multiple separate methods using the same alias.

$tipsy->router()
    ->post('item/edit/:id', function($Params) {
        // save the item
    })
    ->get('item/edit/:id', function($Params) {
        // retrieve the item
    })
    ->alias('item/:id/edit', 'item/edit/:id');
$tipsy->run();

Dependency Injection

Tipsy allows easy reference of a bunch of different services and models used within itself using Dependency Injection.

Services

For a list of all available services, see Services.

$tipsy->router()
    ->when('user/:id', function($Params, $Scope, $View) {
        $Scope->user = $Params->id;
        $View->display('home');
  });

Class reference

// Define the User
$tipsy->service('User', [
    sup => function() {
        return 'Sup '.$this->user;
    }
]);

// Set the route
$tipsy->router()
    ->when('user', function($User) {
        echo $User->sup();
    });

Advanced Routing

Advanced Routes

If the first argument of when is an array you can create more customized routes, including multiple comma separated methods.

$tipsy->router()
    ->when([
        'route' => 'update',
        'method' => 'post,put',
        'controller' => function() {
            echo 'Updating content...';
        }
    ]);

Other ways to do things

Here are a few different ways to define the home page using the methods described above.

$tipsy->router()
    ->when('', function() {
        echo 'Here I am';
    });

is the same as

$tipsy->router()
    ->when('/', function() {
        echo 'Here I am';
    });

is the same as

$tipsy->router()
    ->home(function() {
        echo 'Here I am';
    });

is the same as

$tipsy->router()
    ->when([
        'route' => '',
        'method' => '*',
        'controller' => function() {
            echo 'Here I am';
        }
    ]);

Services

Tipsy exposes it's objects as services using Built in Services.

You can define your own services using User Defined Services


User Defined Services

Services in Tipsy are static objects. They are accessible in the same ways as Built in Services. Services can be arrays, closures, classes or class instances.

Array

$tipsy->service('Service', [
    stuff => function() {
        // some stuff
    }
]);

Closure Function

Using a closure allows for the use of Dependency Injection as you can see with this $View example.

$tipsy->service('Service', function($View) {
    $service = [
        stuff => function() use ($View) {
            // some stuff
        }
    ];
    return $service;
);

External Class

class Service extends Tipsy\Service {
    public function stuff() {
        // stuff
    }
}
$tipsy->service('Service');

Class Instance

class Service extends Tipsy\Service {
    public function stuff() {
        // stuff
    }
}
$tipsy->service('Service', new Service);

Anonymous Class

$tipsy->service('Service', new class() extends Tipsy\Service {
    public function stuff() {
    }
});

Built in Services

Tipsy uses Dependency Injection to allow easy access to services. Services act as static models that are the same instance throughout the Tipsy instance.

Available Services

View

Allows access to the view renderer. For more information see Views.

$tipsy->router()
    ->when('', function($View) {
        $View->display('home');
    });

Scope

The scope of the view. All variables are passes by reference to allow two way data binding. For more information see Views.

// Router PHP
$tipsy->router()
    ->when('chat', function($Scope, $View) {
        $Scope->message = 'Hello!';
        $View->display('chat'); // will output "<b>Hello!</b>"
    });
<?/* Chat PHTML */ ?>
<b><?=$message?></b>

Tipsy

Direct access to the Tipsy object.

$tipsy->router()
    ->when('debug', function($Tipsy) {
        var_dump($Tipsy->config());
    });

Route

Direct access to the current route

$tipsy->router()
    ->when('debug', function($Route) {
        var_dump($Route);
    });

Params

Access to route params. Anything that starts with ":" will populate this service.

GET /user/devin
$tipsy->router()
    ->when('user/:id', function($Params) {
        echo $Params->id;
    });
devin

Request

The HTTP request service. Access to request variables or JSON data. Data is accessible by it's properties/

Methods

  • base() - returns the basepath of the script
  • host() - returns the hostname
  • loc($x) - returns the path piece from the path
  • path() - returns the path of the request
  • url - returns the full path including hostname
GET /?var=test
$tipsy->router()
    ->get('/', function($Request) {
        echo $Request->var;
    });
test

Db

Db provides basic database connectivity.

$tipsy->config([db => [url => 'mysql://user:pass@host:port/database']]);
//or
$tipsy->config([db => [
    host => 'host',
    user => 'user',
    pass => 'pass',
    port => 'port',
    database => 'database',
    driver => 'mysql'
]);
$tipsy->db()->query('select * from stuff');

Middleware

Middleware allows you to intercept or execute services before the app runs. Use the run function as you would construct. Middleware supports the same definitions as Services.

Login middleware example

$tipsy = new Tipsy\Tipsy;

$tipsy->middleware('LoginService', [
    'run' => function() {
        if (!$_SESSION['user']) {
            // login
        } else {
            $this->user = 'devin';
        }
    }
]);

$tipsy->router()->home(function($LoginService) {
    echo $LoginService->user;
});

$tipsy->run();

A middleware closure using dependency injection

This will create the middleware and run it immediately.

$tipsy->middleware(function($Request) {
    if ($Request->loc() == 'api') {
        // do special login stuff for any /api/* url
    }
});

Views

Tipsy comes with a phtml template system. It supports layouts as well as cascading include directories.


Examples

Single view with no layout

By default the path is the current directory. But you can set it to anything you want.

// Set the path of the view
$tipsy->config(['view' => [
    'path' => 'views'
]]);

// Set up the router
$tipsy->router()
    ->when('/', function($View, $Scope) {
        $View->display('home', ['user' => 'Devin']);
    });
<?/* view located in views/home.phtml */?>
Welcome <b><?=$user?></b>!

Adding a layout

By default Tipsy will look for layout.phtml in your view path, but you can set it to anything you want.

// Set the path of the view and layout
$tipsy->config(['view' => [
    'path' => 'views',
    'layout' => 'awesome-layout'
]]);
<?/* layout located in views/awesome-layout.phtml */?>
<title>Awesome</title>
<h1>Awesome.sauce</h1>

<div class="content">
    <?=$this->content?>
</div>

Returning output

The view service supports both display, which outputs, and render, which returns the output to a variable.

$tipsy->router()
    ->when('/chat/reply', function($View) {
        $output = $View->render('reply');

        echo json_encode([
            'time' => date('Y-m-d H:i:s'),
            'html' => $output
        ]);
    });

Two way data binding

All variables are passed by reference so you can change anything in, and out of the templates.

// Router PHP
$tipsy->router()
    ->when('chat', function($Scope, $View) {
        $Scope->message = 'Hello!';
        $View->display('chat');         // will output "<b>Hello!</b>"
        echo $Scope->message;           // will output "Goodbye!"
    });
<? /* chat.phtml */ ?>
<b><?=$message?></b>
<? $message = 'Goodbye!'?>

Displaying a partial template

By accessing the $this variable you can access the current $View service.

render

Renders a file and returns as string

<?=$this->render('file', ['user' => 'devin'])?>
display

Calls the render function and prints the output

<? $this->display('file', ['user' => 'devin']); ?>
$include

Renders a file with the current scope and returns the output

<?=$include('file', ['user' => 'devin'])?>

Adding paths in config

You can set config either using the methods above, or using an config file. For more information on config, see Config.

// Tell tipsy to read the config file
$tipsy->config('config.ini');
; File located at config.ini
[view]
path=views
layout=layout
stack[]=main/english
stack[]=main/spanish
stack[]=cobrand/english
stack[]=cobrand/spanish

PHTML View Templates

.phtml differs from .php in that it is primarily for templating and views rather than scripts or code. The language itself is the same, however in phtml, it is mostly html, rather than php. This helps separate your types of code so that frontend and backend developers can better find what they need.

PHP Short Tags

Because most of the code is html, it is common to use Short Tags.

<?php echo $something ?>            // A normal opening tag, output, and closing
<?=$something?>                     // A short tag, output, and closing

For more information on how to enable them if you don't already have them, see PHP Short Tags

Alternative syntax for control structures

Reading html and looking for closing braces, it can be a pain. I recommend using the alternative syntax to make the code more readable.

Normal syntax
<? if (1 == 1) { ?>
    Duh.
<? } ?>
Alternative syntax
<? if (1 == 1) : ?>
    Duh.
<? endif; ?>

Examples

PHP only example

// assign the data
$friends = ['bob','jim','katie'];

// loop through the data and output
echo '<div class="friends">';

foreach ($friends as $friend) {
    echo '<b>'.$friend.'<b> is my friend.<br>';
}

echo '</div>';

PHP & PHTML Example

friends.php
$Scope->friends = ['bob','jim','katie'];
$View->display('friends');
friends.phtml
<div class="friends">
    <? foreach ($friends as $friend) : ?>
        <b><?=$friend?><b> is my friend.<br>
    <? endforeach ; ?>
</div>

View Scope

Each time a template is rendered, a new scope is created. This scope is accessible by the current template, and any template that it renders inside of it.


Single Scope

The display and render methods of View accept 2 parameters, the template filename, and an optional scope. Scope can also be accessed using Dependency Injection.

$tipsy->when('friends', function($Scope, $View) {
    $Scope->friends = ['bob','jim','katie'];
    $View->display('friends');
});

or

$tipsy->when('friends', function($View) {
    $View->display('friends', ['friends' => ['bob','jim','katie']]);
});

Multi Scope

whatever.php
$data = [
    'user' => [
        'name' => 'devin,
        'friends' => [
            'bob',
            'jim',
            'katie'
        ]
    ]
];
$View->display('home', $data);
home.phtml
Hello <?=$user->name?>!

<? foreach ($user->friends as $friend) : ?>
    <?=$include('friend', ['user' => $friend])?>
<? endforeach ; ?>
friend.phtml
<?=$user?>

Resource

Tipsy provides a simple ORM for SQL.


Methods

load, o

Load is called on class instantiation, or when called using object notation. Accepts either an id, an array, or ab object to construct.

Load the object by $id from the database

$User->load($id);

or

User::o($id);

Create a new object with an array. You may also pass an object instead of an array in replace of any of the 3 examples below.

$user = $User->load([
    name => 'devin'
]);

or

$user = new User([
    name => 'devin'
]);

or

$user = User::o([
    name => 'devin'
]);
query, q

Send a query to retrieve a list of items. This will return a Looper object.

$User->query('select * from user where name like ', ['devin%']);

or

User::q('select * from user where name like ', ['devin%']);

Note that both the Resource object and the Looper object have the method get. You can always safely use $object->get(0) regardless of object type.

dbId

Returns the database id of the object

$user = $User->load(1);
echo $user->dbId(); // prints 1
properties

Returns the objects properties as an array

$user = new User([
    name => 'devin'
]);
var_dump($user->properties()); // dumps an array with the user devin
exports

By default, returns the object's properties. This method is called internally by json(), and is best used when dumping different data for different users. See the example below for more info.

$user = new User([
    name => 'devin'
]);
var_dump($user->exports()); // dumps an array with the user devin
json

Resource allows use of $resource->json() and json_encode($resource) interchangeably. Both these method call the exports method.

$user = new User([
    name => 'devin'
]);
echo $user->json(); // prints the json output of the resources properties
createTable

Not yet fully implemented

dropTable

Not yet fully implemented


Resource creation

Resources are defined using Tipsy's Service interface. See User-Defined-Services for more information.

$tipsy->service('User', [
    _id => 'id',
    _table => 'user'
]);

Examples

Loading an item using route params
// create the User model based on the resource class
$tipsy->service('User', [
    _id => 'id',
    _table => 'user'
]);
// load the user when the route is visited and display the user name
$tipsy->when('user/:id', function($Params, $User) {
    echo $User->load($Params->id)->name;
});
Overload the exports method for a user
// create the User model
class User extends \Tipsy\Resource {
    public function exports() {
        $props = $this->properties();

        // properties viewable by anyone
        $public = ['id', 'name', 'location', 'website', 'image'];

        // properties viewable only by the logged in user
        $private = ['email', 'gender', 'auth'];

        if (Tipsy::middleware('Session')->user()) {
            $public = array_merge($public, $private);
        }

        foreach ($props as $key => $prop) {
            if (!in_array($key, $public)) {
                unset($props[$key]);
            }
        }

        return $props;
    }

    public function __construct($id = null) {
        $this->idVar('id')->table('user')->load($id);
    }
}

// load the user when the route is visited and display the users properties
$tipsy->when('user/:id', function($Params, $User) {
    echo $User->load($Params->id)->json();
});

Factory

Factory provides an in memory cache of objects with a type, key, value store system. This is enabled by default for Resource.


Use with Resource

When objects are loaded from the database using the load method they are automatically added to the factory cache.

$user = User::o(1);
$user->name = 'new name';

$user = User::o(1); // load the item from memory instead of another database call
echo $user->name; // prints "new name"

Standalone usage

The factory method gets or sets an object by classname, or passed object

$object = new User([
    id => 1
]);

$u = $tipsy->factory($object);
// or
$u = $tipsy->factory('User', $object);

Disabling

Disable in php

$tipsy->config(['tipsy' => ['factory' => false]]);

or in a config file

tipsy:
  factory: false

Looper

Looper provides an interface similar to Iterator with some Resource specific features.


Methods

each

Looper allows the use of $loop->each(), foreach ($loop as $item), or while($loop->valid() interchangeably.

foreach ($loop as $item) {
    //item
}

or

$loop->each(function() {
    // $this
});

or

while ($loop->valid()) {
    // $loop->current()
    $loop->next();
}
get

Get grabs an item in the loop by index.

$loop->get(0);
json

Looper allows the use of $loop->json() or json_encode($loop) interchangeably.

echo $loop->json();

or

echo json_encode($loop);
set

Set properties of all items in a loop

$loop = new \Tipsy\Looper([
    (object)['a' => 1],
    (object)['a' => 1],
    (object)['a' => 2]
]);
$loop = $loop->set('a', 1);
echo $loop->json(); // returns 3 items with a set to 1
filter

Filtering a Loop will return all objects who's properties match the properties of a passed argument.

$loop = new \Tipsy\Looper([
    (object)['a' => 1],
    (object)['a' => 1],
    (object)['a' => 2]
]);
$loop = $loop->filter([
    'a' => 1
]);
echo $loop->json(); // returns the first 2 items as json
not

Not works the same as filter except backwards

$loop = new \Tipsy\Looper([
    (object)['a' => 1],
    (object)['a' => 1],
    (object)['a' => 2]
]);
$loop = $loop->not([
    'a' => 1
]);
echo $loop->json(); // returns the last item in json
slice

Slice works similar to array_slice

$loop = new \Tipsy\Looper([
    (object)['a' => 1],
    (object)['a' => 1],
    (object)['a' => 2]
]);
$loop = $loop->slice(1, 2);
echo $loop->json(); // returns the last 2 items in json
parent

When nesting loops, parent returns the immediate parent loop. This is most useful when using filters

$loop = new \Tipsy\Looper([
    (object)['a' => 1],
    (object)['a' => 1],
    (object)['a' => 2]
]);
$loop = $loop->filter(['a' => 1])->set('a', 2)->parent();
echo $loop->json(); // returns a list of 3 items with a set to 2

Advanced Usage

You can see some more advanced examples in tests/LooperTest.php.


Examples

Basic usage

use Tipsy\Tipsy;

$app = new Tipsy;
$app->get('home', function($View) {
    $View->display('home', [user => 'crystal']);
});
<h1>Hello <?=$user?></h1>

API & DBO usage

$app->post('drink/:id', function($Params, $Request, $Maitai) {
    $Maitai
        ->load($Params->id)
        ->serialize($Request->request())
        ->save();
    echo $Maitai->json()
});
POST /drink/1?rating=5&name=maitai

Example code

All example code is provided in their own repo with 1 click deployments provided free by Heroku.


Plugins

Officially supported plugins


About

The goal of Tipsy is to provide a very lightweight PHP framework, capable of handling just about everything.

Performance

Tipsy is benchmarked by kenjis php framework benchmark and currently rated at the fastest pure php framework as of 11/29/2015.

Contribute

See Contributing for info on issues, pull requests, and contact.

Author

Tipsy is created and maintained by Devin Smith.