Products Customers Pricing Docs Help Blog

PHP Guide

If you haven't set up your project yet, please head over to the QuickStart guide to get up and running. You can also check out our API Reference for more detailed information about our SDK.

Introduction

The Parse platform provides a complete backend solution for your mobile application. Generally, our goal is to eliminate the need for managing servers and writing server-side code. Our PHP SDK is for those applications and situations where server-side code is necessary, or simply prefered.

The Parse PHP SDK is fully open source, and anyone can contribute to make it better, or make their own changes if necessary. Check out the GitHub repository for more information.

The Parse PHP SDK requires version 5.4 or greater of the PHP runtime.

Apps

On Parse, you create an App for each of your applications. Each App has its own application id and REST API key that you apply to your web app. Your account on Parse can accommodate multiple Apps. This is useful even if you have one application, since you can deploy different versions for test and production.

Objects

ParseObject

Storing data on Parse is built around the ParseObject. Each ParseObject contains key-value pairs of JSON-compatible data. This data is schemaless, which means that you don't need to specify ahead of time what keys exist on each ParseObject. You simply set whatever key-value pairs you want, and our backend will store it.

For example, let's say you're tracking high scores for a game. A single ParseObject could contain:

score: 1337, playerName: "Sean Plott", cheatMode: false

Keys must be alphanumeric strings. Values can be strings, numbers, booleans, or even sequential arrays and associative arrays - anything that can be JSON-encoded. Note however that Arrays and Associative Arrays require separate methods to set them on a ParseObject.

Saving Objects

Let's say you want to save the GameScore described above to the Parse Cloud. The interface is similar to a our other SDKs, including the save method:

$gameScore = new ParseObject("GameScore");

$gameScore->set("score", 1337);
$gameScore->set("playerName", "Sean Plott");
$gameScore->set("cheatMode", false);

try {
  $gameScore->save();
  echo 'New object created with objectId: ' . $gameScore->getObjectId();
} catch (ParseException $ex) {  
  // Execute any logic that should take place if the save fails.
  // error is a ParseException object with an error code and message.
  echo 'Failed to create new object, with error message: ' + $ex->getMessage();
}

After this code runs, you will probably be wondering if anything really happened. To make sure the data was saved, you can look at the Data Browser in your app on Parse. You should see something like this:

objectId: "xWMyZ4YEGZ", score: 1337, playerName: "Sean Plott", cheatMode: false,
createdAt:"2011-06-10T18:33:42Z", updatedAt:"2011-06-10T18:33:42Z"

There are two things to note here. You didn't have to configure or set up a new Class called GameScore before running this code. Your Parse app lazily creates this Class for you when it first encounters it.

There are also a few fields you don't need to specify that are provided as a convenience. objectId is a unique identifier for each saved object. createdAt and updatedAt represent the time that each object was created and last modified in the cloud. Each of these fields is filled in by Parse, so they don't exist on a ParseObject until a save operation has completed.

Retrieving Objects

Saving data to the cloud is fun, but it's even more fun to get that data out again. If you have the objectId, you can retrieve the whole ParseObject using a ParseQuery:

$query = new ParseQuery("GameScore");
try {
  $gameScore = $query->get("xWMyZ4YEGZ");
  // The object was retrieved successfully.
} catch (ParseException $ex) {
  // The object was not retrieved successfully.
  // error is a ParseException with an error code and message.
}

To get the values out of the ParseObject, use the get method.

$score = $gameScore->get("score");
$playerName = $gameScore->get("playerName");
$cheatMode = $gameScore->get("cheatMode");

The three special values are provided as the result of methods:

$objectId = $gameScore->getObjectId();
$updatedAt = $gameScore->getUpdatedAt();
$createdAt = $gameScore->getCreatedAt();

If you need to refresh an object you already have with the latest data that is in the Parse Cloud, you can call the fetch method like so:

$gameScore->fetch();

Updating Objects

Updating an object is simple. Just set some new data on it and call the save method. For example:

// Create the object.
$gameScore = new ParseObject("GameScore");

$gameScore->set("score", 1337);
$gameScore->set("playerName", "Sean Plott");
$gameScore->set("cheatMode", false);
$gameScore->setArray("skills", ["pwnage", "flying"]);

$gameScore->save();
// Now let's update it with some new data. In this case, only cheatMode and score
// will get sent to the cloud. playerName hasn't changed.
$gameScore->set("cheatMode", true);
$gameScore->set("score", 1338);
$gameScore->save();

Parse automatically figures out which data has changed so only "dirty" fields will be sent to the Parse Cloud. You don't need to worry about squashing data that you didn't intend to update.

Counters

The above example contains a common use case. The "score" field is a counter that we'll need to continually update with the player's latest score. Using the above method works but it's cumbersome and can lead to problems if you have multiple clients trying to update the same counter.

To help with storing counter-type data, Parse provides methods that atomically increment (or decrement) any number field. So, the same update can be rewritten as:

$gameScore->increment("score");
$gameScore->save();

You can also increment by any amount by passing in a second argument to increment. When no amount is specified, 1 is used by default.

Arrays

