Files
ninjaMail_public/ng/ninjamail.class.php
T
2026-05-13 12:43:28 +02:00

613 lines
18 KiB
PHP

<?php
declare(strict_types=1);
/**
* ninjaMail API kliens alaposztály
*
* Kezeli a HTTP kommunikációt a ninjaMail API végponttal.
*/
class ninjaMail
{
private string $host;
private string $key;
public ?object $data = null;
/** @var int cURL időtúllépés másodpercben */
private int $timeout = 15;
/**
* @param string $host Az API gazdagép alap URL-je (pl. https://example.org)
* @param string|bool $key API hitelesítési kulcs
*/
public function __construct(string $host, string|bool $key = false)
{
$this->host = rtrim($host, '/');
if ($key !== false && $key !== '') {
$this->key = (string)$key;
}
}
/**
* Ellenőrzi, hogy az API gazdagép és kulcs be van-e állítva.
*/
public function check(): bool
{
return !empty($this->host) && !empty($this->key);
}
/**
* POST kérést küld az API adott végpontjára.
*
* @param string $f A gyár/végpont neve (pl. 'subscribe')
* @param array $data POST mezők tömbje
* @return object A dekódolt JSON válasz objektumként
* @throws RuntimeException Ha hiányoznak a hitelesítési adatok, cURL hiba
* esetén, vagy ha a válasz nem érvényes JSON
*/
public function process(string $f, array $data): object
{
if (!$this->check()) {
throw new RuntimeException('ninjaMail: hiányzó gazdagép vagy API kulcs.');
}
$url = $this->host . '/a/' . rawurlencode($f) . '/?key=' . urlencode($this->key);
$ch = curl_init($url);
if ($ch === false) {
throw new RuntimeException('ninjaMail: nem sikerült inicializálni a cURL munkamenetet.');
}
curl_setopt_array($ch, [
CURLOPT_ENCODING => 'UTF-8',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $data,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_MAXREDIRS => 0,
CURLOPT_HTTPHEADER => ['Accept: application/json'],
]);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
curl_close($ch);
if ($response === false) {
throw new RuntimeException(
sprintf('ninjaMail: cURL hiba (%d): %s', $errno, $error)
);
}
$decoded = json_decode((string)$response);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new RuntimeException(
'ninjaMail: érvénytelen JSON válasz: ' . json_last_error_msg()
);
}
$result = (object)$decoded;
$this->data = $result;
return $result;
}
/**
* Véletlenszerű kulccsal indít távoli belépési munkamenetet.
*
* @return object API válasz (tartalmaz 'token' mezőt siker esetén)
* @throws RuntimeException Lásd: process()
*/
public function login(): object
{
return $this->process('login', [
'rkey' => bin2hex(random_bytes(16)),
]);
}
}
// ---------------------------------------------------------------------------
// Levélküldés
// ---------------------------------------------------------------------------
/**
* Egyszeri e-mail küldése megadott címzettnek.
*/
class ninjaMailSend extends ninjaMail
{
private string $to = '';
private string $subject = '';
private string $message = '';
private string $message_text = '';
/**
* @param string $to Feliratkozó azonosítója vagy e-mail cím
*/
public function to(string $to): true
{
$this->to = trim($to);
return true;
}
/**
* @param string $s Az e-mail tárgya
*/
public function subject(string $s): true
{
$this->subject = $s;
return true;
}
/**
* @param string $m HTML tartalom
* @param string|bool $t Opcionális egyszerű szöveges változat;
* ha nincs megadva, a HTML-ből kerül levezetésre
*/
public function message(string $m, string|bool $t = false): true
{
$this->message = $m;
$this->message_text = ($t !== false && $t !== '')
? (string)$t
: trim(strip_tags($m));
return true;
}
/**
* Elküldi az e-mailt.
*
* @return bool true ha a levél sikeresen sorba állt
* @throws InvalidArgumentException Ha kötelező mező hiányzik
* @throws RuntimeException Lásd: process()
*/
public function send(): bool
{
if (empty($this->to) || empty($this->subject) || empty($this->message)) {
throw new InvalidArgumentException(
'ninjaMailSend: a to, subject és message mezők kötelezők.'
);
}
$post = [
'to' => $this->to,
'subject' => $this->subject,
'message' => $this->message,
'message_text' => $this->message_text,
];
return $this->process('send', $post)->status === 'message_queued';
}
}
// ---------------------------------------------------------------------------
// Feliratkozások
// ---------------------------------------------------------------------------
/**
* Feliratkozók hozzáadása és eltávolítása levelezőlistákból.
*/
class ninjaMailSubscription extends ninjaMail
{
private int $list = 0;
private int $activated = 0;
private int $forcenamechange = 0;
/**
* @param int $id Lista azonosítója
* @return bool false ha az azonosító nem pozitív egész szám
*/
public function list(int $id): bool
{
if ($id <= 0) {
return false;
}
$this->list = $id;
return true;
}
/**
* @param bool $s true = azonnal megerősített; false = megerősítő e-mail küldése
*/
public function activated(bool $s): true
{
$this->activated = $s ? 1 : 0;
return true;
}
/**
* @param bool $s true = névfrissítés engedélyezése meglévő feliratkozóknál
*/
public function namechange(bool $s): true
{
$this->forcenamechange = $s ? 1 : 0;
return true;
}
/**
* Feliratkozó hozzáadása a listához.
*
* @param string $email A feliratkozó e-mail címe
* @param string $name A feliratkozó neve (opcionális)
* @return bool true sikeres feliratkozás esetén
* @throws InvalidArgumentException Ha a lista nincs beállítva
* @throws RuntimeException Lásd: process()
*/
public function subscribe(string $email, string $name = ''): bool
{
if ($this->list <= 0) {
throw new InvalidArgumentException(
'ninjaMailSubscription: lista azonosítója nincs beállítva.'
);
}
$post = [
'list' => $this->list,
'name' => $name,
'email' => $email,
'activated' => $this->activated,
'forcenamechange' => $this->forcenamechange,
];
return $this->process('subscribe', $post)->status === 'success';
}
/**
* Feliratkozó eltávolítása a listából.
*
* @param string $email A leiratkozó e-mail címe
* @return bool true sikeres leiratkozás esetén
* @throws InvalidArgumentException Ha a lista nincs beállítva
* @throws RuntimeException Lásd: process()
*/
public function unsubscribe(string $email): bool
{
if ($this->list <= 0) {
throw new InvalidArgumentException(
'ninjaMailSubscription: lista azonosítója nincs beállítva.'
);
}
$post = [
'list' => $this->list,
'email' => $email,
];
return $this->process('unsubscribe', $post)->status === 'success';
}
}
// ---------------------------------------------------------------------------
// Hírlevél
// ---------------------------------------------------------------------------
/**
* Hírlevelek létrehozása, frissítése és küldése.
*/
class ninjaMailNewsletter extends ninjaMail
{
private int $newsletter = 0;
private string $subject = '';
private string $message = '';
private string $message_text = '';
/**
* Lekérdezi vagy beállítja az aktuális hírlevél azonosítóját.
*
* @param int|false $id Hírlevél azonosítója a beállításhoz; false = lekérdezés
* @return int|bool Lekérdezési módban az aktuális azonosítót adja vissza;
* beállítási módban true/false
*/
public function newsletter(int|false $id = false): int|bool
{
if ($id !== false) {
if ($id > 0) {
$this->newsletter = $id;
return true;
}
return false;
}
return $this->newsletter;
}
/**
* @param string $s A hírlevél tárgya
*/
public function subject(string $s): true
{
$this->subject = $s;
return true;
}
/**
* @param string $m HTML tartalom
* @param string|bool $t Opcionális egyszerű szöveges változat
*/
public function message(string $m, string|bool $t = false): true
{
$this->message = $m;
$this->message_text = ($t !== false && $t !== '')
? (string)$t
: trim(strip_tags($m));
return true;
}
/**
* @param bool $update true = meglévő frissítése; false = új létrehozása
* @return int|false Az új/frissített hírlevél azonosítója, vagy false hiba esetén
* @throws InvalidArgumentException Ha nincs üzenet beállítva
* @throws RuntimeException Lásd: process()
*/
private function query(bool $update = false): int|false
{
if (empty($this->message)) {
throw new InvalidArgumentException(
'ninjaMailNewsletter: az üzenet tartalma kötelező.'
);
}
$post = [
'new' => true,
'id' => $update ? $this->newsletter : false,
'subject' => $this->subject,
'message' => $this->message,
'message_text' => $this->message_text,
];
$data = $this->process('newsletter', $post);
if ($data->status === 'success' && isset($data->id) && is_numeric($data->id)) {
$this->newsletter = (int)$data->id;
return $this->newsletter;
}
return false;
}
/**
* Új hírlevelet hoz létre.
*
* @return int|false Az új hírlevél azonosítója, vagy false hiba esetén
*/
public function create(): int|false
{
return $this->query(false);
}
/**
* Meglévő hírlevelet frissít.
*
* @return int|false A hírlevél azonosítója, vagy false hiba esetén
* @throws InvalidArgumentException Ha nincs hírlevél kiválasztva
*/
public function update(): int|false
{
if ($this->newsletter <= 0) {
throw new InvalidArgumentException(
'ninjaMailNewsletter: hírlevél azonosítója nincs beállítva a frissítéshez.'
);
}
return $this->query(true);
}
/**
* Hírlevelet küld azonnali vagy ütemezett időpontban.
*
* @param int|false $time Unix időbélyeg a küldés időpontjához; false vagy 0 = azonnali
* @return bool true sikeres sorbaállítás esetén
* @throws InvalidArgumentException Ha nincs hírlevél kiválasztva
* @throws RuntimeException Lásd: process()
*/
public function send(int|false $time = false): bool
{
if ($this->newsletter <= 0) {
throw new InvalidArgumentException(
'ninjaMailNewsletter: hírlevél azonosítója nincs beállítva a küldéshez.'
);
}
$post = [
'send' => true,
'id' => $this->newsletter,
'start' => ($time && $time > 0) ? $time : 0,
];
$data = $this->process('newsletter', $post);
return in_array($data->status ?? '', ['success', 'already_queued'], true);
}
/**
* Listázza az elérhető híreveleket.
*
* @return object API válasz tömbbel
* @throws RuntimeException Lásd: process()
*/
public function get(): object
{
return $this->process('newsletter', ['get' => true]);
}
}
// ---------------------------------------------------------------------------
// Kampány
// ---------------------------------------------------------------------------
/**
* Kampányok létrehozása, törlése és konfigurálása.
*/
class ninjaMailCampaign extends ninjaMail
{
private int $campaign = 0;
/**
* Lekérdezi vagy beállítja az aktuális kampány azonosítóját.
*
* @param int|false $id Kampány azonosítója beállításhoz; false = lekérdezés
* @return int|bool Lekérdezési módban az aktuális azonosítót adja vissza;
* beállítási módban true/false
*/
public function campaign(int|false $id = false): int|bool
{
if ($id === false) {
return $this->campaign;
}
if ($id > 0) {
$this->campaign = $id;
return true;
}
return false;
}
/**
* Új kampányt hoz létre.
*
* @param string $name A kampány neve
* @return int|false Az új kampány azonosítója, vagy false hiba esetén
* @throws InvalidArgumentException Ha a név üres
* @throws RuntimeException Lásd: process()
*/
public function create(string $name): int|false
{
if (trim($name) === '') {
throw new InvalidArgumentException(
'ninjaMailCampaign: a kampány neve nem lehet üres.'
);
}
$post = [
'new' => true,
'name' => $name,
];
$data = $this->process('campaign', $post);
if ($data->status === 'success' && isset($data->id) && is_numeric($data->id)) {
$this->campaign = (int)$data->id;
return $this->campaign;
}
return false;
}
/**
* Törli az aktuális kampányt.
*
* @return bool true sikeres törlés esetén
* @throws InvalidArgumentException Ha nincs kampány kiválasztva
* @throws RuntimeException Lásd: process()
*/
public function remove(): bool
{
if ($this->campaign <= 0) {
throw new InvalidArgumentException(
'ninjaMailCampaign: kampány azonosítója nincs beállítva a törléshez.'
);
}
$post = [
'remove' => true,
'id' => $this->campaign,
];
return $this->process('campaign', $post)->status === 'success';
}
/**
* Listákat rendel a kampányhoz.
*
* @param int[] $lists Lista azonosítók tömbje
* @return bool true sikeres frissítés esetén
* @throws InvalidArgumentException Ha a $lists üres vagy nincs kampány beállítva
* @throws RuntimeException Lásd: process()
*/
public function update(array $lists): bool
{
if (empty($lists)) {
throw new InvalidArgumentException(
'ninjaMailCampaign: legalább egy lista azonosítója szükséges.'
);
}
if ($this->campaign <= 0) {
throw new InvalidArgumentException(
'ninjaMailCampaign: kampány azonosítója nincs beállítva a frissítéshez.'
);
}
$post = [
'update' => true,
'id' => $this->campaign,
'lists' => $lists,
];
return $this->process('campaign', $post)->status === 'success';
}
/**
* Hírlevelet csatol a kampányhoz.
*
* @param int $newsletter A csatolni kívánt hírlevél azonosítója
* @return bool true sikeres csatolás esetén
* @throws InvalidArgumentException Ha az azonosító érvénytelen vagy nincs kampány beállítva
* @throws RuntimeException Lásd: process()
*/
public function attach(int $newsletter): bool
{
if ($newsletter <= 0) {
throw new InvalidArgumentException(
'ninjaMailCampaign: érvénytelen hírlevél azonosító.'
);
}
if ($this->campaign <= 0) {
throw new InvalidArgumentException(
'ninjaMailCampaign: kampány azonosítója nincs beállítva a csatoláshoz.'
);
}
$post = [
'relations' => true,
'id' => $this->campaign,
'newsletter' => $newsletter,
];
return $this->process('campaign', $post)->status === 'success';
}
}
// ---------------------------------------------------------------------------
// Statisztika
// ---------------------------------------------------------------------------
/**
* Hírlevél-statisztikák lekérdezése.
*/
class ninjaMailStatistics extends ninjaMail
{
/**
* Statisztikát kér le egy adott hírlevélről.
*
* @param int $id A hírlevél azonosítója
* @return object Az API statisztikai válasza
* @throws InvalidArgumentException Ha az azonosító érvénytelen
* @throws RuntimeException Lásd: process()
*/
public function get(int $id): object
{
if ($id <= 0) {
throw new InvalidArgumentException(
'ninjaMailStatistics: érvénytelen hírlevél azonosító.'
);
}
$post = [
'newsletter' => $id,
'type' => 1,
];
return $this->process('statistics', $post);
}
}