Tuesday, April 2, 2013

Couchbase, PHP, XAMPP and Windows

A while ago I blogged about Couchbase, PHP and IIS, and earlier today I got a request for more information on how to do the same with XAMPP.

After I downloaded the installer and installed XAMPP in c:\xampp I downloaded one of my development builds of the php extension. Please note that this is a development build I've used for simple testing for myself, and I provide it AS IS WITHOUT ANY WARRANTIES so use it on your own risk (and not in production ;) ).

To "install" the extension, extract the zipfile and execute the following commands from within that directory:

copy php_couchbase.dll c:\xampp\php\ext
copy libcouchbase.dll c:\xampp\php
copy libcouchbase.dll c:\xampp\apache\bin

Use the XAMPP Control panel to edit the php.ini file, and enable the extension by adding:

[couchbase]
extension=php_couchbase.dll

(You may also change other tunables. See couchbase.ini for a full list).

(Re)start the Apache server from the XAMPP Control panel, and start writing code using the PHP extension. You'll find a description of the API in the file couchbase-api.php

Happy hacking!

Monday, April 1, 2013

Building Couchbase PHP driver on Ubuntu

I've been testing our PHP connector on Ubuntu lately, so I thought I should tell all of you how I'm doing this. I know a lot of people prefer to run Linux on their desktop, but I don't have any boxes running Linux at home. What I do have is a server running SmartOS, which makes it really easy to spin up new virtual machines with "any" operating system I'd like to test.

The first thing I do on my SmartOS server is to create a manifest file for my new vm. This manifest file is a JSON file describing the vm, and yesterday I was going to build on Ubuntu 12 so I created a file named ubuntu12.json looking like this:

{
   "brand": "kvm",
   "vcpus": 1,
   "autoboot": false,
   "alias": "ubuntu12",
   "ram": 1024,
   "resolvers": ["10.0.0.1"],
   "disks": [
      {
         "boot": true,
         "model": "virtio",
         "size": 10240
      }
   ],
   "nics": [
      {
         "nic_tag": "admin",
         "model": "virtio",
         "ip": "10.0.0.251",
         "netmask": "255.255.255.0",
         "gateway": "10.0.0.1"
      }
   ]
}

with that in place I created the virtual machine with the following command:

[root@smartos ~] # vmadm create -f ubuntu12.json
Successfully created 3d09ad09-e124-4b68-90fd-53c76f05dbc0

The next thing I did was to copy the Ubunto 12.10 installation iso file to /zones/3d09ad09-e124-4b68-90fd-53c76f05dbc0/root/cdrom.iso, before I executed the following command:

[root@smartos ~] # vmadm boot 3d09ad09-e124-4b68-90fd-53c76f05dbc0 order=cd,once=d cdrom=/cdrom.iso,ide

This boots the vm and provides a "console" to the machine over vnc. To figure out where to connect the vncviewer to complete the installation I executed:

root@smartos ~]# vmadm info 3d09ad09-e124-4b68-90fd-53c76f05dbc0 vnc
{
  "vnc": {
    "host": "10.0.0.22",
    "port": 39944,
    "display": 34044
  }
}

With my Ubuntu 12.10 installed the first thing I did was to install some extra packages:

trond@ubuntu12:~$ sudo wget -O/etc/apt/sources.list.d/couchbase.list http://packages.couchbase.com/ubuntu/couchbase-ubuntu1204.list
trond@ubuntu12:~$ wget -O- http://packages.couchbase.com/ubuntu/couchbase.key | sudo apt-key add -
trond@ubuntu12:~$ sudo apt-get update
trond@ubuntu12:~$ sudo apt-get install openssh-server php5-dev php5-cli libcouchbase2 libcouchbase2-libevent libcouchbase2-bin libcouchbase-dev
trond@ubuntu12:~$ wget -Orepo https://git-repo.googlecode.com/files/repo-1.19
trond@ubuntu12:~$ chmod a+x repo
trond@ubuntu12:~$ sudo cp repo /usr/bin

I can now build the php extension by running:

trond@ubuntu12:~$ mkdir compile
trond@ubuntu12:~$ cd compile
trond@ubuntu12:~/compile$ repo init -u git://github.com/trondn/manifests -m php.xml
trond@ubuntu12:~/compile$ cd php
trond@ubuntu12:~/compile$ phpize
trond@ubuntu12:~/compile$ ./configure
trond@ubuntu12:~/compile$ make
trond@ubuntu12:~/compile/php$ cd tests
trond@ubuntu12:~/compile/php/tests$ cp couchbase.local.inc.dist couchbase.local.inc
trond@ubuntu12:~/compile/php/tests$ vi couchbase.local.inc