To help with storing array data, there are three operations that can be used to atomically change an array associated with a given key:

  • add append the given object to the end of an array field.
  • addUnique add the given object only if it isn't already contained in an array field. The position of the insert is not guaranteed.
  • remove remove all instances of the given object from an array field.

For example, we can add items to the set-like "skills" field like so:

$gameScore->addUnique("skills", ["flying"]);
$gameScore->addUnique("skills", ["kungfu"]);
$gameScore->save();

Note that it is not currently possible to atomically add and remove items from an array in the same save. You will have to call save in between every different kind of array operation.

Destroying Objects

To delete an object from the cloud:

$gameScore->destroy();

You can delete a single field from an object with the delete method:

// After this, the playerName field will be empty
$gameScore->delete("playerName");

// Saves the field deletion to the Parse Cloud
$gameScore->save();

Relational Data

Objects may have relationships with other objects. For example, in a blogging application, a Post object may have many Comment objects. Parse supports all kind of relationships, including one-to-one, one-to-many, and many-to-many.

One-to-One and One-to-Many Relationships

One-to-one and one-to-many relationships are modeled by saving a ParseObject as a value in the other object. For example, each Comment in a blogging app might correspond to one Post.

To create a new Post with a single Comment, you could write:

// Create the post
$myPost = new ParseObject("Post");
$myPost->set("title", "I'm Hungry");
$myPost->set("content", "Where should we go for lunch?");

// Create the comment
$myComment = new ParseObject("Comment");
$myComment->set("content", "Let's do Sushirrito.");

// Add the post as a value in the comment
$myComment->set("parent", $myPost);

// This will save both myPost and myComment
$myComment->save();

Internally, the Parse framework will store the referred-to object in just one place, to maintain consistency. You can also link objects using just their objectIds like so:

$post = new ParseObject("Post", "1zEcyElZ80");

$myComment->set("parent", $post);

By default, when fetching an object, related ParseObjects are not fetched. These objects' values cannot be retrieved until they have been fetched like so:

$post = $fetchedComment->get("parent");
$post->fetch();
$title = $post->get("title");

Many-to-Many Relationships

Many-to-many relationships are modeled using ParseRelation. This works similar to storing an array of ParseObjects in a key, except that you don't need to fetch all of the objects in a relation at once. In addition, this allows ParseRelation to scale to many more objects than the array of ParseObject approach. For example, a User may have many Posts that she might like. In this case, you can store the set of Posts that a User likes using relation. In order to add a Post to the "likes" array of the User, you can do:

$user = ParseUser::getCurrentUser();
$relation = $user->getRelation("likes");
$relation->add($post);
$user->save();

You can remove a post from a ParseRelation:

$relation->remove($post);
$user->save();

You can call add and remove multiple times before calling save:

$relation->remove($post1);
$relation->remove($post2);
$user->save();

You can also pass in an array of ParseObject to add and remove:

$relation->add([$post1, $post2, $post3]);
$user->save();

By default, the array of objects in this relation are not downloaded. You can get an array of the posts that a user likes by using the ParseQuery returned by getQuery. The code looks like:

$postsLiked = $relation->getQuery()->find();
// $postsLiked contains the posts that the current user likes.

If you want only a subset of the Posts, you can add extra constraints to the ParseQuery returned by query like this:

$query = $relation->getQuery();
$query->equalTo("title", "I'm Hungry");
$postsLiked = $query->find();
// $postsLiked contains post liked by the current user which have the title "I'm Hungry".

For more details on ParseQuery, please look at the query portion of this guide. A ParseRelation behaves similar to an array of ParseObject for querying purposes, so any query you can do on an array of objects, you can do on a ParseRelation.

Data Types

So far we've used values with type String, Number, and ParseObject. Parse also supports PHP DateTimes and null.

You can nest PHP arrays and associative arrays (JSON Objects) to store more structured data within a single ParseObject.

Some examples:

$number = 42;
$string = "the number is " . $number;
$date = new DateTime();
$array = [$string, $number];
$object = ["number" => $number, "string" => $string];

$bigObject = new ParseObject("BigObject");
$bigObject->set("myNumber", $number);
$bigObject->set("myString", $string);
$bigObject->set("myDate", $date);
$bigObject->setArray("myArray", $array);
$bigObject->setAssociativeArray("myObject", $object);
$bigObject->set("myNull", null);
$bigObject->save();

ParseObjects should not exceed 128 kilobytes in size.

For more information about how Parse handles data, check out our documentation on Data & Security.

Queries

We've already seen how a ParseQuery with get can retrieve a single ParseObject from Parse. There are many other ways to retrieve data with ParseQuery - you can retrieve many objects at once, put conditions on the objects you wish to retrieve, and more.

Basic Queries

In many cases, get isn't powerful enough to specify which objects you want to retrieve. ParseQuery offers different ways to retrieve an array of objects rather than just a single object.

The general pattern is to create a ParseQuery, put conditions on it, and then retrieve an Array of matching ParseObjects using find. For example, to retrieve of the scores with a particular playerName, use the equalTo method to constrain the value for a key.

