PHP SDK

Here’s a tiny, drop-in PHP “SDK” you can include in a theme or plugin to consume the Artemis Herbal Remedies Database. It:

  • Wraps the REST calls (works inside or outside WordPress)
  • Handles query params & taxonomy filters (arrays or strings)
  • Supports pagination helpers
  • Adds optional caching (uses WP transients when available)
  • Returns decoded arrays with headers when useful

1) Client Class (single file)

Create class-artemis-herbs-client.php with:

<?php
/**
 * Minimal client for the Artemis Herbal Remedies Database (WordPress REST API)
 * Base URL: https://herbs.artemis-temple.com/wp-json
 */
class Artemis_Herbs_Client {
    protected $base;
    protected $timeout;
    protected $user_agent;
    protected $cache_ttl; // seconds

    public function __construct(array $opts = []) {
        $this->base       = rtrim($opts['base'] ?? 'https://herbs.artemis-temple.com/wp-json', '/');
        $this->timeout    = (int)($opts['timeout'] ?? 12);
        $this->user_agent = $opts['user_agent'] ?? 'ArtemisHerbsClient/1.0';
        $this->cache_ttl  = isset($opts['cache_ttl']) ? (int)$opts['cache_ttl'] : 600; // 10 min
    }

    /** ---------- Public endpoints ---------- */

    /** GET /wp/v2/artm_monograph (list) */
    public function list_monographs(array $args = [], bool $with_headers = false) {
        $path = '/wp/v2/artm_monograph';
        $args = $this->normalize_args($args);
        return $this->get($path, $args, $with_headers);
    }

    /** Generator over all pages */
    public function iterate_monographs(array $args = []) : \Generator {
        $page = 1;
        $args = $this->normalize_args($args);
        $args['per_page'] = $args['per_page'] ?? 100;
        do {
            $args['page'] = $page;
            [$items, $headers] = $this->get('/wp/v2/artm_monograph', $args, true);
            foreach ($items as $row) { yield $row; }
            $total_pages = isset($headers['x-wp-totalpages'][0]) ? (int)$headers['x-wp-totalpages'][0] : 0;
            $page++;
        } while ($total_pages && $page <= $total_pages);
    }

    /** GET /wp/v2/artm_monograph/{id} */
    public function get_monograph($id, array $args = []) {
        $path = '/wp/v2/artm_monograph/' . rawurlencode($id);
        $args = $this->normalize_args($args);
        return $this->get($path, $args);
    }

    /** GET /wp/v2/{taxonomy} (list terms) */
    public function list_terms(string $taxonomy, array $args = [], bool $with_headers = false) {
        $path = '/wp/v2/' . $taxonomy;
        $args = $this->normalize_args($args);
        return $this->get($path, $args, $with_headers);
    }

    /** GET /wp/v2/{taxonomy}/{id} */
    public function get_term(string $taxonomy, $id, array $args = []) {
        $path = '/wp/v2/' . $taxonomy . '/' . rawurlencode($id);
        $args = $this->normalize_args($args);
        return $this->get($path, $args);
    }

    /** Convenience: search monographs by keyword */
    public function search_monographs(string $q, array $args = []) {
        $args['search'] = $q;
        return $this->list_monographs($args);
    }

    /** ---------- Internals ---------- */

    /** Normalize filters: allow array values for tax filters and _fields */
    protected function normalize_args(array $args) : array {
        // Support arrays for taxonomy filters and _fields; http_build_query handles [].
        // Add safe defaults:
        if (!isset($args['_fields'])) {
            // small default response
            $args['_fields'] = ['id','slug','link','title','meta'];
        }
        // Many consumers want embed sometimes
        if (!empty($args['_embed']) && $args['_embed'] !== 'false') {
            $args['_embed'] = '1';
        } else {
            unset($args['_embed']);
        }
        return $args;
    }

