Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
/demo export-ignore
/tests export-ignore
# Leave development files out of the package that Composer downloads
/.github export-ignore
/demo export-ignore
/tests export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/phpunit.xml.dist export-ignore
/openapi-bundled.yaml export-ignore
/.gitignore export-ignore
/CHANGELOG.md export-ignore
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
name: PHP ${{ matrix.php }}${{ matrix.dependencies == 'lowest' && ' (lowest dependencies)' || '' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['8.1', '8.2', '8.3', '8.4']
dependencies: [highest]
include:
- php: '8.1'
dependencies: lowest
steps:
- uses: actions/checkout@v5
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
- run: composer validate --strict
- name: Install dependencies
run: composer update --no-interaction --no-progress ${{ matrix.dependencies == 'lowest' && '--prefer-lowest' || '' }}
- run: composer test
27 changes: 27 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Release

# Runs when you push a version tag such as v1.1.0. Packagist picks up the tag by itself;
# this workflow only creates the GitHub release, with the notes from CHANGELOG.md.

on:
push:
tags: ['v*']

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Create the GitHub release from CHANGELOG.md
env:
GH_TOKEN: ${{ github.token }}
run: |
notes=$(awk -v heading="## ${GITHUB_REF_NAME#v}" '$0 == heading { found = 1; next } /^## / { found = 0 } found' CHANGELOG.md)
if [ -z "$notes" ]; then
echo "::error::CHANGELOG.md has no section '## ${GITHUB_REF_NAME#v}'"
exit 1
fi
gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --notes "$notes"
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Changelog

All notable changes to `algebrakit/sdk-php` are listed here. The project follows [semantic versioning](https://semver.org).

## 1.1.0

### Fixed
- `lockOrUnlockSession` called `/session/lock`, which doesn't exist, so it always failed with a 404. It now calls `/sessions/lock`.
- The required PHP version is now 8.1. The SDK uses enums, which don't exist in PHP 8.0.

### Added
- `AlgebrakitApiException`, thrown when a request fails. Its message includes the API's explanation, for example `Algebrakit API error 401: Could not validate credentials: the provided api-key is not valid`, and `getStatusCode()` returns the HTTP status. It extends `RuntimeException`, so existing `catch` blocks keep working.
- A request timeout, 30 seconds by default, set with the new `timeout` constructor argument.
- `SessionService::DEFAULT_BASE_URL`.
- Unit tests and CI.

### Changed
- Requires Guzzle 7.9 or newer.
- Repository links point to `github.com/algebrakit/sdk-php`.
- The README covers how to show a session in a web page, all API methods, regions and error handling.

### Removed
- The web demo `demo/play-exercise.php`, replaced by the [minimal PHP example](https://github.com/algebrakit/algebrakit-examples/tree/main/minimal/php) in algebrakit-examples. `demo/various-functions.php` is replaced by `demo/tour.php`, which reads the API key from `ALGEBRAKIT_API_KEY`.

## 1.0.0

- First release: `createSession`, `getSessionScore`, `lockOrUnlockSession`, `getSessionInfo`, `retrieveSessions`, `validateExercise` and `getExerciseInfo`.
215 changes: 98 additions & 117 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,167 +1,148 @@
# Algebrakit PHP SDK
# Algebrakit SDK for PHP

A PHP SDK for interacting with the Algebrakit Web Service API.
[![Packagist](https://img.shields.io/packagist/v/algebrakit/sdk-php)](https://packagist.org/packages/algebrakit/sdk-php)
[![PHP](https://img.shields.io/packagist/dependency-v/algebrakit/sdk-php/php)](https://packagist.org/packages/algebrakit/sdk-php)
[![CI](https://github.com/algebrakit/sdk-php/actions/workflows/ci.yml/badge.svg)](https://github.com/algebrakit/sdk-php/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

## Installation
The official server-side SDK for the [Algebrakit](https://algebrakit.com) API. Your server uses it to create exercise sessions, get scores and manage sessions. Students then work on the exercises in your web page, through the Algebrakit component.

Install the SDK into your project with Composer:
For how Algebrakit works, the full API reference and the component's options, see the [Algebrakit documentation](https://docs.algebrakit.com/).

```bash
composer require algebrakit/sdk-php
```
> **Want to see it working first?** [algebrakit-examples](https://github.com/algebrakit/algebrakit-examples/tree/main/minimal/php) has a small web app built on this SDK that runs on localhost in a few minutes.

## Requirements
## Before you start

- PHP 8.0 or higher
- Guzzle HTTP client
- PHP 8.1 or newer, and [Composer](https://getcomposer.org).
- An Algebrakit API key. Don't have one yet? [Contact Algebrakit](https://algebrakit.com/contact-us).

## Usage
Keep the API key on your server. Never send it to the browser.

### Basic Setup
## Install

```php
<?php
```bash
composer require algebrakit/sdk-php
```

require_once 'vendor/autoload.php';
## Quick start

use Algebrakit\SDK\Services\SessionService;
Create a service and a session for an exercise:

```php
use Algebrakit\SDK\Models\Requests\CreateSessionRequest;
use Algebrakit\SDK\Models\Shared\ExerciseById;
use Algebrakit\SDK\Services\SessionService;

// Initialize the SessionService with your API key
$apiKey = 'your-api-key-here';
$sessionService = new SessionService($apiKey);
$sessionService = new SessionService(getenv('ALGEBRAKIT_API_KEY'));

$result = $sessionService->createSession(new CreateSessionRequest(
exercises: [new ExerciseById('fa42e943-8213-41a6-8a91-8c22a929ffe9')]
))[0];
$sessionId = $result->sessions[0]->sessionId;
```

### Creating a Session
A session holds one student's work on one exercise. Store its ID with the student, so they can continue later and you can get their score.

```php
// Create a session for an exercise
$createSessionRequest = new CreateSessionRequest(
exercises: [new ExerciseById('your-exercise-id', 'latest')]
);
### Show the session to a student

$createSessionResponse = $sessionService->createSession($createSessionRequest);
In your web page, the Algebrakit component shows the session:

foreach ($createSessionResponse as $response) {
if ($response->success && !empty($response->sessions)) {
$session = $response->sessions[0];
echo "Session created: " . $session->sessionId . "\n";
}
}
```html
<script>
AlgebraKIT = { config: { proxy: { url: '/proxy/algebrakit' } } };
</script>
<script src="https://widgets.algebrakit.com"></script>

<akit-exercise session-id="<?= htmlspecialchars($sessionId) ?>"></akit-exercise>
```

### Getting Session Scores
The component sends its API calls to `/proxy/algebrakit/*` on your server. Your server adds the API key and forwards them to the Algebrakit API. The [minimal PHP example](https://github.com/algebrakit/algebrakit-examples/tree/main/minimal/php) implements all of this; for production, see [Proxy setup](https://docs.algebrakit.com/pages/420-proxy-setup/).

```php
use Algebrakit\SDK\Models\Requests\SessionScoreRequest;
## API

$sessionScoreRequest = new SessionScoreRequest('your-session-id');
$sessionScoreResponse = $sessionService->getSessionScore($sessionScoreRequest);
`SessionService` has one method per API call. Each takes a request object and returns a response object.

foreach ($sessionScoreResponse->questions as $question) {
echo "Question: {$question->id}, Marks: {$question->scoring->marksEarned}/{$question->scoring->marksTotal}\n";
}
```
| Method | Endpoint | What it does |
|---|---|---|
| `createSession` | `POST /session/create` | Create sessions for one or more exercises: `ExerciseById`, `ExerciseBySpec` (an exercise defined in code) or `ExerciseBySession` (a copy of an existing session's exercise) |
| `getSessionScore` | `POST /session/score` | Get a session's score, and optionally lock it |
| `getSessionInfo` | `POST /session/info` | Get a session's details: its elements, the student's work and the scoring |
| `lockOrUnlockSession` | `POST /sessions/lock` | `LOCK`, `UNLOCK` or `FINALIZE` sessions |
| `retrieveSessions` | `POST /session/retrieve` | Get existing sessions by ID |
| `validateExercise` | `POST /exercise/validate` | Check an exercise ID or specification before you use it |
| `getExerciseInfo` | `POST /exercise/published-info` | Get the published versions and structure of an exercise |

### Validating an Exercise
### Get the score

```php
use Algebrakit\SDK\Models\Requests\ExerciseValidateRequest;

$validateRequest = new ExerciseValidateRequest(exerciseId: 'your-exercise-id');
$validateResponse = $sessionService->validateExercise($validateRequest);
use Algebrakit\SDK\Models\Requests\SessionScoreRequest;

echo "Exercise valid: " . ($validateResponse->valid ? 'Yes' : 'No') . "\n";
$scoring = $sessionService->getSessionScore(new SessionScoreRequest($sessionId, lockSession: true))->scoring;
echo "{$scoring->marksEarned} of {$scoring->marksTotal} marks\n";
```

## Running the Demos

### CLI Demo

Demonstrates SDK API calls with text output.

1. Install dependencies:
```bash
composer install
```

2. Update the API key in `demo/various-functions.php`:
```php
$apiKey = 'your-actual-api-key';
```

3. Run the demo:
```bash
php demo/various-functions.php
```
### Lock a session

### Web Demo

Renders a working Algebrakit exercise in the browser.
```php
use Algebrakit\SDK\Models\Requests\SessionLockRequest;

1. Install dependencies:
```bash
composer install
```
$sessionService->lockOrUnlockSession(new SessionLockRequest('LOCK', [$sessionId]));
```

2. Update the configuration in `demo/play-exercise.php`:
```php
$apiKey = 'your-actual-api-key';
$apiUrl = 'https://api.algebrakit.com';
$widgetUrl = 'https://widgets.algebrakit.com';
```
## Configuration

3. Start a local PHP server:
```bash
php -S localhost:8000
```
Use named arguments for the optional settings:

4. Open `http://localhost:8000/demo/play-exercise.php` in your browser.
```php
$sessionService = new SessionService(
getenv('ALGEBRAKIT_API_KEY'),
baseUrl: 'https://prod.api.sg-1.algebrakit.com', // optional, see the regions below
timeout: 10, // optional, in seconds; the default is 30
);
```

## API Reference
| Region | Base URL |
|---|---|
| EU (default) | `https://api.algebrakit.com` |
| Singapore | `https://prod.api.sg-1.algebrakit.com` |

### SessionService Methods
You can also pass your own Guzzle client as the second argument (`httpClient:`), for example to add logging. It must then send the `X-API-KEY` header itself.

- `createSession(CreateSessionRequest $request): array` - Create new exercise sessions
- `getSessionScore(SessionScoreRequest $request): SessionScoreResponse` - Get session scoring results
- `getSessionInfo(SessionInfoRequest $request): SessionInfoResponse` - Get detailed session information
- `retrieveSessions(SessionRetrieveRequest $request): SessionRetrieveResponse` - Retrieve existing sessions
- `lockOrUnlockSession(SessionLockRequest $request): SessionLockResponse` - Lock or unlock sessions
- `validateExercise(ExerciseValidateRequest $request): ExerciseValidateResponse` - Validate an exercise
- `getExerciseInfo(ExerciseInfoRequest $request): ExerciseInfoResponse` - Get exercise information
## Error handling

### Exercise Types
The methods throw an `AlgebrakitApiException` when a request fails. Its message includes the API's explanation, and `getStatusCode()` returns the HTTP status (or `null` for a network error or timeout):

You can specify exercises in three ways:
```php
use Algebrakit\SDK\Exceptions\AlgebrakitApiException;
use Algebrakit\SDK\Models\Requests\SessionInfoRequest;

1. **By Exercise ID**:
```php
new ExerciseById('exercise-id', 'latest')
```
try {
$sessionService->getSessionInfo(new SessionInfoRequest($sessionId));
} catch (AlgebrakitApiException $e) {
error_log($e->getMessage());
// Algebrakit API error 401: Could not validate credentials: the provided api-key is not valid
}
```

2. **By Exercise Specification**:
```php
new ExerciseBySpec($exerciseSpecObject)
```
`AlgebrakitApiException` extends `RuntimeException`, so existing code that catches `RuntimeException` keeps working.

3. **By Session ID** (reuse exercise from existing session):
```php
new ExerciseBySession('existing-session-id')
```
## More

## Error Handling
- [algebrakit-examples](https://github.com/algebrakit/algebrakit-examples): runnable examples in PHP, TypeScript and .NET
- [Documentation](https://docs.algebrakit.com/): [SDKs](https://docs.algebrakit.com/pages/320-SDK/), [API reference](https://docs.algebrakit.com/pages/310-API/), [web components](https://docs.algebrakit.com/pages/410-webcomponents/) and [learning events](https://docs.algebrakit.com/pages/450-learning-events/)
- [OpenAPI specification](https://api.algebrakit.com/openapi/yaml)
- SDKs for other languages: [TypeScript](https://github.com/algebrakit/sdk-typescript) and [.NET](https://github.com/algebrakit/sdk-dotnet)
- A tour of every API call: [`demo/tour.php`](demo/tour.php). Run it with `ALGEBRAKIT_API_KEY=<your API key> composer demo`.

All service methods throw `RuntimeException` if the API request fails:
## Development

```php
try {
$response = $sessionService->createSession($request);
} catch (RuntimeException $e) {
echo "Error: " . $e->getMessage() . "\n";
}
```bash
composer install
composer test # unit tests, no API key needed
```

To release: update [CHANGELOG.md](CHANGELOG.md), then push a tag such as `v1.1.0`. Packagist picks up the tag automatically, and GitHub Actions creates a GitHub release from the changelog.

## License

MIT License
[MIT](LICENSE)
Loading
Loading