$query = new ParseQuery("GameScore");
$query->equalTo("playerName", "Dan Stemkoski");
$results = $query->find();
echo "Successfully retrieved " . count($results) . " scores.";
// Do something with the returned ParseObject values
for ($i = 0; $i < count($results); $i++) { 
  $object = $results[$i];
  echo $object->getObjectId() . ' - ' . $object->get('playerName');
}

Query Constraints

There are several ways to put constraints on the objects found by a ParseQuery. You can filter out objects with a particular key-value pair with notEqualTo:

$query->notEqualTo("playerName", "Michael Yabuti");

You can give multiple constraints, and objects will only be in the results if they match all of the constraints. In other words, it's like an AND of constraints.

$query->notEqualTo("playerName", "Michael Yabuti");
$query->greaterThan("playerAge", 18);

You can limit the number of results by setting limit. By default, results are limited to 100, but anything from 1 to 1000 is a valid limit:

$query->limit(10); // limit to at most 10 results

If you want exactly one result, a more convenient alternative may be to use first instead of using find.

$query = new ParseQuery("GameScore");
$query->equalTo("playerEmail", "dstemkoski@example.com");
$object = $query->first();

You can skip the first results by setting skip. This can be useful for pagination, though it is limited to a maximum of ten thousand:

$query->skip(10); // skip the first 10 results

For sortable types like numbers and strings, you can control the order in which results are returned:

// Sorts the results in ascending order by the score field
$query->ascending("score");

// Sorts the results in descending order by the score field
$query->descending("score");

For sortable types, you can also use comparisons in queries:

// Restricts to wins < 50
$query->lessThan("wins", 50);

// Restricts to wins <= 50
$query->lessThanOrEqualTo("wins", 50);

// Restricts to wins > 50
$query->greaterThan("wins", 50);

// Restricts to wins >= 50
$query->greaterThanOrEqualTo("wins", 50);

If you want to retrieve objects matching several different values, you can use containedIn, providing an array of acceptable values. This is often useful to replace multiple queries with a single query. For example, if you want to retrieve scores made by any player in a particular array:

// Finds scores from any of Jonathan, Dario, or Shawn
$query->containedIn("playerName",
                  ["Jonathan Walsh", "Dario Wunsch", "Shawn Simon"]);

If you want to retrieve objects that do not match any of several values you can use notContainedIn, providing an array of acceptable values. For example if you want to retrieve scores from players besides those in an array:

// Finds scores from anyone who is neither Jonathan, Dario, nor Shawn
$query->notContainedIn("playerName",
                     ["Jonathan Walsh", "Dario Wunsch", "Shawn Simon"]);

If you want to retrieve objects that have a particular key set, you can use exists. Conversely, if you want to retrieve objects without a particular key set, you can use doesNotExist.

// Finds objects that have the score set
$query->exists("score");

// Finds objects that don't have the score set
$query->doesNotExist("score");

You can use the matchesKeyInQuery method to get objects where a key matches the value of a key in a set of objects resulting from another query. For example, if you have a class containing sports teams and you store a user's hometown in the user class, you can issue one query to find the array of users whose hometown teams have winning records. The query would look like:

$teamQuery = new ParseQuery("Team");
$teamQuery->greaterThan("winPct", 0.5);
$userQuery = ParseUser::query();
$userQuery->matchesKeyInQuery("hometown", "city", $teamQuery);
$results = $userQuery->find();
// results has the array of users with a hometown team with a winning record

Conversely, to get objects where a key does not match the value of a key in a set of objects resulting from another query, use doesNotMatchKeyInQuery. For example, to find users whose hometown teams have losing records:

$losingUserQuery = ParseUser::query();
$losingUserQuery->doesNotMatchKeyInQuery("hometown", "city", teamQuery);
$results = $losingUserQuery->find();
// results has the array of users with a hometown team with a losing record

You can restrict the fields returned by calling select with an array of keys. To retrieve documents that contain only the score and playerName fields (and also special built-in fields such as objectId, createdAt, and updatedAt):

$query = new ParseQuery("GameScore");
$query->select("score", "playerName");
$results = $query->find();
// each of results will only have the selected fields available.

The remaining fields can be fetched later by calling fetch on the returned objects:

$result = $query->first();
// only the selected fields of the object will now be available here.
$result->fetch();
// all fields of the object will now be available here.

Queries on Array Values

For keys with an array type, you can find objects where the key's array value contains 2 by:

// Find objects where the array in arrayKey contains 2.
$query->equalTo("arrayKey", 2);

You can also find objects where the key's array value contains each of the elements 2, 3, and 4 with the following:

// Find objects where the array in arrayKey contains all of the elements 2, 3, and 4.
$query->containsAll("arrayKey", [2, 3, 4]);

Queries on String Values

Use startsWith to restrict to string values that start with a particular string. Similar to a MySQL LIKE operator, this is indexed so it is efficient for large datasets:

// Finds barbecue sauces that start with "Big Daddy's".
$query = new ParseQuery("BarbecueSauce");
$query->startsWith("name", "Big Daddy's");
If you're trying to implement a generic search feature, we recommend taking a look at this blog post: Implementing Scalable Search on a NoSQL Backend.

Relational Queries

