Preventing XSS in Input $_POST Form
Find purpose-built libraries which are actively maintained. For example, anti-xss:

$harm_string = "Hello, i try to &lt;script&gt;alert('Hack');&lt;/script&gt; your site";
$harmless_string = $antiXss-&gt;xss_clean($harm_string);

$harm_string = "&lt;IMG SRC=&amp;#x6A&amp;#x61&amp;#x76&amp;#x61&amp;#x73&amp;#x63&amp;#x72&amp;#x69&amp;#x70&amp;#x74&amp;#x3A&amp;#x61&amp;#x6C&amp;#x65&amp;#x72&amp;#x74&amp;#x28&amp;#x27&amp;#x58&amp;#x53&amp;#x53&amp;#x27&amp;#x29&gt;";
$harmless_string = $antiXss-&gt;xss_clean($harm_string);

$harm_string = "&lt;a href='&amp;#x2000;javascript:alert(1)'&gt;CLICK&lt;/a&gt;";
$harmless_string = $antiXss-&gt;xss_clean($harm_string);

$harm_string = "&lt;a href=\"\u0001java\u0003script:alert(1)\"&gt;CLICK&lt;a&gt;";
$harmless_string = $antiXss-&gt;xss_clean($harm_string);

$harm_string = '&lt;li style="list-style-image: url(javascript:alert(0))"&gt;';
$harmless_string = $antiXss-&gt;xss_clean($harm_string);

$harm_string = '&lt;li style="list-style-image: url(javascript:alert(0))"&gt;';
$antiXss-&gt;removeEvilAttributes(array('style')); // allow style-attributes
$harmless_string = $antiXss-&gt;xss_clean($harm_string);

$harm_string = "\x3cscript src=http://www.example.com/malicious-code.js\x3e\x3c/script\x3e";
$harmless_string = $antiXss-&gt;xss_clean($harm_string);

$harm_string = "&lt;iframe width="560" onclick="alert('xss')" height="315" src="https://www.youtube.com/embed/foobar?rel=0&amp;controls=0&amp;showinfo=0" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;";

$antiXss-&gt;removeEvilHtmlTags(array('iframe'));


And a UTF8 library for data you are sending:

UTF8::json_enocde(array(1, '¥', 'ä')); // '[1,"\u00a5","\u00e4"]'

UTF8::html_encode('中文空白'); // '&amp;#20013;&amp;#25991;&amp;#31354;&amp;#30333;'

UTF8::utf8_decode(UTF8::utf8_encode('-ABC-中文空白-')); // '-ABC-中文空白-'


Implement Content Security Policy(CSP) on the web server as well. For example:

default-src 'self' https:; connect-src 'self' https: http:; font-src 'self' https:; frame-src *; img-src
 'self' https: http: data:; media-src 'self' https:; object-src 'self' https:; script-src 'sha256-q590j1fW
+aERb666H10h55ePy0sxRjUYCiOmJPftXDs=' 'self' https: 'unsafe-eval' 'unsafe-inline' http:; style-src 'self'
 https: 'unsafe-inline' http:; report-uri /tracking/csp?action=listing_frame&controller=embed&req_uuid
=cff37d5d-4c12-4c8b-b288-1ce0d103a25c&version=c7fc601874a5350c79eceb33ba6d4c09a433035f;




  default-src is set to 'self' which means it's setting all CSP rules to only allow src attribute from a same-origin. In short, you should only be able to load src from a relative endpoint.
  frame-src is set to wildcard (*) so we can load external src links in frames (iframe, frame, frameset). Because we're injecting HTML past the body element, we cannot use frame or frameset. The WAF has made it next to impossible to use iframe.
  script-src has 'self' supplied after the sha256 hashed script for unsafe-inline and unsafe-eval, but https does not have 'self' supplied meaning we can load external scripts for execution.


References


anti-xss repo: public function testXssClean()
PayloadsAllTheThings/XSS injection at master · swisskyrepo/PayloadsAllTheThings 
UTF8 can be tricky – especially with PHP – DerEuroMark
voku/portable-utf8:  Portable UTF-8 library - performance optimized (unicode) string functions for php.
Airbnb – When Bypassing JSON Encoding, XSS Filter, WAF, CSP, and Auditor turns into Eight Vulnerabilities | Brett Buerhaus
How to protect PHP application from XSS attacks: CSP 3 nonce | PHP &amp; Symfony Tips