浏览代码

Library is finally ready to RC.

Cixo Develop 4 月之前
父节点
当前提交
b15c523bef

+ 47 - 0
sources/authorization_method.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace phpnotify;
+
+/**
+ * This class represents authorization method in the library.
+ * 
+ * It represents authorization service, it may be overwrite by other class
+ * which implements specified method, like user login or auth token.
+ */
+abstract class authorization_method { 
+    /**
+     * This return that authorization token is required or no.
+     * 
+     * This is used to determinate that authorization header is required.
+     * When it return true, then header would be added to the notify request.
+     *
+     * @return bool True when authorization token is required, false if not.
+     */
+    public abstract function is_header_required(): bool;
+
+    /**
+     * This return content of the authorization header.
+     * 
+     * This return content of the authorization header, or null when header
+     * is not required. Default return null.
+     * 
+     * @return ?string Return content of the authorization header or null
+     *                 when it is not required.
+     */
+    public function header_content(): string {
+        return '';
+    }
+
+    /**
+     * This return name of the authorization header. 
+     * 
+     * This return name of the authorization header. Default return 
+     * 'Authorization' which is standard name of that header. But it could
+     * be changed by overwriting that function.
+     *
+     * @return string Name of the authorization header.
+     */
+    public function header_name(): string {
+        return 'Authorization';
+    }   
+}

+ 57 - 0
sources/authorization_store.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace phpnotify;
+
+/**
+ * This could be used to store authorization method in the class.
+ *
+ * That class helps other classes storing authorization method, which is
+ * implemented in more than one class.
+ */
+class authorization_store {
+    /**
+     * This variable store current authorization method.
+     * @var authorization_method
+     */
+    private authorization_method $method;
+
+    /** 
+     * This initialize new aithorization part of class. It is protected.
+     * 
+     * This function initialize authorization store. When any method is not
+     * provided, then it use default empty authorization. Method could be
+     * given there, for example when it is used by class, which is created
+     * by class which also store authorization. It is protected, because it
+     * would not being used as autonomous class.
+     * 
+     * @param ?authorization_method Default method of authorization.
+     */
+    protected function __construct(?authorization_method $method = null) {
+        if ($method === null) {
+            $method = new empty_authorization();
+        }
+
+        $this->method = $method;
+    }
+
+    /**
+     * That function return current setup access authorization method.
+     *
+     * @return authorization_method Current authorization method.
+     */
+    public function get_access_method(): authorization_method {
+        return $this->method;
+    }
+   
+    /**
+     * This set new access method to use in the requests.
+     *
+     * @param authorization_method New method of the authorization.
+     *
+     * @return object Self to chain loading.
+     */
+    public function access_method(authorization_method $method): object {
+        $this->method = $method;
+        return $this;
+    }
+}

+ 29 - 0
sources/empty_authorization.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace phpnotify;
+
+/**
+ * That class represents guest mode without any authorization.
+ *
+ * That could be use, when any authorization is not required. It is not 
+ * recommended, because it is not safe. It would be use only in local
+ * enviroment, which is not available from internet.
+ */
+class empty_authorization extends authorization_method {
+    /** 
+     * It create new empty authorization.
+     * 
+     * It generate new empty authorization, which don't require any 
+     * parameters.
+     */
+    public function __construct() {}
+
+    /**
+     * It return false, because authorization is not required.
+     *
+     * @return bool It return that header is required.
+     */
+    public function is_header_required(): bool {
+        return false;
+    }
+}

+ 0 - 2
sources/fetch.php

@@ -21,8 +21,6 @@ use \curl_exec as curl_exec;
 use \curl_error as curl_error;
 use \curl_errno as curl_errno;
 