There are several ways to issue queries for relational data. If you want to retrieve objects where a field matches a particular ParseObject, you can use equalTo just like for other data types. For example, if each Comment has a Post object in its post field, you can fetch comments for a particular Post:

// Assume ParseObject $myPost was previously created.
$query = new ParseQuery("Comment");
$query->equalTo("post", $myPost);
$comments = $query->find();
// comments now contains the comments for myPost

If you want to retrieve objects where a field contains a ParseObject that matches a different query, you can use matchesQuery. Note that the default limit of 100 and maximum limit of 1000 apply to the inner query as well, so with large data sets you may need to construct queries carefully to get the desired behavior. In order to find comments for posts containing images, you can do:

$innerQuery = new ParseQuery("Post");
$innerQuery->exists("image");
$query = new ParseQuery("Comment");
$query->matchesQuery("post", $innerQuery);
$comments = $query->find();
// comments now contains the comments for posts with images.

If you want to retrieve objects where a field contains a ParseObject that does not match a different query, you can use doesNotMatchQuery. In order to find comments for posts without images, you can do:

$innerQuery = new ParseQuery("Post");
$innerQuery->exists("image");
$query = new ParseQuery("Comment");
$query->doesNotMatchQuery("post", $innerQuery);
$query->find();
// comments now contains the comments for posts without images.

You can also do relational queries by objectId:

$post = new ParseObject("Post", "1zEcyElZ80");
$query->equalTo("post", $post);

In some situations, you want to return multiple types of related objects in one query. You can do this with the include method. For example, let's say you are retrieving the last ten comments, and you want to retrieve their related posts at the same time:

$query = new ParseQuery("Comment");

// Retrieve the most recent ones
query->descending("createdAt");

// Only retrieve the last ten
query->limit(10);

// Include the post data with each comment
query->includeKey("post");

$comments = query->find();
// Comments now contains the last ten comments, and the "post" field
// has been populated. For example:
for ($i = 0; $i < count($comments); $i++) {
  // This does not require a network access.
  $post = $comments[$i]->get("post");
}

You can also do multi level includes using dot notation. If you wanted to include the post for a comment and the post's author as well you can do:

$query->includeKey("post.author");

You can issue a query with multiple fields included by calling includeKey multiple times. This functionality also works with ParseQuery helpers like first and get.

Counting Objects

Caveat: Count queries are rate limited to a maximum of 160 requests per minute. They can also return inaccurate results for classes with more than 1,000 objects. Thus, it is preferable to architect your application to avoid this sort of count operation (by using counters, for example.)

If you just need to count how many objects match a query, but you do not need to retrieve all the objects that match, you can use count instead of find. For example, to count how many games have been played by a particular player:

$query = new ParseQuery("GameScore");
$query->equalTo("playerName", "Sean Plott");
$count = $query->count();
// The count request succeeded. Show the count
echo "Sean has played " . $count . " games";

Compound Queries

If you want to find objects that match one of several queries, you can use ParseQuery.or method to construct a query that is an OR of the queries passed in. For instance if you want to find players who either have a lot of wins or a few wins, you can do:

$lotsOfWins = new ParseQuery("Player");
$lotsOfWins->greaterThan("wins", 150);

$fewWins = new ParseQuery("Player");
$fewWins->lessThan("wins", 5);

$mainQuery = ParseQuery::orQueries([$lotsOfWins, $fewWins]);
$results = $mainQuery->find();
// results contains an array of players that either have won a lot of games or won only a few games.

You can add additional constraints to the newly created ParseQuery that act as an 'and' operator.

Note that we do not, however, support GeoPoint or non-filtering constraints (e.g. near, withinGeoBox, limit, skip, ascending/descending, includeKey) in the subqueries of the compound query.

Each ParseObject is an instance of a specific subclass with a class name that you can use to distinguish different sorts of data. For example, we could call the high score object a GameScore. We recommend that you NameYourClassesLikeThis and nameYourKeysLikeThis, just to keep your code looking pretty.

To create a new subclass, create a new class which extends the ParseObject class, add the $parseClassName static property, and call the registerSubclass method before use. Any ParseQuery will return instances of the new class for any ParseObject with the same class name.

class GameScore extends ParseObject
{
  public static $parseClassName = "GameScore";
}
// Do this once, at the start of your app, before ParseClient::initialize(...);
GameScore::registerSubclass();

// Create a new instance of that class.
$gameScore = new GameScore();

You can add additional methods and properties to your subclasses of ParseObject.

// A complex subclass of ParseObject
class Monster extends ParseObject
{
  public static $parseClassName = "Monster";

  public function hasSuperHumanStrength() {
    return this->get("strength") > 18;
  }

  public static function spawn($strength) {
    $monster = new Monster();
    $monster->set("strength", $strength);
    return $monster;
  }
}
$monster = Monster::spawn(200);
echo monster->strength();  // Displays 200.
echo monster->hasSuperHumanStrength();  // Displays true.

Files

Creating a ParseFile

ParseFile lets you store application files in the cloud that would otherwise be too large or cumbersome to fit into a regular ParseObject. The most common use case is storing images, but you can also use it for documents, videos, music, and any other binary data (up to 10 megabytes).