    /** GET helper with optional caching; returns [body, headers] when $with_headers */
    protected function get(string $path, array $params = [], bool $with_headers = false) {
        $url = $this->base . $path;
        if (!empty($params)) {
            $url .= (strpos($url, '?') === false ? '?' : '&') . http_build_query($params);
        }

        // Cache key
        $ckey = 'ahc_' . md5($url);
        if ($this->cache_ttl > 0 && function_exists('get_transient')) {
            $cached = get_transient($ckey);
            if ($cached) {
                return $with_headers ? [$cached['body'], $cached['headers']] : $cached['body'];
            }
        }

        // Prefer WP HTTP API if available
        if (function_exists('wp_remote_get')) {
            $res = wp_remote_get($url, [
                'timeout' => $this->timeout,
                'user-agent' => $this->user_agent,
            ]);
            if (is_wp_error($res)) {
                throw new \RuntimeException('HTTP error: ' . $res->get_error_message());
            }
            $code = (int) wp_remote_retrieve_response_code($res);
            $raw  = wp_remote_retrieve_body($res);
            $hdrs = wp_remote_retrieve_headers($res)->getAll();
        } else {
            // Fallback to cURL
            $ch = curl_init($url);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT        => $this->timeout,
                CURLOPT_USERAGENT      => $this->user_agent,
                CURLOPT_HEADER         => true,
            ]);
            $resp = curl_exec($ch);
            if ($resp === false) {
                throw new \RuntimeException('cURL error: ' . curl_error($ch));
            }
            $code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
            $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
            $headers_raw = substr($resp, 0, $header_size);
            $raw         = substr($resp, $header_size);
            curl_close($ch);
            $hdrs = $this->parse_headers($headers_raw);
        }

        if ($code < 200 || $code >= 300) {
            throw new \RuntimeException("HTTP $code: $raw");
        }

        $data = json_decode($raw, true);
        if (!is_array($data) && !is_null($data)) {
            throw new \RuntimeException('Invalid JSON response');
        }

        if ($this->cache_ttl > 0 && function_exists('set_transient')) {
            set_transient($ckey, ['body' => $data, 'headers' => $hdrs], $this->cache_ttl);
        }

        return $with_headers ? [$data, $hdrs] : $data;
    }

    protected function parse_headers(string $raw) : array {
        $headers = [];
        foreach (explode("\r\n", $raw) as $line) {
            if (strpos($line, ':') !== false) {
                [$k, $v] = explode(':', $line, 2);
                $k = strtolower(trim($k));
                $v = trim($v);
                $headers[$k][] = $v;
            }
        }
        return $headers;
    }
}

2) Usage Examples

A) In a theme template (list monographs)

require_once get_stylesheet_directory() . '/inc/class-artemis-herbs-client.php';

$api = new Artemis_Herbs_Client([
    'cache_ttl' => 900, // 15 minutes
]);

$rows = $api->list_monographs([
    'per_page' => 20,
    '_fields'  => ['id','slug','title','link'],
    'orderby'  => 'title',
    'order'    => 'asc',
]);

echo '<ul class="herb-list">';
foreach ($rows as $r) {
    printf('<li><a href="%s">%s</a></li>',
        esc_url($r['link']),
        esc_html($r['title']['rendered'] ?? $r['slug'])
    );
}
echo '</ul>';

B) Filter by multiple taxonomy terms (arrays supported)

$rows = $api->list_monographs([
    'artm_system' => 'nervous',
    'artm_action' => ['nervine','anxiolytic','adaptogen'],
    '_fields'     => ['id','slug','title','link'],
]);

C) Iterate all pages (generator)

echo '<ul>';
foreach ($api->iterate_monographs([
    '_fields' => ['id','slug','title','link'],
    'orderby' => 'title',
    'order'   => 'asc',
]) as $row) {
    echo '<li>' . esc_html($row['title']['rendered']) . '</li>';
}
echo '</ul>';

D) Get one monograph with embedded terms

$item = $api->get_monograph(1234, ['_embed' => 1]);
// Access taxonomy terms under $item['_embedded']['wp:term']

E) List terms of a taxonomy (e.g., artm_action)

$terms = $api->list_terms('artm_action', [
    'per_page' => 100,
    'orderby'  => 'name',
    'order'    => 'asc',
]);
// $terms[] => [ id, name, slug, count, ... ]

3) Quick Shortcode (optional)

Drop this into a plugin or theme to expose a simple list via shortcode:

add_shortcode('artemis_herbs', function($atts){
    $a = shortcode_atts([
        'system' => '',        // e.g. digestive
        'action' => '',        // e.g. carminative (comma-separated allowed)
        'limit'  => 20,
    ], $atts);

    require_once __DIR__ . '/class-artemis-herbs-client.php';
    $api = new Artemis_Herbs_Client(['cache_ttl' => 600]);

    $args = [
        'per_page' => (int)$a['limit'],
        '_fields'  => ['id','slug','title','link'],
        'orderby'  => 'title',
        'order'    => 'asc',
    ];
    if ($a['system']) $args['artm_system'] = trim($a['system']);
    if ($a['action']) $args['artm_action'] = array_map('trim', explode(',', $a['action']));

    try {
        $rows = $api->list_monographs($args);
    } catch (\Throwable $e) {
        return '<p>Error: ' . esc_html($e->getMessage()) . '</p>';
    }

    if (!$rows) return '<p>No results.</p>';

    $out = '<ul class="artemis-herbs">';
    foreach ($rows as $r) {
        $out .= sprintf('<li><a href="%s">%s</a></li>',
            esc_url($r['link']),
            esc_html($r['title']['rendered'] ?? $r['slug'])
        );
    }
    $out .= '</ul>';
    return $out;
});

Usage:

[artemis_herbs system="digestive" action="carminative,nervine" limit="30"]

4) Notes & Tips

  • Taxonomy filters: Use either slugs (artm_action=carminative) or arrays (artm_action[]=carminative&artm_action[]=nervine). The client builds both via http_build_query.
  • Fields: Use _fields to keep payloads small. For full monographs, omit _fields or pass what you need.
  • Embedding: Add ['_embed' => 1] when you need related resources (terms/media) inline.
  • Caching: Inside WordPress, the client uses transients. Adjust cache_ttl or set to 0 to disable.