-require_once('response.php');
-
 /**
  * This class is responsible for downloading data from the server. It uses 
  * buildin CURL extension to make HTTP request. It not parsing response 

+ 78 - 0
sources/login_authorization.php

@@ -0,0 +1,78 @@
+<?php
+
+namespace phpnotify;
+
+use \TypeError as TypeError;
+use \base64_encode as base64_encode;
+
+/**
+ * This could be used to authorize by username and password.
+ * 
+ * It could be used to authorize by login and password. It is better option
+ * than empty authorization, which does not give any security, but it is 
+ * necessary to use TLS, because password and login does not being hashed, 
+ * and could being captured by intermediary when use empty HTTP.
+ */
+class login_authorization extends authorization_method {
+    /**
+     * It store encoded login and password, in form which is used by header,
+     * that mean `base64('login:password')`.
+     * @var string
+     */
+    private string $coded;
+
+    /** 
+     * This create new login authorization.
+     *
+     * This create new login authorization, require username and password
+     * for them. Username and password could not being empty.
+     *
+     * @throws TypeError When login or password is empty.
+     * 
+     * @param string $login Login of the user.
+     * @param string $password Password for that user.
+     */
+    public function __construct(string $login, string $password) {
+        $login = trim($login);
+        $password = trim($password);
+        
+        if (strlen($login) === 0 || strlen($password) === 0) {
+            throw new TypeError('Login and password could being empty.');
+        }
+
+        $this->coded = $this->encode($login, $password);
+    }
+
+    /**
+     * It return true, because header is required when use login.
+     *
+     * @return bool True because header is required for login authorization.
+     */
+    public function is_header_required(): bool {
+        return true;
+    }
+
+    /**
+     * This return content of the header.
+     *
+     * This return content of the headed, which contain coded login and
+     * password. It is coded in the base64, which is not hash function end
+     * could be easy decoded. It is cause that TLS must being used.
+     *
+     * @return string Content of the headed.
+     */
+    public function header_content(): string {
+        return 'Basic '.$this->coded;
+    }
+
+    /**
+     * This encode login and password to use it headed.
+     *
+     * @param string $login Login of the user.
+     * @param string $password Password for that user.
+     * @return string Encoded form of login and password.
+     */
+    private function encode(string $login, string $password): string {
+        return base64_encode($login.':'.$password);
+    }
+}

+ 198 - 102
sources/notification.php

@@ -2,36 +2,65 @@
 
 namespace phpnotify;
 