Getting started with ParseFile is easy. There are a couple of ways to create a file. The first is to provide the contents of the file.

$contents = "Hello World.";
$file = ParseFile::createFromData($contents, "myfile.txt");

Alternatively, you can create a file from the contents of a local file:

$localFilePath = "/tmp/myFile.txt";
$file = ParseFile::createFromFile($localFilePath, "myfile.txt");

Notice in this example that we give the file a name of myfile.txt. There's two things to note here:

  • You don't need to worry about filename collisions. Each upload gets a unique identifier so there's no problem with uploading multiple files named photo.jpg.
  • It's important that you give a name to the file that has a file extension. This lets Parse figure out the file type and handle it accordingly. So, if you're storing PNG images, make sure your filename ends with .png.

Next you'll want to save the file up to the cloud. As with ParseObject, the save method is the way to go.

$file->save();
// The file has been saved to Parse and now has a URL.
$url = $file->getURL();

Finally, after the save completes, you can associate a ParseFile with a ParseObject just like any other piece of data:

$jobApplication = new ParseObject("JobApplication");
$jobApplication->set("applicantName", "Joe Smith");
$jobApplication->set("applicantResumeFile", $file);
$jobApplication->save();

Retrieving File Contents

How to best retrieve the file contents back depends on the context of your application. It's best if you can make the visitors browser do the work for you. Typically, that means rendering the file's URL into your output. Here we output an uploaded profile photo in an image tag:

$profilePhoto = $profile->get("photoFile");
echo '<img src="' . $profilePhoto->getURL() . '">';

If you want to fetch the contents of the file, you can retrieve it like this:

$contents = $file->getData();

Analytics

Parse provides a number of hooks for you to get a glimpse into the ticking heart of your app. We understand that it's important to understand what your app is doing, how frequently, and when.

While this section will cover different ways to instrument your app to best take advantage of Parse's analytics backend, developers using Parse to store and retrieve data can already take advantage of metrics on Parse.

Custom Analytics

ParseAnalytics also allows you to track free-form events, with a handful of string keys and values. These extra dimensions allow segmentation of your custom events via your app's Dashboard.

Say your app offers search functionality for apartment listings, and you want to track how often the feature is used, with some additional metadata.

// Define ranges to bucket data points into meaningful segments
$dimensions = [
  "priceRange" => '1000-1500', 
  "source" => 'craigslist', 
  "dayType": 'weekday'
];
// Send the dimensions to Parse along with the 'search' event
ParseAnalytics::track('search', $dimensions);

ParseAnalytics can even be used as a lightweight error tracker — simply invoke the following and you'll have access to an overview of the rate and frequency of errors, broken down by error code, in your application:

$codeString = '' + $error->getCode();
ParseAnalytics::track('error', ["code" => codeString]);

Note that Parse currently only stores the first eight dimension pairs per call to ParseAnalytics::track().

Config

Parse Config

ParseConfig is a way to configure your applications remotely by storing a single configuration object on Parse. It enables you to add things like feature gating or a simple "Message of the Day". To start using ParseConfig you need to add a few key/value pairs (parameters) to your app on the Parse Config Dashboard.

After that you will be able to fetch the ParseConfig on the client, like in this example:

$config = new ParseConfig(); // Makes a network request for the current config data.
$winningNumber = $config->get("winningNumber");
$message = "Yay! The number is " . $winningNumber . "!";
echo $message;

You can cache the ParseConfig object, for example in a $_SESSION variable, to avoid requesting it every time the page is accessed:

$config = $_SESSION['config'];
if (!$config) {
    $config = new ParseConfig();
    $_SESSION['config'] = $config;
}

Parameters

ParseConfig supports most of the data types supported by ParseObject:

  • string
  • number
  • Date
  • ParseFile
  • ParseGeoPoint
  • Array
  • Object

We currently allow up to 100 parameters in your config and a total size of 128KB across all parameters.

Users

At the core of many apps, there is a notion of user accounts that lets users access their information in a secure manner. We provide a specialized user class called ParseUser that automatically handles much of the functionality required for user account management.

With this class, you'll be able to add user account functionality in your app.

ParseUser is a subclass of ParseObject, and has all the same features, such as flexible schema, automatic persistence, and a key value interface. All the methods that are on ParseObject also exist in ParseUser. The difference is that ParseUser has some special additions specific to user accounts.

Properties

ParseUser has several values that set it apart from ParseObject:

  • username: The username for the user (required).
  • password: The password for the user (required on signup).
  • email: The email address for the user (optional).

We'll go through each of these in detail as we run through the various use cases for users.

Signing Up

The first thing your app will do is probably ask the user to sign up. The following code illustrates a typical sign up:

$user = new ParseUser();
$user->set("username", "my name");
$user->set("password", "my pass");
$user->set("email", "email@example.com");

// other fields can be set just like with ParseObject
$user->set("phone", "415-392-0202");

try {
  $user->signUp();
  // Hooray! Let them use the app now.
} catch (ParseException $ex) {
  // Show the error message somewhere and let the user try again.
  echo "Error: " . $ex->getCode() . " " . $ex->getMessage();
}