Now you should specify the hostname of your Couchbase cluster in COUCHBASE_CONFIG_HOST, and clear (or specify correct values) for COUCHBASE_CONFIG_USER and COUCHBASE_CONFIG_PASSWD).

trond@ubuntu12:~/compile/php/tests$ cd ..
trond@ubuntu12:~/compile/php$ make test

Depending on your cluster configuration you might encounter some test errors (the tests are unfortunately not written fully bullet proof). If you have a one-node cluster with a replica count set to 1, the following tests will fail (because they don't check for "inconsistent" configuration):

GetReplica - GetReplica [tests/phpt/GetReplica/GetReplica.phpt]
GetReplica - GetReplicaMulti [tests/phpt/GetReplica/GetReplicaMulti.phpt]

You can now install the driver by:

trond@ubuntu12:~/compile/php$ sudo cp modules/couchbase.so /usr/lib/php5/20100525
trond@ubuntu12:~/compile/php$ sudo cp example/couchbase.ini /etc/php5/cli/conf.d

And verify that it is properly installed by running:

trond@ubuntu12:~/compile/php$ php -i | grep couchbase
/etc/php5/cli/conf.d/couchbase.ini
couchbase
couchbase support => enabled
couchbase.compression_factor => 1.3 => 1.3
couchbase.compression_threshold => 2000 => 2000
couchbase.compressor => none => none
couchbase.durability_default_poll_interval => 100000 => 100000
couchbase.durability_default_timeout => 40000000 => 40000000
couchbase.instance.persistent => On => On
couchbase.restflush => On => On
couchbase.serializer => php => php
couchbase.view_timeout => 75 => 75

So let's go ahead and create a small test using our driver. Create the file test.php with the following content:

<?php
  try {
     $cb = new Couchbase("mycluster");
     print "Store foo: " . $cb->set("foo", "bar") . "\n";
     print "Get foo: " . $cb->get("foo") . "\n";
  } catch (CouchbaseException $e) {
     var_dump($e);
  }
?>

And test it with the following command:

trond@ubuntu12:~/compile/php$ php test.php
Store foo: 14566767818228433408
Get foo: bar

Happy hacking!!!

Monday, February 4, 2013

Accessing Couchbase from PHP on your Mac!


Given that I've already blogged about how to use PHP from IIS on Windows I figured I should do another blog post for people using Mac OSX. Luckily for us Mac OSX ships with both Apache2 and PHP, so its fairly easy to start using it.

Our PHP extension is built on top of libcouchbase, so the first thing we need to do is to install that. I guess most Mac users are already using homebrew to get stuff onto their Mac so installing libcouchbase is simply a matter of:

trond@ok ~> brew install libcouchbase

The next thing we need to do is to download the PHP extension and install it somewhere
locally. Personally I do like to put such modules in it's own directory, so I'm going to use /opt/couchbase/lib for now:

trond@ok ~> cd /tmp
trond@ok /tmp> wget http://packages.couchbase.com/clients/php/php-ext-couchbase-1.1.2-macosx-x86_64.tar.gz
trond@ok tmp> tar xfz php-ext-couchbase-1.1.2-macosx-x86_64.tar.gz
trond@ok /tmp> sudo mkdir -p /opt/couchbase/lib
trond@ok /tmp> sudo cp php-ext-couchbase/couchbase.so /opt/couchbase/lib

With the plugin in place we need to tell php to load it. To do so add the following line in /etc/php.ini:

extension=/opt/couchbase/lib/couchbase.so

This should be enough to use Couchbase from PHP, so lets go ahead and verify that it works:

trond@ok ~> php -i | grep couchbase
couchbase
couchbase support => enabled
couchbase.compression_factor => 1.3 => 1.3
couchbase.compression_threshold => 2000 => 2000
couchbase.compressor => none => none
couchbase.durability_default_poll_interval => 100000 => 100000
couchbase.durability_default_timeout => 40000000 => 40000000
couchbase.serializer => php => php
couchbase.view_timeout => 75 => 75

Yay! We're almost there. To enable PHP in Apache2 we need to uncomment the following line in /etc/apache2/httpd.conf

#LoadModule php5_module libexec/apache2/libphp5.so

Remove the # and save the file. We need to start (or restart) apache2 for the changes to take effect:

trond@ok ~> sudo apachectl start

So lets check that it works! Go ahead and create the following file in $HOME/Sites/phpinfo.php

<?php phpinfo(); ?>

and access it: http://localhost/~your-username/phpinfo.php

You should get a page with a lot of text, and if you search the page you should find a section with information about the Couchbase extension.

Now that we've got something that works, lets create a small example page that actually use the extension. Go ahead and create a file named $HOME/Sites/index.php with the following content:

<html>
    <head>
        <title>Yay</title>
    </head>
    <body>
        <h1>This page has been accessed:
            <?php
            try {
                $cb = new Couchbase();
                print($cb->increment("counter", 1, true, 0, 0));
            } catch (CouchbaseException $ex) {
                var_dump($ex);
            }
            ?>
        </h1>
    </body>
</html>

If you have a Couchbase server running on the same machine you should be able to access this page through http://localhost/~yourname/ and for each time you access the page the counter should increase.

I'll write another blog post with examples that utilize the API.

Tuesday, December 11, 2012

IIS, PHP and Windows

In this short blog post I’m going to show you how to use PHP to access your Couchbase cluster on your Windows machine. I have a limited set of hardware at home, so I used a laptop with Microsoft Windows 7 home premium edition to test this example. Unfortunately for you that laptop came with a Norwegian version of Windows, so I can't tell you the exact name for the various menu items.

The first thing we need to do is to install Microsoft Internet Information Services (I'm going to refer to this as IIS) on your computer. You do so by selecting the "enable/disable windows features", and check the box for "Internet Information Services". Expand that selection and locate and expand "Webservices", and then locate and expand something like "application development" (sorry for not having an english copy of Windows ;)). In this category check the CGI checkbox. At the level right beneath the Internet Information Services you should have an entry containing something like "tools for web management", and in this sub category you should check "IIS-management console".


So lets go ahead and verify that the IIS installation works, and that it doesn’t support PHP already (that would render this blogpost pretty useless right ;)) Go ahead and put the following into c:\inetpub\webroot\test.php:

   <?php
   phpinfo();
   ?>


If you try to connect to http://localhost/test.php, you should either a page with an error message. If you get something else your IIS installation may already be configured (and hopefully we'll make it work).


The next thing we need to do is to install PHP and the Couchbase extension. At the time of writing we don’t have a binary package you can install, but it isn’t really hard to build it yourself. If you follow the steps I outlined in http://trondn.blogspot.no/2012/11/building-php-extension-for-couchbase-on.html you should be up’n’running in no time. I followed that instruction when I wrote this example, but I used a slightly different configure command:


configure --disable-all --enable-cli --with-couchbase=shared --enable-json --enable-cgi --with-config-file-scan-dir=C:\php\conf.d


You should now create C:\php\conf.d\couchbase.ini with the following content:



extension=php_couchbase.dll


Now that we’ve got IIS and PHP installed its time to look at the magic to glue them together. Open up the IIS Service Management Console (as I’ve already said I've got Norwegian windows so I may have used the wrong translation there) and select “Handler Mappings”, and in the actions pane (upper right corner) select “Add Module Mapping”. Fill in the following details starting from the top: “*.php”, “FastCgiModule”, “C:\php\php-cgi.exe” and whatever you want as the description. Be sure to answer “Yes” on the next dialog that asks if it should create a FastCGI application for this executable (if not it won’t work). You may also want to enter the “default document” setting and add index.php as an alternative. By doing that IIS will search for a file named index.php if the URL specifies a directory.

With this in place it’s time to retry our test page. Tune your browser back to http://localhost/test.php, and this time you should see a nice page with a lot of info. Scroll down and you should locate a section with information about the Couchbase extension.

Yay!!! We made that work, but being able to run PHP from IIS isn’t that fun all by itself.. Let’s see if we can get the Couchbase beersample for php working!

Go ahead and download and install Couchbase server 2.0 from http://www.couchbase.com/download, and choose that you want to install the beer sample database during configuration.
With that in place you can download the PHP example program I wrote earlier today (I’ll describe that in another blog post) from https://github.com/trondn/beersample-php/archive/master.zip. Unzip that program in C:\inetpub\webroot. Before we can try the sample application you have to create two new views in Couchbase. The first one should be in a design document named brewery and named by_name and look like:



    function (doc, meta) {
        if (doc.type == "brewery") {
            emit(meta.id, doc.name);
        }
    }


The next one is located in the beer design document and named by_name and looks like:


function (doc, meta) { if (doc.type == "beer") { emit(doc.name, doc.brewery_id); } }


Make sure you remember to press the publish button after you create them, or things won't work.


You should now be able to direct your browser to http://localhost/beersample-php-master and start browsing breweries :)

Happy hacking!

Thursday, November 1, 2012

Building the PHP extension for Couchbase on Microsoft Windows!


I've got a pretty good background from doing cross platform work. My first job as a software developer was to port a system to Trusted Solaris (and add privilege bracketing), but I've been porting stuff to Windows, HP-UX, Linux etc to name a few later on. It is important to me that the software should be portable, and that was one of the fixed requirement I had when I started libcouchbase back in the days.

Two days ago I sat down with Matt planning what I should focus on for the SDK team, and we both felt that we had to give the Windows support for some of our projects a makeover. Given PHPs popularity I figured that I should start with that.

I was kind of surprised to see how easy it was to build PHP with support for Couchbase on windows, but relax; we are going to ship binary versions for you so that you won't have to do this yourself, but being a geek I thought you might find it interesting to read how to do it yourself.

The first thing you have to do is to get set up a development environment. I initially used the description from PHP wiki, with some minor modifications. Feel free to use the steps outlined in that link, but I'll this is however how I did it. First I installed the following software:


  • Windows server 2008 r2
  • Visual studio 2008 professional
  • Windows SDK 6.1
  • git source control system
  • winrar


With that installed, you can open up a "windows sdk" shell, and get execute the following commands:

setenv /x86 /xp /release
mkdir c:\php-sdk

By using winrar I then extracted the php-sdk-binary-tools-20110915.zip into that directory. I could then create the directories and get ready to build the source.

cd c:\php-sdk
bin\phpsdk_setvars.bat
bin\phpsdk_buildtree.bat php-src

Start up a "git-bash shell", and navigate to c:\php-src\vc9\x86, and check out the code. Unfortunately this won't work as of today for you because not all of the patches for the source code have been pushed through yet:

git clone git://github.com/php/php-src
git clone git://github.com/couchbase/libcouchbase
cd php-src/ext
git clone git://github.com/couchbase/php-ext-couchbase couchbase

We're now ready to build the source! The first thing we need to do is to build libcouchbase (the C library that provides the functionality to talk to the Couchbase cluster):

cd libcouchbase
nmake -f NMakefile all install

With that installed we're ready to build the PHP module that expose the PHP functionality:

cd ..\php-src
buildconf
configure --disable-all --enable-cli --with-couchbase=shared
nmake all install
copy ../deps/bin/libcouchbase.dll c:\php

As you see I'm disabling all other modules than the Couchbase plugin (and I'm making that as a shared object). You would have to populate the "deps" directory with other libraries if you wanted to add support for them (but that's beyond the intention of this blog post).

So let's go ahead and try our new php extension. If you don't have a Couchbase cluster running already, now is the time to install one :) With the Couchbase cluster running, you can create a simple php.ini file that tells php to load our module:

extension=c:\php\php_couchbase.dll

I'm pretty sure you all know PHP way better than me, so please forgive me for the stupid code we'll use to show that it's working. I created the file test.php with the following content:

<?php
   $cb = new Couchbase("","","","default");
   $cb->set("hello", "world");
   var_dump($cb->get("hello"));
?>

Running the program with:

cd c:\php
php -f test.php

Should print out:

string(5) "world"

Thats all for this time! Happy hacking!

Shift of focus!


I really like going to conferences, not necessarily because I want to attend the different sessions, but because its a great arena to meet up with users and people interested in the same stuff as I am. Yesterday I attended CouchConf in Berlin, which was a lot of fun. I got to meet old friends, people I've only talked to on IRC (so fun to get a face behind the nick) and new people!

As part of attending CouchConf I spent some time talking to Matt Ingenthron about our SDKs, so I'm happy to announce that as of today I'm going to spend a significant amount of time working on our clients. Up until now I've done most of my contributions to the clients on my spare time, so I'm really looking forward to be able to spend my entire day trying to make your life easier.

Friday, September 28, 2012

YANFS - Yet Another Network File System

In the last few blog posts we've been exploring different kinds of how you might want to utilize libcouchbase from your app. To me it has always been really hard to come up with good example apps, so I always end up creating pretty boring applications.

Those of you who attended CouchConf in San Francisco really missed out if you didn't watch Couchbase Labs demo sessions, where they showed off CBFS. I'm not going to go into any details about what CBFS is (hopefully Dustin Sallings will create a blog post about that), but you can think cbfs as a large object store analogous to S3 (but meant to run in your own environment).

CBFS is implemented in the go language (if you haven't looked at that yet, put that on your todo list!), and it's shipped with it's own client to perform operations (upload files, list files etc). This gave me a great idea for todays blog post! FUSE is a project that allows you to create a filesystem implementation in user space, so the topic for today is an example where we utilize libcouchbase in our implementation for our FUSE driver.

As usual I won't explain the entire code, but I'll try to comment on the important bits. You'll find the source code for the entire project at http://github.com/trondn/mount_cbfs

The readme in the project should tell you how to install fuse and build the stuff, so in this blog post I'm only going to focus on how it works.

Creating a full featured filesystem driver is a lot of work, so I figured it would be better to start with a minimal implementation with a lot of limitations we could fix up later on (or I would never get around building it ;))


  • It is fully single threaded. This should be pretty easy to fix up later on by using a pool of libcouchbase instances, no shared data etc..
  • Read Only. This limits the number of entry points we need to implement.
So let's start looking at the code and how it all fits together. In main we register the struct containing all of the function pointers to the functions our filesystem support:

static struct fuse_operations cbfs_oper = {
    .getattr = cbfs_getattr,
    .open = cbfs_open,
    .read = cbfs_read,
    .readdir = cbfs_readdir,
};

This means that whenever someone tries to open a file in our filesystem, cbfs_open is called etc. So let's walk through an example here: What is the flow when you're typing "ls -l" in your shell.

The first things that happens is that cbfs_getattr is called. Its responsibility is to populate a stats struct with information about the file. Let's skip the details for now, and just assume that we return that this is a directory. Now cbfs_readdir is called with the directory to receive all of the entries in the directory, before cbfs_gettattr is called for every file to get the details of the files.

So how does our cbfs_readdir work like (after all this blog is about libcouchbase, not fuse ;)). CBFS use a http interface so we can request the list of files in a directory through the following URL:

http://cbfsserver:8484/.cbfs/list/path

It returns a JSON document looking something like:

{"files":{"file1":{}},"dirs":{},"path":"/foo"}

All we need to do is to decode the JSON and populate the information to FUSE. So how do we do this through libcouchbase? Through the http interface:

static lcb_error_t uri_execute_get(const char *uri, struct SizedBuffer *sb) {
    lcb_http_cmd_t cmd = {
        .version = 1,
        .v.v1 = {
            .path = uri,
            .npath = strlen(uri),
            .body = NULL,
            .nbody = 0,
            .method = LCB_HTTP_METHOD_GET,
            .chunked = 0,
            .content_type = "application/x-www-form-urlencoded",
            .host = cfg->cbfs_host,
            .username = cfg->cbfs_username,
            .password = cfg->cbfs_password
        }
    };

    return lcb_make_http_request(instance, sb, LCB_HTTP_TYPE_RAW, &cmd, NULL);
}

Since I'm using the synchronous interface to libcouchbase that call will block until we've received the response from the server. In my response handler I just copy whatever data the server returned to me:

static void complete_http_callback(lcb_http_request_t req, lcb_t instance,
                                   const void *cookie, lcb_error_t error,
                                   const lcb_http_resp_t *resp)
{
    struct SizedBuffer *sb = (void*)cookie;

    if (error == LCB_SUCCESS) {
        /* Allocate one byte extra for a zero term */
        sb->data = malloc(resp->v.v0.nbytes + 1);
        sb->size = resp->v.v0.nbytes;
        memcpy(sb->data, resp->v.v0.bytes, resp->v.v0.nbytes);
        sb->data[resp->v.v0.nbytes] = '\0';
    }
}

Both cbfs_read and cbfs_getattr utilize the same function from libcouchbase, but they're only hitting different URL's to get their data.

Adding supports for write operations should be no worse than finding the correct URL and do a PUT etc.

Happy Hacking :)