-require_once('fetch.php');
-
-class notification {
-    private array $tags;
-    private array $actions;
-    private fetch $request;
-
-    public function __construct(string $location, string $content) {
-        $this->tags = array();
-        $this->actions = array();
-
-        $this->request = new fetch($location);
-        $this->request->set_method('POST');
-        $this->request->send_raw($content, 'text/plain');
+use \TypeError as TypeError;
+use \RuntimeException as RuntimeException;
+
+/**
+ * It add more personalization options to the notifications.
+ *
+ * @see https://docs.ntfy.sh/publish/#list-of-all-parameters
+ */
+class notification extends notification_base{
+    /**
+     * This store tags, which would be send.
+     * @var array
+     */
+    private array $tags = [];
+
+    /**
+     * That store actions for the notification.
+     * @var array 
+     */
+    private array $actions = [];
+
+    /**
+     * That set notification title.
+     * 
+     * @param string $title Title to set.
+     * 
+     * @return notification Self to chain loading.
+     */
+    public function title(string $title): object {
+        return $this->set('Title', $title);
     }
 
-    public function set_title(string $title): object {
-        $this->request->add_header('Title', $title);
-        return $this;
-    }
-
-    public function set_priority(int $priority): object {
+    /**
+     * That set notification priority. Priority could be from 1 to 5.
+     *
+     * @see https://docs.ntfy.sh/publish/#message-priority
+     * 
+     * @param int Epriority Message priority.
+     * 
+     * @return notification Self to chain loading.
+     */
+    public function priority(int $priority): object {
         if ($priority < 1 || $priority > 5) {
             throw new TypeError('Priority must be between 1 and 5.');
         }
 
-        $this->request->add_header('Priority', strval($priority));
-        return $this;
+        return $this->set('Priority', strval($priority));
     }
 
+    /**
+     * That add new tag to notification
+     *
+     * @see https://docs.ntfy.sh/publish/#tags-emojis
+     * @see https://docs.ntfy.sh/emojis/
+     * 
+     * @param string $tag New tag to add.
+     * 
+     * @return notification Self to chain loading.
+     */
     public function add_tag(string $tag): object {
         $tag = trim($tag);
 
@@ -47,60 +76,115 @@ class notification {
         return $this;
     }
 
+    /**
+     * That return list of tags to use when request is processing.
+     *
+     * @return string Header with the tags.
+     */
     private function get_tags(): string {
         return join(',', $this->tags);
     }
 
-    public function set_markdown(): object {
-        $this->request->add_header('Markdown', 'yes');
-        return $this;
+    /**
+     * That make notification body markdown formated.
+     * 
+     * @see https://docs.ntfy.sh/publish/#markdown-formatting
+     * 
+     * @return notification Self to chain loading.
+     */
+    public function enable_markdown(): object {
+        return $this->set('Markdown', 'yes');
     }
 
+    /**
+     * That make notification delayed delivery.
+     * 
+     * @see https://docs.ntfy.sh/publish/#scheduled-delivery
+     *
+     * @param string $when Time to show notification.
+     * 
+     * @return notification Self to chain loading.
+     */
     public function delivery_time(string $when): object {
-        $this->request->add_header('At', $when);
-        return $this;
+        return $this->set('At', $when);
     }
 
-    public function set_email(string $email): object {
+    /**
+     * That send notification to given email.
+     *
+     * @see https://docs.ntfy.sh/publish/#__tabbed_27_3
+     * 
+     * @param string $email Email to send notification to.
+     * 
+     * @return notification Self to chain loading.
+     */
+    public function email(string $email): object {
         if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
             throw new TypeError('Email "'.$email.'" is invalid.');
         }
 
-        $this->request->add_header('Email', $email);
-        return $this;
+        return $this->set('Email', $email);
     }
 
+    /**
+     * That set link which would being open after click on notification.
+     *
+     * @see https://docs.ntfy.sh/publish/#click-action
+     * 
+     * @param string $url Url to open after click.
+     * 
+     * @return notification Self to chain loading.
+     */
     public function click_url(string $url): object {
-        if (!filter_var($url, FILTER_VALIDATE_URL)) {
-            throw new TypeError('"'.$url.'" is not valid URL.');
-        }
-
-        $this->request->add_header('Action', $url);
-        return $this;
+        return $this->set('Click', $url);
     }
 
+    /**
+     * That add link to attachment.
+     *
+     * @see https://docs.ntfy.sh/publish/#attach-file-from-a-url
+     * 
+     * @param string $title Title to set.
+     * 
+     * @return notification Self to chain loading.
+     */
     public function attach_url(string $url): object {
         if (!filter_var($url, FILTER_VALIDATE_URL)) {
             throw new TypeError('"'.$url.'" is not valid URL.');
         }
 
-        $this->request->add_header('Attach', $url);
-        return $this;
+        return $this->set('Attach', $url);
     }
 
+    /**
+     * That set URL to notification icon.
+     *
+     * @see https://docs.ntfy.sh/publish/#icons
+     * 
+     * @param string $url Url of the notification.
+     * 
+     * @return notification Self to chain loading.
+     */
     public function icon_url(string $url): object {
         if (!filter_var($url, FILTER_VALIDATE_URL)) {
             throw new TypeError('"'.$url.'" is not valid URL.');
         }
 
-        $this->request->add_header('Icon', $url);
-        return $this;
+        return $this->set('Icon', $url);
     }
 
+    /**
+     * That would make notification phone call.
+     * 
+     * @see https://docs.ntfy.sh/publish/#phone-calls
+     *
+     * @param string|true True when call would be make or phone number.
+     * 
+     * @return notification Self to chain loading.
+     */
     public function call(string|true $phone_number): object {
         if ($phone_number === true) {
-            $this->request->add_header('Call', 'yes');
-            return $this;
+            return $this->set('Call', 'yes');
         }
 
         $check = str_replace(' ', '', $phone_number);
@@ -113,56 +197,81 @@ class notification {
             throw new TypeError('"'.$phone_number.'" is invalid number.');
         }
 
-        $this->request->add_header('Call', $phone_number);
-        return $this;
+        return $this->set('Call', $phone_number);
     }
 
+    /**
+     * That add name of the file, which would be send,,
+     * 
+     * @see https://docs.ntfy.sh/publish/#attachments
+     * 
+     * @param string $filename Name of the file to save.
+     * 
+     * @return notification Self to chain loading.
+     */
     public function save_as(string $filename): object {
         if (basename($filename) !== $filename) {
             throw new TypeError('"'.$filename.'" is invalid filename.');
         }
 
-        $this->request->add_header('Filename', $filename);
-        return $this;
-    }
-
-    public function login(string $nick, string $password): object {
-        $decoded = $nick.':'.$password;
-        $encoded = base64_encode($decoded);
-        $param = 'Basic '.$encoded;
-
-        $this->request->add_header('Authorization', $param);
-        return $this;
-    }
-
-    public function token(string $token): object {
-        $token = trim($token);
-        $param = 'Bearer '.$token;
-
-        $this->request->add_header('Authorization', $param);
-        return $this;
+        return $this->set('Filename', $filename);
     }
 
+    /**
+     * That would disable sending notification via firebase.
+     * 
+     * @see https://docs.ntfy.sh/publish/#disable-firebase
+     *
+     * @return notification Self to chain loading.
+     */
     public function disable_firebase(): object {
-        $this->request->add_header('Firebase', 'no');
-        return $this;
+        return $this->set('Firebase', 'no');
     }
 
+    /**
+     * That would disable notification cache on server side.
+     * 
+     * @see https://docs.ntfy.sh/publish/#message-caching
+     * 
+     * @return notification Self to chain loading.
+     */
     public function disable_cache(): object {
-        $this->request->add_header('Cache', 'no');
-        return $this;
+        return $this->set('Cache', 'no');
     }
 
-    public function set_poll_id(string $id): object {
-        $this->request->add_header('Poll-ID', $id);
-        return $this;
+    /**
+     * That would set iPhone Poll-ID.
+     * 
+     * @param string $id Poll ID to set.
+     *
+     * @return notification Self to chain loading.
+     */
+    public function poll_id(string $id): object {
+        return $this->set('Poll-ID', $id);
     }
 
+    /**
+     * That would enable unified push delivery.
+     *
+     * @see https://docs.ntfy.sh/publish/#unifiedpush
+     *
+     * @return notification Self to chain loading
+     */
     public function enable_unified_push(): object {
-        $this->request->add_header('UnifiedPush', 'yes');
-        return $this;
+        return $this->set('UnifiedPush', 'yes');
     }
 
+    /**
+     * That add new action to the notification.
+     *
+     * @see https://docs.ntfy.sh/publish/#action-buttons
+     * 
+     * @param string $action Action type.
+     * @param string $label Label of the action button.
+     * @param ?array<str, str> $param List of the actions params.
+     *
+     * @return notification Self to chain loading.
+     */
     public function add_action(
         string $action, 
         string $label, 
@@ -184,41 +293,28 @@ class notification {
         array_push($this->actions, $content);
         return $this;
     }
-
+    
+    /**
+     * That process all actions into header content.
+     *
+     * @return string Content of the actions header.
+     */
     private function get_actions(): string {
         return join('; ', $this->actions);
     }
 
-    public function send(bool $throws = false): bool {
-        $this->request->set_header('Actions', $this->get_actions());
-        $this->request->set_header('Tags', $this->get_tags());
-        
-        try {
-            $result = $this->request->request()->receive_array();
+    /**
+     * Overwriting it is required to add headers with arrays.
+     */
+    protected function prepare(): void {
+        parent::prepare();
 
-            if (array_key_exists('id', $result)) {
-                return true;
-            }
+        if (count($this->actions) > 0) {
+            $this->set('Actions', $this->get_actions());
+        }
 
-            if (count($result) === 0) {
-                throw new RuntimeException('Can not fetch. General error.');
-            }
-        
-            if (array_key_exists('error', $result)) {
-                $error = (
-                    'Can not publish: "'
-                    .$result['error']
-                    .'"'
-                );
-
-                throw new RuntimeException($error);
-            }
-        } catch (Exception $error) {
-            if ($throws) {
-                throw $error;
-            }
-
-            return false;
+        if (count($this->tags) > 0) {
+            $this->set('Tags', $this->get_tags());
         }
     }
-}
+}

+ 131 - 0
sources/notification_base.php

@@ -0,0 +1,131 @@
+<?php
+
+namespace phpnotify;
+
+use \TypeError as TypeError;
+use \RuntimeException as RuntimeException;
+
+/**
+ * This class is base of the notification.
+ *
+ * This class is base for the notification. It would be enlarged by other
+ * classes with new functions, which gives more functionality. It give only
+ * base, requireed to send notification, that mean content and process for
+ * authentication. 
+ */
+class notification_base {
+    /**
+     * This store request to the server.
+     * @var fetch
+     */
+    private fetch $query;
+
+    /**
+     * This store authorization method which would be used to send
+     * notification into server.
+     * @var authorization_method
+     */
+    private authorization_method $authorization;
+
+    /**
+     * This create new notification.
+     *
+     * This create new notification, it would be used only by library, by 
+     * {@see subject} class. It require url of the subject, content of the
+     * notificatin and authorization method,
+     * 
+     * @param string $url Location of the notification subject.
+     * @param string $content Content of the notification.
+     * @param authorization_method $method Method of the authorization.
+     */
+    public function __construct(
+        string $url,
+        string $content, 
+        authorization_method $method
+    ) {
+        $this->authorization = $method;
+
+        $this->query = new fetch($url);
+        $this->query->set_method('POST');
+        $this->query->send_raw($content, 'text/plain');
+    }
+
+    /**
+     * This function is used to prepare request before send.
+     * 
+     * This function is called before makes fetch request. It could being
+     * overwriten, but that function must still being called, to process
+     * authorization headers.
+     */
+    protected function prepare(): void {
+        if ($this->authorization->is_header_required()) {
+            $name = $this->authorization->header_name();
+            $content = $this->authorization->header_content();
+
+            $this->query->set_header($name, $content);
+        }
+    }
+  
+    /**
+     * This function could being used to add functionality of that class.
+     *
+     * That makes adding new functionalities to that class simple, because
+     * add posibility to makes simple one-line functions. That function 
+     * set header of the request, and return notification itself.
+     *
+     * @param string $name Name of the header to set.
+     * @param string $content Content of the header.
+     *
+     * @return notification_base Self to chain loading.
+     */
+    protected function set(string $name, string $content): object {
+        $this->query->set_header($name, $content);
+        return $this;
+    }
+
+    /**
+     * That function send notification request.
+     * 
+     * That function send notification request to the notifications server.
+     * It process request, and return true when all went well. When any error
+     * occurs, it returns false, or when $throws parameter is true, it throws
+     * error, which occurs.
+     *
+     * @throws RuntimeException When $throws parameter is true end any error
+     *                          occurs.
+     *
+     * @param bool $throws When true function would throw exception on 
+     *                     request error, when false it return false on fetch
+     *                     error.
+     *
+     * @return bool True when notification had been send, false.
+     */
+    public function send(bool $throws = false): bool {
+        try {
+            $this->prepare();
+
+            $result = $this
+            ->query
+            ->request()
+            ->receive_array();
+
+            if (array_key_exists('id', $result)) {
+                return true;
+            }
+
+            if (count($result) === 0) {
+                throw new RuntimeException('Can not fetch. General error.');
+            }
+
+            if (array_key_exists('error', $result)) {
+                throw new RuntimeException('"'.$error.'" error occurs.');
+            }
+        } catch (Exception $error) {
+            if ($throws) {
+                throw $error;
+            }
+
+            return false;
+        }
+    }
+}

+ 71 - 5
sources/notifier.php

@@ -2,16 +2,82 @@
 
 namespace phpnotify;
 
-require_once('subject.php');
+use \TypeError as TypeError;
 
-class notifier {
+/**
+ * This represents notification server, in library named notifier.
+ * 
+ * This class represents notification server in the system. It is required
+ * to subscribe new subject. It also store auth service like login and 
+ * password, or auth token. That class would be used to generate new subjects
+ * with same server url, and potentially same authorization methods. It makes 
+ * generating that subjects much easier, without passing same authentication 
+ * and server URL to all new subjects. Change authentication method in new 
+ * subject generated by notifier would not change notifier authentication
+ * method.
+ */
+class notifier extends authorization_store {
+    /**
+     * This store URL of the notifications server.
+     * @var string
+     */
     private string $url;
 
+    /** 
+     * This function initialize new notifier by the server URL.
+     *
+     * This create new notifications server wrapper, from that URL. It 
+     * use empty authorization as default authorization method. URL is 
+     * validating, and when incorrect URL format had been incorrect throws
+     * TypeError.
+     * 
+     * @throws TypeError When URL is bad formated.
+     *
+     * @param string URL of the server.
+     */
     public function __construct(string $url) {
+        parent::__construct();
+        
+        if (!filter_var($url, FILTER_VALIDATE_URL)) {
+            throw new TypeError('"'.$url.'" is not property URL.');
+        }
+        
         $this->url = $url;
     }
 
-    public function subscribe(string $subject): subject {
-        return new subject($this->url, $subject);
+    /**
+     * This function create new notifier from URL.
+     * 
+     * That function create new notifier from URL, but it not use 'new' 
+     * keyword, which results in the better look code with chainloading.
+     * It use same constructor as {@see notifier::__construct}.
+     *
+     * @throws TypeError Whem URL is bad formated.
+     *
+     * @param string $url URL for the notifications server.
+     * @return notifier New notification server.
+     */
+    public static function create(string $url): object {
+        return new self($url);
     }
-}
+
+    /** 
+     * This function would be use to select a subject.
+     * 
+     * This function select a subject to notify in. It return new subject 
+     * which is initialized from notifier URL and authorization method.
+     * When authorization method of the subject had been changed, notifier 
+     * still use own authorization method for new subjects.
+     *
+     * @param string $subject Name of the subject to use.
+     *
+     * @return subject New subject to send notifications in.
+     */
+    public function topic(string $subject): subject {
+        return new subject(
+            $this->url, 
+            $subject, 
+            $this->get_access_method()
+        );
+    }
+}

+ 30 - 0
sources/phpnotify.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace phpnotify;
+
+/**
+ * This function load file with given name.
+ *
+ * @param string $name Name of the file to load.
+ */
+function load(string $name): void {
+    $directory = dirname(__FILE__);
+    $file = $directory.'/'.$name;
+
+    require_once($file);
+}
+
+/** 
+ * Order of that file depends on classes hierarchy.
+ */
+load('response.php');
+load('fetch.php');
+load('authorization_method.php');
+load('empty_authorization.php');
+load('login_authorization.php');
+load('token_authorization.php');
+load('authorization_store.php');
+load('notifier.php');
+load('subject.php');
+load('notification_base.php');
+load('notification.php');

+ 108 - 12
sources/subject.php

@@ -2,22 +2,118 @@
 
 namespace phpnotify;
 
-require_once('notification.php');
-
-class subject {
+/**
+ * That class represents subject, also known as topic in library.
+ * 
+ * That represents single topic, also known as subject in the library. It
+ * would be build by notifier, but could also being builded manually without
+ * it. Subject would be used to generating new notifications, which could be
+ * personalized and sended to notifications server
+ */
+class subject extends authorization_store {
+    /** 
+     * URL of the subject.
+     * @var string
+     */
     private string $url;
-    private string $subject;
 
-    public function __construct(string $url, string $subject) {
-        $this->url = $url;
-        $this->subject = $subject;
+    /**
+     * This create new subject.
+     *
+     * This create new topic from the server URL, subject name and also
+     * authentication method. It would being used by notifier class, for
+     * generating subjects manually use {@see subject::create} static 
+     * function. When subject is incorrect or url generated from that is
+     * not valid URL, raises TypeError.
+     *
+     * @throws TypeError When subject is not correct
+     * @throws TypeError When final subject URL is not valid.
+     *
+     * @param string $server Server URL.
+     * @param string $subject Subject name.
+     * @param authorization_method $authorization Authorization method to use.
+     */ 
+    public function __construct(
+        string $server, 
+        string $subject,
+        authorization_method $authorization
+    ) {
+        parent::__construct($authorization);
+
+        $subject = trim($subject);
+
+        if (strlen($subject) === 0 || strpos($subject, ' ') !== false) {
+            throw new TypeError(
+                'Subject could not being empty and contains space.'
+            );
+        }
+
+        $this->url = $this->get_topic_url($server, $subject);
+
+        if (!filter_var($this->url, FILTER_VALIDATE_URL)) {
+            throw new TypeError('"'.$subject.'" is not property topic name.');
+        }
     }
 
-    private function get_subject_url(): string {
-        return $this->url.'/'.$this->subject;
+    /**
+     * That function create new subject directly from server name and
+     * subject name. It use empty authorization method by default, but it
+     * could be changed. When server URL is invalid throws TypeError.
+     *
+     * @throws TypeError When server URL is invalid.
+     * 
+     * @param string $server URL of the server.
+     * @param string $subject Name of the subject.
+     *
+     * @return subject New created subject.
+     */
+    public static function create(string $server, string $subject): subject {
+        if (!filter_var($server, FILTER_VALIDATE_URL)) {
+            throw new TypeError('"'.$server.'" is not property URL.');
+        }
+
+        return new self($server, $subject, new empty_authorization());
+    }
+
+    /**
+     * This return full subject url.
+     *
+     * This function return full URL to the subject. It require server 
+     * URL and subject name. 
+     *
+     * @param string $server Server URL.
+     * @param string $subject Subject to use.
+     *
+     * @return string Full subject URL.
+     */
+    private function get_topic_url(
+        string $server, 
+        string $subject
+    ): string {
+        if ($server[strlen($server) - 1] === '/') {
+            return $server.$subject;
+        }
+
+        return $server.'/'.$subject;
     }
 
-    public function publish(string $content): notification {
-        return new notification($this->get_subject_url(), $content);
+    /**
+     * This function generate new notification to publish in subject.
+     *
+     * This create new notification, to publish in that subject. Content 
+     * given as parameter is content of the notofication. Notification
+     * could be infill with more data before send, that function not sending 
+     * notification, but only create it.
+     *
+     * @param string $content Content of the new notification.
+     *
+     * @return notification New notification to infill with data and send.
+     */
+    public function new_notification(string $content): notification {
+        return new notification(
+            $this->url, 
+            $content,
+            $this->get_access_method()
+        );
     }
-}
+}

+ 56 - 0
sources/token_authorization.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace phpnotify;
+
+use \TypeError as TypeError;
+
+/**
+ * That class could be used to authorize by token.
+ * 
+ * This could be used to authenticate by previously generated token, which
+ * is most secure option in most cases. 
+ */
+class token_authorization extends authorization_method {
+    /**
+     * This store authorization token.
+     * @var string
+     */
+    private string $token;
+
+    /** 
+     * This create new authorization method.
+     * 
+     * This create new authorization method from given authorization token.
+     * It check that token is not blank and also triming it. When token is
+     * empty, then raise TypeError.
+     *
+     * @throws TypeError When given token is empty.
+     * 
+     * @param string $token Authorization token to use.
+     */
+    public function __construct(string $token) {
+        $this->token = trim($token);
+
+        if (strlen($this->token) === 0) {
+            throw new TypeError('Authorization token could not being empty.');
+        }
+    }
+
+    /**
+     * When use authorization by token, header is required.
+     * 
+     * @return bool It return true, because header is required.
+     */
+    public function is_header_required(): bool {
+        return true;
+    }
+
+    /**
+     * It return content of the header, with given token.
+     *
+     * @return string Content of the authorization header.
+     */
+    public function header_content(): string {
+        return 'Bearer '.$this->token;
+    }
+}

+ 2 - 2
tests/01-fetch.php

@@ -2,7 +2,7 @@
 
 namespace phpnotify;
 
-require('../sources/fetch.php');
+require(dirname(__FILE__).'/../sources/phpnotify.php');
 
 $response = fetch::create('https://jsonplaceholder.typicode.com/todos/1')->request();
 echo(var_dump($response->receive_array()));
@@ -20,4 +20,4 @@ echo(var_dump($create->receive_array()));
 
 $result = fetch::create('https://jsonplaceholder.typicode.com/posts/1')
 ->request();
-echo(var_dump($result->receive_array()));
+echo(var_dump($result->receive_array()));

+ 17 - 12
tests/02-notify.php

@@ -2,14 +2,15 @@
 
 namespace phpnotify;
 
-require_once('../sources/notifier.php');
-require_once('../sources/subject.php');
-require_once('../sources/notification.php');
+/**
+ * Load library.
+ */
+require_once(dirname(__FILE__).'/../sources/phpnotify.php');
 
 function get_line(string $name): string {
-    $content = file_get_contents($name);
-    $content = str_replace('\r', '', $content);
-    $content = str_replace('\n', '', $content);
+    $content = file_get_contents(dirname(__FILE__).'/'.$name);
+    $content = str_replace("\r", '', $content);
+    $content = str_replace("\n", '', $content);
 
     return $content;
 }
@@ -19,11 +20,15 @@ $TOKEN = get_line('.token');
 $URL = get_line('.url');
 $SUBJECT = get_line('.subject');
 
-$notifier = new notifier('https://ntfy.cixoelectronic.pl');
-$subject = $notifier->subscribe('test');
+$auth = new token_authorization($TOKEN);
+
+$subject = notifier::create($URL)->access_method($auth)->topic($SUBJECT);
 
 $subject
-->publish('Sample content')
-->set_title('Title of')
-->token($TOKEN)
-->send(true);
+->new_notification('Sample content')
+->title('Title of')
+->click_url('https://cixoelectronic.pl')
+->add_tag('file_folder')
+->add_tag('microbe')
+->priority(4)
+->send(true);