This call will asynchronously create a new user in your Parse App. Before it does this, it also checks to make sure that both the username and email are unique. Also, it securely hashes the password in the cloud. We never store passwords in plaintext, nor will we ever transmit passwords back to the client in plaintext.

Note that we used the signUp method, not the save method. New ParseUsers should always be created using the signUp method. Subsequent updates to a user can be done by calling save.

If a signup isn't successful, you should read the error object that is returned. The most likely case is that the username or email has already been taken by another user. You should clearly communicate this to your users, and ask them try a different username.

You are free to use an email address as the username. Simply ask your users to enter their email, but fill it in the username property — ParseUser will work as normal. We'll go over how this is handled in the reset password section.

Logging In

Of course, after you allow users to sign up, you need to let them log in to their account in the future. To do this, you can use the class method logIn.

try {
  $user = ParseUser::logIn("myname", "mypass");
  // Do stuff after successful login.
} catch (ParseException $error) {
  // The login failed. Check error to see why.
}

Verifying Emails

Enabling email verification in an application's settings allows the application to reserve part of its experience for users with confirmed email addresses. Email verification adds the emailVerified key to the ParseUser object. When a ParseUser's email is set or modified, emailVerified is set to false. Parse then emails the user a link which will set emailVerified to true.

There are three emailVerified states to consider:

  1. true - the user confirmed his or her email address by clicking on the link Parse emailed them. ParseUsers can never have a true value when the user account is first created.
  2. false - at the time the ParseUser object was last refreshed, the user had not confirmed his or her email address. If emailVerified is false, consider calling fetch on the ParseUser.
  3. missing - the ParseUser was created when email verification was off or the ParseUser does not have an email.

Current User

It would be bothersome if the user had to log in every time they open your app. You can avoid this by using the cached current ParseUser object.

By default, whenever you use any signup or login methods, the user will be saved in PHP Session storage (The $_SESSION superglobal.)

$currentUser = ParseUser::getCurrentUser();
if ($currentUser) {
    // do stuff with the user
} else {
    // show the signup or login page
}

You can clear the current user by logging them out:

ParseUser::logOut();

$currentUser = ParseUser::getCurrentUser();  // this will now be null

Setting the Current User

If you’ve created your own authentication routines, or otherwise logged in a user on the server side, you can now pass the session token to the client and use the become method. This method will ensure the session token is valid before setting the current user.

try {
  $user = ParseUser::become("session-token-here");
  // The current user is now set to user.
} catch (ParseException $ex) {
  // The token could not be validated.
}

Session Storage Interface

If you do not want to use the default PHP $_SESSION for storage, you can add your own storage mechanism. We provide the ParseSessionStorageInterface and a default implementation, ParseSessionStorage. Simply create your own storage class that implements ParseSessionStorageInterface and inject it to the SDK when you initialize:

ParseClient::setStorage(new MyStorageClass());

Security For User Objects

The ParseUser class is secured by default. Data stored in a ParseUser can only be modified by that user. By default, the data can still be read by any client. Thus, some ParseUser objects are authenticated and can be modified, whereas others are read-only.

Specifically, you are not able to invoke any of the save or delete methods unless the ParseUser was obtained using an authenticated method, like logIn or signUp. This ensures that only the user can alter their own data.

The following illustrates this security policy:

$user = ParseUser::logIn("my_username", "my_password");
$user->set("username", "my_new_username");  // attempt to change username
$user->save();
// This succeeds, since the user was authenticated on the device

// Get the user from a non-authenticated method
$query = ParseUser::query();
$userAgain = $query->get($user->getObjectId());
$userAgain->set("username", "another_username");
// This will throw a ParseException, since the ParseUser is not authenticated
$userAgain->save();

The ParseUser obtained from ParseUser::getCurrentUser() will always be authenticated.

If you need to check if a ParseUser is authenticated, you can invoke the authenticated method. You do not need to check authenticated with ParseUser objects that are obtained via an authenticated method.

Security For Other Objects

The same security model that applies to the ParseUser can be applied to other objects. For any object, you can specify which users are allowed to read the object, and which users are allowed to modify an object. To support this type of security, each object has an access control list, implemented by the ParseACL class.

The simplest way to use a ParseACL is to specify that an object may only be read or written by a single user. To create such an object, there must first be a logged in ParseUser. Then, new ParseACL::createACLWithUser($user) generates a ParseACL that limits access to that user. An object's ACL is updated when the object is saved, like any other property. Thus, to create a private note that can only be accessed by the current user:

$privateNote = new ParseObject("Note");
$privateNote->set("content", "This note is private!");
$privateNote->setACL(ParseACL::createACLWithUser(ParseUser::getCurrentUser()));
$privateNote->save();

This note will then only be accessible to the current user, although it will be accessible to any device where that user is signed in. This functionality is useful for applications where you want to enable access to user data across multiple devices, like a personal todo list.

Permissions can also be granted on a per-user basis. You can add permissions individually to a ParseACL using setReadAccess and setWriteAccess. For example, let's say you have a message that will be sent to a group of several users, where each of them have the rights to read and delete that message:

$groupMessage = new ParseObject("Message");
$groupACL = new ParseACL();

// userList is an array with the users we are sending this message to.
for ($i = 0; $i < count($userList); $i++) {
  $groupACL->setReadAccess($userList[$i], true);
  $groupACL->setWriteAccess($userList[$i], true);
}

$groupMessage->setACL($groupACL);
$groupMessage->save();

You can also grant permissions to all users at once using setPublicReadAccess and setPublicWriteAccess. This allows patterns like posting comments on a message board. For example, to create a post that can only be edited by its author, but can be read by anyone:

$publicPost = new ParseObject("Post");
$postACL = ParseACL::createACLWithUser(ParseUser::getCurrentUser());
$postACL->setPublicReadAccess(true);
$publicPost->setACL($postACL);
$publicPost->save();

Operations that are forbidden, such as deleting an object that you do not have write access to, result in a ParseError.OBJECT_NOT_FOUND error code. For security purposes, this prevents clients from distinguishing which object ids exist but are secured, versus which object ids do not exist at all.

Resetting Passwords

It's a fact that as soon as you introduce passwords into a system, users will forget them. In such cases, our library provides a way to let them securely reset their password.

To kick off the password reset flow, ask the user for their email address, and call:

try {
  ParseUser::requestPasswordReset("email@example.com");
	// Password reset request was sent successfully
} catch (ParseException $ex) {
  // Password reset failed, check the exception message
}

This will attempt to match the given email with the user's email or username field, and will send them a password reset email. By doing this, you can opt to have users use their email as their username, or you can collect it separately and store it in the email field.

The flow for password reset is as follows:

  1. User requests that their password be reset by typing in their email.
  2. Parse sends an email to their address, with a special password reset link.
  3. User clicks on the reset link, and is directed to a special Parse page that will allow them type in a new password.
  4. User types in a new password. Their password has now been reset to a value they specify.

Note that the messaging in this flow will reference your app by the name that you specified when you created this app on Parse.

Querying

To query for users, you can get a new ParseQuery for ParseUsers:

$query = ParseUser::query();
$query->equalTo("gender", "female"); 
$results = $query->find();

Associations

Associations involving a ParseUser work right out of the box. For example, let's say you're making a blogging app. To store a new post for a user and retrieve all of their posts:

$user = ParseUser::getCurrentUser()

// Make a new post
$post = new ParseObject("Post");
$post->set("title", "My New Post");
$post->set("body", "This is some great content.");
$post->set("user", $user);
$post->save();

// Find all posts by the current user
$query = new ParseQuery("Post");
$query->equalTo("user", $user);
$userPosts = $query->find();
// $userPosts contains all of the posts by the current user.

Users in the Data Browser

The User class is a special class that is dedicated to storing ParseUser objects. In the data browser, you'll see a little person icon next to the User class:

User_icon

Roles

As your app grows in scope and user-base, you may find yourself needing more coarse-grained control over access to pieces of your data than user-linked ACLs can provide. To address this requirement, Parse supports a form of Role-based Access Control. Roles provide a logical way of grouping users with common access privileges to your Parse data. Roles are named objects that contain users and other roles. Any permission granted to a role is implicitly granted to its users as well as to the users of any roles that it contains.

For example, in your application with curated content, you may have a number of users that are considered "Moderators" and can modify and delete content created by other users. You may also have a set of users that are "Administrators" and are allowed all of the same privileges as Moderators, but can also modify the global settings for the application. By adding users to these roles, you can ensure that new users can be made moderators or administrators, without having to manually grant permission to every resource for each user.

We provide a specialized class called ParseRole that represents these role objects in your client code. ParseRole is a subclass of ParseObject, and has all of the same features, such as a flexible schema, automatic persistence, and a key value interface. All the methods that are on ParseObject also exist on ParseRole. The difference is that ParseRole has some additions specific to management of roles.

Properties

ParseRole has several properties that set it apart from ParseObject:

  • name: The name for the role. This value is required, and can only be set once as a role is being created. The name must consist of alphanumeric characters, spaces, -, or _. This name will be used to identify the Role without needing its objectId.
  • users: A relation to the set of users that will inherit permissions granted to the containing role.
  • roles: A relation to the set of roles whose users and roles will inherit permissions granted to the containing role.

Security for Role Objects

The ParseRole uses the same security scheme (ACLs) as all other objects on Parse, except that it requires an ACL to be set explicitly. Generally, only users with greatly elevated privileges (e.g. a master user or Administrator) should be able to create or modify a Role, so you should define its ACLs accordingly. Remember, if you give write-access to a ParseRole to a user, that user can add other users to the role, or even delete the role altogether.

To create a new ParseRole, you would write:

// By specifying no write privileges for the ACL, we can ensure the role cannot be altered.
$roleACL = new ParseACL();
$roleACL->setPublicReadAccess(true);
$role = ParseRole::createRole("Administrator", $roleACL);
$role->save();

You can add users and roles that should inherit your new role's permissions through the "users" and "roles" relations on ParseRole:

$role = ParseRole::createRole($roleName, $roleACL);
for ($i = 0; $i < count($usersToAddToRole); $i++) {
  $role->getUsers()->add($usersToAddToRole[$i]);
}
for ($i = 0; $i < count($rolesToAddToRole); $i++) {
  $role->getRoles()->add($rolesToAddToRole[$i]);
}
$role->save();

Take great care when assigning ACLs to your roles so that they can only be modified by those who should have permissions to modify them.

Security for Other Objects

Now that you have created a set of roles for use in your application, you can use them with ACLs to define the privileges that their users will receive. Each ParseObject can specify a ParseACL, which provides an access control list that indicates which users and roles should be granted read or write access to the object.

Giving a role read or write permission to an object is straightforward. You can either use the ParseRole:

$moderators = /* Query for some ParseRole */;
$wallPost = new ParseObject("WallPost");
$postACL = new ParseACL();
$postACL->setRoleWriteAccess($moderators, true);
$wallPost->setACL($postACL);
$wallPost->save();

You can avoid querying for a role by specifying its name for the ACL:

$wallPost = new ParseObject("WallPost");
$postACL = new ParseACL();
$postACL->setRoleWriteAccess("Moderators", true);
$wallPost->setACL($postACL);
$wallPost->save();

Role Hierarchy

As described above, one role can contain another, establishing a parent-child relationship between the two roles. The consequence of this relationship is that any permission granted to the parent role is implicitly granted to all of its child roles.

These types of relationships are commonly found in applications with user-managed content, such as forums. Some small subset of users are "Administrators", with the highest level of access to tweaking the application's settings, creating new forums, setting global messages, and so on. Another set of users are "Moderators", who are responsible for ensuring that the content created by users remains appropriate. Any user with Administrator privileges should also be granted the permissions of any Moderator. To establish this relationship, you would make your "Administrators" role a child role of "Moderators", like this:

$administrators = /* Your "Administrators" role */;
$moderators = /* Your "Moderators" role */;
$moderators->getRoles()->add($administrators);
$moderators->save();

Cloud Functions

Cloud Functions can be called from PHP using ParseCloud.run. For example, to call the Cloud Function named hello:

$result = ParseCloud::run('hello', []);
// result is 'Hello world!'
Take a look at the Cloud Code Guide to learn more about Cloud Functions.

GeoPoints

Parse allows you to associate real-world latitude and longitude coordinates with an object. Adding a ParseGeoPoint to a ParseObject allows queries to take into account the proximity of an object to a reference point. This allows you to easily do things like find out what user is closest to another user or which places are closest to a user.

ParseGeoPoint

To associate a point with an object you first need to create a ParseGeoPoint. For example, to create a point with latitude of 40.0 degrees and -30.0 degrees longitude:

$point = new ParseGeoPoint(40.0, -30.0);

This point is then stored in the object as a regular field.

$placeObject->set("location", $point);

Note: Currently only one key in a class may be a ParseGeoPoint.

Geo Queries

Now that you have a bunch of objects with spatial coordinates, it would be nice to find out which objects are closest to a point. This can be done by adding another restriction to ParseQuery using near. Getting an array of ten places that are closest to a user may look something like:

// User's location
$userGeoPoint = $userObject->get("location");
// Create a query for places
$query = new ParseQuery("PlaceObject");
// Interested in locations near user.
$query->near("location", $userGeoPoint);
// Limit what could be a lot of points.
$query->limit(10);
// Final array of objects
$placesObjects = $query->find();

At this point $placesObjects will be an array of objects ordered by distance (nearest to farthest) from $userGeoPoint. Note that if an additional ascending()/descending() order-by constraint is applied, it will take precedence over the distance ordering.

To limit the results using distance, check out withinMiles, withinKilometers, and withinRadians.

It's also possible to query for the set of objects that are contained within a particular area. To find the objects in a rectangular bounding box, add the withinGeoBox restriction to your ParseQuery.

$southwestOfSF = new ParseGeoPoint(37.708813, -122.526398);
$northeastOfSF = new ParseGeoPoint(37.822802, -122.373962);

$query = new ParseQuery("PizzaPlaceObject");
$query->withinGeoBox("location", $southwestOfSF, $northeastOfSF);
$pizzaPlacesInSF = $query->find();

Caveats

At the moment there are a couple of things to watch out for:

  1. Each ParseObject class may only have one key with a ParseGeoPoint object.
  2. Using the near constraint will also limit results to within 100 miles.
  3. Points should not equal or exceed the extreme ends of the ranges. Latitude should not be -90.0 or 90.0. Longitude should not be -180.0 or 180.0. Attempting to set latitude or longitude out of bounds will cause an error.

Push Notifications

To learn more about push check out our Push Notification Guide!

Error Handling

The Parse PHP SDK throws ParseExceptions when errors are returned from the Parse API. For other errors, the base Exception class will be thrown. It is recommended to wrap your Parse calls in try/catch blocks to handle any errors which occur.

$query = new ParseQuery("Note");
try {
  // This will throw a ParseException, the object is not found.
  $result = $query->get("aBcDeFgH")
} catch (ParseException $error) {
  // $error is an instance of ParseException with details about the error.
  echo $error->getCode();
  echo $error->getMessage();
}