From 622d49435ac519b6e6d49b6660eab010f04d1120 Mon Sep 17 00:00:00 2001 From: kodeart Date: Sat, 14 Oct 2023 16:34:56 +0000 Subject: [PATCH] deploy: 724c5e1bf797b73f79aa3d67b894af4998cb7154 --- 404.html | 2 +- assets/javascripts/bundle.407015b8.min.js | 29 -------------- assets/javascripts/bundle.407015b8.min.js.map | 8 ---- assets/javascripts/bundle.aecac24b.min.js | 29 ++++++++++++++ assets/javascripts/bundle.aecac24b.min.js.map | 7 ++++ assets/javascripts/lunr/min/lunr.el.min.js | 1 + assets/javascripts/lunr/min/lunr.he.min.js | 1 + assets/javascripts/lunr/min/lunr.hy.min.js | 1 + assets/javascripts/lunr/min/lunr.kn.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 2 +- assets/javascripts/lunr/min/lunr.sa.min.js | 1 + assets/javascripts/lunr/min/lunr.te.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 2 +- assets/javascripts/lunr/wordcut.js | 4 +- ...208ed371.min.js => search.f886a092.min.js} | 18 ++++----- ....min.js.map => search.f886a092.min.js.map} | 9 ++--- assets/stylesheets/main.35e1ed30.min.css | 1 + assets/stylesheets/main.35e1ed30.min.css.map | 1 + assets/stylesheets/main.7a7fce14.min.css | 1 - assets/stylesheets/main.7a7fce14.min.css.map | 1 - assets/stylesheets/palette.356b1318.min.css | 1 + .../stylesheets/palette.356b1318.min.css.map | 1 + assets/stylesheets/palette.a0c5b2b5.min.css | 1 - .../stylesheets/palette.a0c5b2b5.min.css.map | 1 - configure/index.html | 4 +- configure/modules/index.html | 4 +- default-dependencies/index.html | 2 +- exception-handlers/index.html | 4 +- get-started/index.html | 4 +- index.html | 4 +- middleware/builtin/index.html | 2 +- middleware/cors/index.html | 4 +- middleware/psr-15/index.html | 4 +- routing/caching/index.html | 2 +- routing/index.html | 4 +- routing/parameters/index.html | 4 +- search/search_index.json | 2 +- shared-instances/index.html | 2 +- sitemap.xml | 36 +++++++++--------- sitemap.xml.gz | Bin 365 -> 365 bytes translation/catalogs/index.html | 4 +- translation/formatters/index.html | 2 +- translation/index.html | 4 +- utils/caching/index.html | 4 +- utils/logger/index.html | 4 +- 45 files changed, 114 insertions(+), 110 deletions(-) delete mode 100644 assets/javascripts/bundle.407015b8.min.js delete mode 100644 assets/javascripts/bundle.407015b8.min.js.map create mode 100644 assets/javascripts/bundle.aecac24b.min.js create mode 100644 assets/javascripts/bundle.aecac24b.min.js.map create mode 100644 assets/javascripts/lunr/min/lunr.el.min.js create mode 100644 assets/javascripts/lunr/min/lunr.he.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hy.min.js create mode 100644 assets/javascripts/lunr/min/lunr.kn.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sa.min.js create mode 100644 assets/javascripts/lunr/min/lunr.te.min.js rename assets/javascripts/workers/{search.208ed371.min.js => search.f886a092.min.js} (66%) rename assets/javascripts/workers/{search.208ed371.min.js.map => search.f886a092.min.js.map} (62%) create mode 100644 assets/stylesheets/main.35e1ed30.min.css create mode 100644 assets/stylesheets/main.35e1ed30.min.css.map delete mode 100644 assets/stylesheets/main.7a7fce14.min.css delete mode 100644 assets/stylesheets/main.7a7fce14.min.css.map create mode 100644 assets/stylesheets/palette.356b1318.min.css create mode 100644 assets/stylesheets/palette.356b1318.min.css.map delete mode 100644 assets/stylesheets/palette.a0c5b2b5.min.css delete mode 100644 assets/stylesheets/palette.a0c5b2b5.min.css.map diff --git a/404.html b/404.html index 2ade951..29c133e 100644 --- a/404.html +++ b/404.html @@ -1 +1 @@ - Koded Framework
\ No newline at end of file + Koded Framework
\ No newline at end of file diff --git a/assets/javascripts/bundle.407015b8.min.js b/assets/javascripts/bundle.407015b8.min.js deleted file mode 100644 index 4361bb7..0000000 --- a/assets/javascripts/bundle.407015b8.min.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict";(()=>{var Ri=Object.create;var gr=Object.defineProperty;var ki=Object.getOwnPropertyDescriptor;var Hi=Object.getOwnPropertyNames,Ht=Object.getOwnPropertySymbols,Pi=Object.getPrototypeOf,yr=Object.prototype.hasOwnProperty,on=Object.prototype.propertyIsEnumerable;var nn=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))yr.call(t,r)&&nn(e,r,t[r]);if(Ht)for(var r of Ht(t))on.call(t,r)&&nn(e,r,t[r]);return e};var an=(e,t)=>{var r={};for(var n in e)yr.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&Ht)for(var n of Ht(e))t.indexOf(n)<0&&on.call(e,n)&&(r[n]=e[n]);return r};var Pt=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var $i=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Hi(t))!yr.call(e,o)&&o!==r&&gr(e,o,{get:()=>t[o],enumerable:!(n=ki(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Ri(Pi(e)):{},$i(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var cn=Pt((xr,sn)=>{(function(e,t){typeof xr=="object"&&typeof sn!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(T){return!!(T&&T!==document&&T.nodeName!=="HTML"&&T.nodeName!=="BODY"&&"classList"in T&&"contains"in T.classList)}function c(T){var Qe=T.type,De=T.tagName;return!!(De==="INPUT"&&s[Qe]&&!T.readOnly||De==="TEXTAREA"&&!T.readOnly||T.isContentEditable)}function f(T){T.classList.contains("focus-visible")||(T.classList.add("focus-visible"),T.setAttribute("data-focus-visible-added",""))}function u(T){T.hasAttribute("data-focus-visible-added")&&(T.classList.remove("focus-visible"),T.removeAttribute("data-focus-visible-added"))}function p(T){T.metaKey||T.altKey||T.ctrlKey||(a(r.activeElement)&&f(r.activeElement),n=!0)}function m(T){n=!1}function d(T){a(T.target)&&(n||c(T.target))&&f(T.target)}function h(T){a(T.target)&&(T.target.classList.contains("focus-visible")||T.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(T.target))}function v(T){document.visibilityState==="hidden"&&(o&&(n=!0),G())}function G(){document.addEventListener("mousemove",N),document.addEventListener("mousedown",N),document.addEventListener("mouseup",N),document.addEventListener("pointermove",N),document.addEventListener("pointerdown",N),document.addEventListener("pointerup",N),document.addEventListener("touchmove",N),document.addEventListener("touchstart",N),document.addEventListener("touchend",N)}function oe(){document.removeEventListener("mousemove",N),document.removeEventListener("mousedown",N),document.removeEventListener("mouseup",N),document.removeEventListener("pointermove",N),document.removeEventListener("pointerdown",N),document.removeEventListener("pointerup",N),document.removeEventListener("touchmove",N),document.removeEventListener("touchstart",N),document.removeEventListener("touchend",N)}function N(T){T.target.nodeName&&T.target.nodeName.toLowerCase()==="html"||(n=!1,oe())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),G(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var fn=Pt(Er=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(f){return!1}},r=t(),n=function(f){var u={next:function(){var p=f.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(f){return encodeURIComponent(f).replace(/%20/g,"+")},i=function(f){return decodeURIComponent(String(f).replace(/\+/g," "))},s=function(){var f=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof f){var d=this;p.forEach(function(oe,N){d.append(N,oe)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),f._entries&&(f._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(c,f){typeof c!="string"&&(c=String(c)),f&&typeof f!="string"&&(f=String(f));var u=document,p;if(f&&(e.location===void 0||f!==e.location.href)){f=f.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=f,u.head.appendChild(p);try{if(p.href.indexOf(f)!==0)throw new Error(p.href)}catch(T){throw new Error("URL unable to set base "+f+" due to "+T)}}var m=u.createElement("a");m.href=c,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=c,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!f)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,G=!0,oe=this;["append","delete","set"].forEach(function(T){var Qe=h[T];h[T]=function(){Qe.apply(h,arguments),v&&(G=!1,oe.search=h.toString(),G=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var N=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==N&&(N=this.search,G&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},s=i.prototype,a=function(c){Object.defineProperty(s,c,{get:function(){return this._anchorElement[c]},set:function(f){this._anchorElement[c]=f},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(c){a(c)}),Object.defineProperty(s,"search",{get:function(){return this._anchorElement.search},set:function(c){this._anchorElement.search=c,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(s,{toString:{get:function(){var c=this;return function(){return c.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(c){this._anchorElement.href=c,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(c){this._anchorElement.pathname=c},enumerable:!0},origin:{get:function(){var c={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],f=this._anchorElement.port!=c&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(f?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(c){},enumerable:!0},username:{get:function(){return""},set:function(c){},enumerable:!0}}),i.createObjectURL=function(c){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(c){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er)});var Kr=Pt((Mt,qr)=>{/*! - * clipboard.js v2.0.11 - * https://clipboardjs.com/ - * - * Licensed MIT © Zeno Rocha - */(function(t,r){typeof Mt=="object"&&typeof qr=="object"?qr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Mt=="object"?Mt.ClipboardJS=r():t.ClipboardJS=r()})(Mt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return Ci}});var s=i(279),a=i.n(s),c=i(370),f=i.n(c),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(O){return!1}}var d=function(O){var E=p()(O);return m("cut"),E},h=d;function v(j){var O=document.documentElement.getAttribute("dir")==="rtl",E=document.createElement("textarea");E.style.fontSize="12pt",E.style.border="0",E.style.padding="0",E.style.margin="0",E.style.position="absolute",E.style[O?"right":"left"]="-9999px";var H=window.pageYOffset||document.documentElement.scrollTop;return E.style.top="".concat(H,"px"),E.setAttribute("readonly",""),E.value=j,E}var G=function(O,E){var H=v(O);E.container.appendChild(H);var I=p()(H);return m("copy"),H.remove(),I},oe=function(O){var E=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},H="";return typeof O=="string"?H=G(O,E):O instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(O==null?void 0:O.type)?H=G(O.value,E):(H=p()(O),m("copy")),H},N=oe;function T(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?T=function(E){return typeof E}:T=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},T(j)}var Qe=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},E=O.action,H=E===void 0?"copy":E,I=O.container,q=O.target,Me=O.text;if(H!=="copy"&&H!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&T(q)==="object"&&q.nodeType===1){if(H==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(H==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Me)return N(Me,{container:I});if(q)return H==="cut"?h(q):N(q,{container:I})},De=Qe;function $e(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?$e=function(E){return typeof E}:$e=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},$e(j)}function wi(j,O){if(!(j instanceof O))throw new TypeError("Cannot call a class as a function")}function rn(j,O){for(var E=0;E0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof I.action=="function"?I.action:this.defaultAction,this.target=typeof I.target=="function"?I.target:this.defaultTarget,this.text=typeof I.text=="function"?I.text:this.defaultText,this.container=$e(I.container)==="object"?I.container:document.body}},{key:"listenClick",value:function(I){var q=this;this.listener=f()(I,"click",function(Me){return q.onClick(Me)})}},{key:"onClick",value:function(I){var q=I.delegateTarget||I.currentTarget,Me=this.action(q)||"copy",kt=De({action:Me,container:this.container,target:this.target(q),text:this.text(q)});this.emit(kt?"success":"error",{action:Me,text:kt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(I){return vr("action",I)}},{key:"defaultTarget",value:function(I){var q=vr("target",I);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(I){return vr("text",I)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(I){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return N(I,q)}},{key:"cut",value:function(I){return h(I)}},{key:"isSupported",value:function(){var I=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof I=="string"?[I]:I,Me=!!document.queryCommandSupported;return q.forEach(function(kt){Me=Me&&!!document.queryCommandSupported(kt)}),Me}}]),E}(a()),Ci=Ai},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==o;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}n.exports=s},438:function(n,o,i){var s=i(828);function a(u,p,m,d,h){var v=f.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function c(u,p,m,d,h){return typeof u.addEventListener=="function"?a.apply(null,arguments):typeof m=="function"?a.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return a(v,p,m,d,h)}))}function f(u,p,m,d){return function(h){h.delegateTarget=s(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=c},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(n,o,i){var s=i(879),a=i(438);function c(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(h))throw new TypeError("Third argument must be a Function");if(s.node(m))return f(m,d,h);if(s.nodeList(m))return u(m,d,h);if(s.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function f(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return a(document.body,m,d,h)}n.exports=c},817:function(n){function o(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),f=document.createRange();f.selectNodeContents(i),c.removeAllRanges(),c.addRange(f),s=c.toString()}return s}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function f(){c.off(i,f),s.apply(a,arguments)}return f._=s,this.on(i,f,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,f=a.length;for(c;c{"use strict";/*! - * escape-html - * Copyright(c) 2012-2013 TJ Holowaychuk - * Copyright(c) 2015 Andreas Lubbe - * Copyright(c) 2015 Tiancheng "Timothy" Gu - * MIT Licensed - */var ns=/["'&<>]/;Go.exports=os;function os(e){var t=""+e,r=ns.exec(t);if(!r)return t;var n,o="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(f[0]===6||f[0]===2)){r=0;continue}if(f[0]===3&&(!i||f[1]>i[0]&&f[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],s;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||a(m,d)})})}function a(m,d){try{c(n[m](d))}catch(h){p(i[0][3],h)}}function c(m){m.value instanceof et?Promise.resolve(m.value.v).then(f,u):p(i[0][2],m)}function f(m){a("next",m)}function u(m){a("throw",m)}function p(m,d){m(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function ln(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Ee=="function"?Ee(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),o(a,c,s.done,s.value)})}}function o(i,s,a,c){Promise.resolve(c).then(function(f){i({value:f,done:a})},s)}}function C(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var It=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: -`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` - `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Ee(s),c=a.next();!c.done;c=a.next()){var f=c.value;f.remove(this)}}catch(v){t={error:v}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var u=this.initialTeardown;if(C(u))try{u()}catch(v){i=v instanceof It?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=Ee(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{mn(h)}catch(v){i=i!=null?i:[],v instanceof It?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new It(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)mn(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Sr=Ie.EMPTY;function jt(e){return e instanceof Ie||e&&"closed"in e&&C(e.remove)&&C(e.add)&&C(e.unsubscribe)}function mn(e){C(e)?e():e.unsubscribe()}var Le={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,s=o.isStopped,a=o.observers;return i||s?Sr:(this.currentObservers=null,a.push(r),new Ie(function(){n.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,s=n.isStopped;o?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,n){return new En(r,n)},t}(F);var En=function(e){ie(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Sr},t}(x);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ie(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,s=n._infiniteTimeWindow,a=n._timestampProvider,c=n._windowTime;o||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,s=o._buffer,a=s.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var s=r.actions;n!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Wt);var Tn=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Dt);var Te=new Tn(Sn);var _=new F(function(e){return e.complete()});function Vt(e){return e&&C(e.schedule)}function Cr(e){return e[e.length-1]}function Ye(e){return C(Cr(e))?e.pop():void 0}function Oe(e){return Vt(Cr(e))?e.pop():void 0}function zt(e,t){return typeof Cr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Nt(e){return C(e==null?void 0:e.then)}function qt(e){return C(e[ft])}function Kt(e){return Symbol.asyncIterator&&C(e==null?void 0:e[Symbol.asyncIterator])}function Qt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ni(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Yt=Ni();function Gt(e){return C(e==null?void 0:e[Yt])}function Bt(e){return pn(this,arguments,function(){var r,n,o,i;return $t(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,et(r.read())];case 3:return n=s.sent(),o=n.value,i=n.done,i?[4,et(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,et(o)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Jt(e){return C(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(qt(e))return qi(e);if(pt(e))return Ki(e);if(Nt(e))return Qi(e);if(Kt(e))return On(e);if(Gt(e))return Yi(e);if(Jt(e))return Gi(e)}throw Qt(e)}function qi(e){return new F(function(t){var r=e[ft]();if(C(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Ki(e){return new F(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?A(function(o,i){return e(o,i,n)}):de,ge(1),r?He(t):Vn(function(){return new Zt}))}}function zn(){for(var e=[],t=0;t=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(f){var u,p,m,d=0,h=!1,v=!1,G=function(){p==null||p.unsubscribe(),p=void 0},oe=function(){G(),u=m=void 0,h=v=!1},N=function(){var T=u;oe(),T==null||T.unsubscribe()};return y(function(T,Qe){d++,!v&&!h&&G();var De=m=m!=null?m:r();Qe.add(function(){d--,d===0&&!v&&!h&&(p=$r(N,c))}),De.subscribe(Qe),!u&&d>0&&(u=new rt({next:function($e){return De.next($e)},error:function($e){v=!0,G(),p=$r(oe,o,$e),De.error($e)},complete:function(){h=!0,G(),p=$r(oe,s),De.complete()}}),U(T).subscribe(u))})(f)}}function $r(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function z(e,t=document){let r=ce(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ce(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),V(e===_e()),B())}function Xe(e){return{x:e.offsetLeft,y:e.offsetTop}}function Qn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,Te),l(()=>Xe(e)),V(Xe(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,Te),l(()=>rr(e)),V(rr(e)))}var Gn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!Dr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),ga?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!Dr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=va.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Bn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Xn=typeof WeakMap!="undefined"?new WeakMap:new Gn,Zn=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=ya.getInstance(),n=new Aa(t,r,this);Xn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){Zn.prototype[e]=function(){var t;return(t=Xn.get(this))[e].apply(t,arguments)}});var Ca=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:Zn}(),eo=Ca;var to=new x,Ra=$(()=>k(new eo(e=>{for(let t of e)to.next(t)}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),J(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ye(e){return Ra.pipe(S(t=>t.observe(e)),g(t=>to.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var ro=new x,ka=$(()=>k(new IntersectionObserver(e=>{for(let t of e)ro.next(t)},{threshold:0}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),J(1));function sr(e){return ka.pipe(S(t=>t.observe(e)),g(t=>ro.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function no(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=he(e),o=bt(e);return r>=o.height-n.height-t}),B())}var cr={drawer:z("[data-md-toggle=drawer]"),search:z("[data-md-toggle=search]")};function oo(e){return cr[e].checked}function Ke(e,t){cr[e].checked!==t&&cr[e].click()}function Ue(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),V(t.checked))}function Ha(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Pa(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(V(!1))}function io(){let e=b(window,"keydown").pipe(A(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:oo("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),A(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!Ha(n,r)}return!0}),pe());return Pa().pipe(g(t=>t?_:e))}function le(){return new URL(location.href)}function ot(e){location.href=e.href}function ao(){return new x}function so(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)so(e,r)}function M(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)so(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function co(){return location.hash.substring(1)}function Vr(e){let t=M("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function $a(e){return L(b(window,"hashchange"),e).pipe(l(co),V(co()),A(t=>t.length>0),J(1))}function fo(e){return $a(e).pipe(l(t=>ce(`[id="${t}"]`)),A(t=>typeof t!="undefined"))}function zr(e){let t=matchMedia(e);return er(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function uo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(V(e.matches))}function Nr(e,t){return e.pipe(g(r=>r?t():_))}function ur(e,t={credentials:"same-origin"}){return ue(fetch(`${e}`,t)).pipe(fe(()=>_),g(r=>r.status!==200?Tt(()=>new Error(r.statusText)):k(r)))}function We(e,t){return ur(e,t).pipe(g(r=>r.json()),J(1))}function po(e,t){let r=new DOMParser;return ur(e,t).pipe(g(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),J(1))}function pr(e){let t=M("script",{src:e});return $(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(g(()=>Tt(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),R(()=>document.head.removeChild(t)),ge(1))))}function lo(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function mo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(lo),V(lo()))}function ho(){return{width:innerWidth,height:innerHeight}}function bo(){return b(window,"resize",{passive:!0}).pipe(l(ho),V(ho()))}function vo(){return Q([mo(),bo()]).pipe(l(([e,t])=>({offset:e,size:t})),J(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(Z("size")),o=Q([n,r]).pipe(l(()=>Xe(e)));return Q([r,t,o]).pipe(l(([{height:i},{offset:s,size:a},{x:c,y:f}])=>({offset:{x:s.x-c,y:s.y-f+i},size:a})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(s=>{let a=document.createElement("script");a.src=i,a.onload=s,document.body.appendChild(a)})),Promise.resolve())}var r=class extends EventTarget{constructor(n){super(),this.url=n,this.m=i=>{i.source===this.w&&(this.dispatchEvent(new MessageEvent("message",{data:i.data})),this.onmessage&&this.onmessage(i))},this.e=(i,s,a,c,f)=>{if(s===`${this.url}`){let u=new ErrorEvent("error",{message:i,filename:s,lineno:a,colno:c,error:f});this.dispatchEvent(u),this.onerror&&this.onerror(u)}};let o=document.createElement("iframe");o.hidden=!0,document.body.appendChild(this.iframe=o),this.w.document.open(),this.w.document.write(`

App class configuration

The configuration object is created by setting the config directives in the Koded\Framework\App constructor.

Argument Type Required Default
modules array no [] A list of DIModules for the app
middleware array no [] The app middleware stack
config object, string no '' A path to a config file, FQCN of the configuration class, or an instance of a config object
renderer string no 'start_response' Response renderer function/method

All directives are optional.

<?php
+ App instance - Koded Framework       

App class configuration

The configuration object is created by setting the config directives in the Koded\Framework\App constructor.

Argument Type Required Default
modules array no [] A list of DIModules for the app
middleware array no [] The app middleware stack
config object, string no '' A path to a config file, FQCN of the configuration class, or an instance of a config object
renderer string no 'start_response' Response renderer function/method

All directives are optional.

<?php
 
 new App(
     modules: [
@@ -49,4 +49,4 @@
 new App(renderer: MyCustomRenderer::class);
 
 new App(renderer: 'My\App\custom_renderer');
-

The purpose of this class method (or function) is to provide a custom processing of the ServerRequestInterface and ResponseInterface objects BEFORE it is finally sent to the client, for example a custom-made HTML renderer for server-side template engines, or a response streaming, etc.

\ No newline at end of file +

The purpose of this class method (or function) is to provide a custom processing of the ServerRequestInterface and ResponseInterface objects BEFORE it is finally sent to the client, for example a custom-made HTML renderer for server-side template engines, or a response streaming, etc.

\ No newline at end of file diff --git a/configure/modules/index.html b/configure/modules/index.html index 6880129..a8aefa3 100644 --- a/configure/modules/index.html +++ b/configure/modules/index.html @@ -1,4 +1,4 @@ - DI Modules - Koded Framework

DIModule interface

The purpose of this feature is to setup the DI container in your app.
+ DI Modules - Koded Framework       

DIModule interface

The purpose of this feature is to setup the DI container in your app.
 

Once your app stops being "Hello World" and assuming you are going to follow some SOLID principles (or maybe not), the application may end up with lots of interfaces and concrete implementations. All of them can be mapped in the app module.

Koded Container koded-container-status supports multiple modules, but one is sufficient for the container's configuration. Again, it is up to you how you are going to structure the application, so sometimes it makes sense to create multiple modules.

Lets say you want to provide a database for the auth backend instance. Because Koded already has one registered by default (Koded\Framework\Auth\SessionAuthBackend) you can easily configure it in your DI module.

<?php
 
 # /opt/my-app/MyAppModule.php
@@ -21,4 +21,4 @@
 # index.php
 
 new App(modules: [new MyAppModule]);
-

As you can see it's very easy to override anything in the Koded framework, including the default bindings.

\ No newline at end of file +

As you can see it's very easy to override anything in the Koded framework, including the default bindings.

\ No newline at end of file diff --git a/default-dependencies/index.html b/default-dependencies/index.html index 99bdf82..8d5094a 100644 --- a/default-dependencies/index.html +++ b/default-dependencies/index.html @@ -1 +1 @@ - Default framework dependencies - Koded Framework

Default framework dependencies

Framework DI module

It loads the following dependencies by default:

Interfaces

See implementations and shared instances.

HTTP

  • PSR-7 Psr\Http\Message\ServerRequestInterface
  • PSR-7 Psr\Http\Message\ResponseInterface
  • Koded\Framework\Request
  • Koded\Framework\Response

Authentication

  • Framework\Auth\AuthBackend

    Used in the AuthMiddleware - Framework\Auth\AuthProcessor

    Executes the authenticate() in the AuthMiddleware

Application should override the AuthBackend to provide its own authentication storage and logic (i.e. database).

The AuthProcessor parses the credentials and may be overridden to support different types of auth mechanisms (i.e. JWT)

Utility

  • PSR-16 Psr\SimpleCache\CacheInterface
  • PSR-3 Psr\Log\LoggerInterface
  • Koded\Stdlib\Configuration

Middleware

Koded uses PSR-15 middlewares. More details on the middleware page

Bindings

Anything that's bound by the default DI module can be replaced with custom implementation(s) in your application DI module(s). The default classes are from the Koded libraries.

\ No newline at end of file + Default framework dependencies - Koded Framework

Default framework dependencies

Framework DI module

It loads the following dependencies by default:

Interfaces

See implementations and shared instances.

HTTP

  • PSR-7 Psr\Http\Message\ServerRequestInterface
  • PSR-7 Psr\Http\Message\ResponseInterface
  • Koded\Framework\Request
  • Koded\Framework\Response

Authentication

  • Framework\Auth\AuthBackend

    Used in the AuthMiddleware - Framework\Auth\AuthProcessor

    Executes the authenticate() in the AuthMiddleware

Application should override the AuthBackend to provide its own authentication storage and logic (i.e. database).

The AuthProcessor parses the credentials and may be overridden to support different types of auth mechanisms (i.e. JWT)

Utility

  • PSR-16 Psr\SimpleCache\CacheInterface
  • PSR-3 Psr\Log\LoggerInterface
  • Koded\Stdlib\Configuration

Middleware

Koded uses PSR-15 middlewares. More details on the middleware page

Bindings

Anything that's bound by the default DI module can be replaced with custom implementation(s) in your application DI module(s). The default classes are from the Koded libraries.

\ No newline at end of file diff --git a/exception-handlers/index.html b/exception-handlers/index.html index e1d5ff2..b5bf373 100644 --- a/exception-handlers/index.html +++ b/exception-handlers/index.html @@ -1,4 +1,4 @@ - Exception handling - Koded Framework

Exception handling

By default, the following exceptions are caught and handled

  • \Exception
  • \Error
  • Koded\Http\HTTPError

The error response payload follows the RFC-7807 specification.

If you wish to capture specific exception types and/or return custom error messages, create your own class and register it with the method App::withErrorHandler() to convert the exceptions into the desired HTTP responses.

handle() method signature

handle(request, response, exception): void

If app does not use 3rd party PSR-7 library, then Koded will use its own implementations from the Koded HTTP library.

<?php
+ Exception handling - Koded Framework       

Exception handling

By default, the following exceptions are caught and handled

  • \Exception
  • \Error
  • Koded\Http\HTTPError

The error response payload follows the RFC-7807 specification.

If you wish to capture specific exception types and/or return custom error messages, create your own class and register it with the method App::withErrorHandler() to convert the exceptions into the desired HTTP responses.

handle() method signature

handle(request, response, exception): void

If app does not use 3rd party PSR-7 library, then Koded will use its own implementations from the Koded HTTP library.

<?php
 
 # CustomExceptionHandler.php
 
@@ -51,4 +51,4 @@
     ->withoutErrorHandler(AnotherCustomExceptionHandler::class)
     ->route(/* ... */)
 )();
-
\ No newline at end of file +
\ No newline at end of file diff --git a/get-started/index.html b/get-started/index.html index 4316c10..ad17ef5 100644 --- a/get-started/index.html +++ b/get-started/index.html @@ -1,4 +1,4 @@ - Get Started - Koded Framework

Requirements

php-version license-status

Installation

Using composer

composer require koded/framework
+ Get Started - Koded Framework       

Requirements

php-version license-status

Installation

Using composer

composer require koded/framework
 

No composer?

If you don't have composer please follow the download instructions how to install it on your OS.

For manual install on Linux you may run this command:

curl https://getcomposer.org/download/latest-stable/composer.phar -o /usr/local/bin/composer \
     && chmod +x /usr/local/bin/composer
 

App basics

Files structure

It is up to you how you're going to structure your project. A simple and clear structuring is essential for great development, on a long run (or short too), but that is up to developer needs, or based on the app complexity, team decisions, or various other reasons.

Let's look at something that is good in general as a startup,

app/
@@ -50,4 +50,4 @@
         return $response;
     })
 )();
-

Now point your browser to app address. It should print "Work in Progress..." with status code 200.

From here on add more routes and resources to your API, but keep in mind that using closures as resources is NOT the recommended way to build the application. For more on this please follow the documentation further.

\ No newline at end of file +

Now point your browser to app address. It should print "Work in Progress..." with status code 200.

From here on add more routes and resources to your API, but keep in mind that using closures as resources is NOT the recommended way to build the application. For more on this please follow the documentation further.

\ No newline at end of file diff --git a/index.html b/index.html index 4b10d5e..2d76e8d 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - Koded Framework

The Koded Web Framework

Why?

Sometimes we want a prototype, or quick/simple operations, or solutions for building fast web APIs and app backends. And having a full-blown frameworks will eventually weight you down with tons of unnecessary abstractions and dependencies.

Koded offers small footprint with a clean design that follows the HTTP and ReST architectural style. It tends to be very fast and easy to work with.

Minimum application

<?php
+ Koded Framework       

The Koded Web Framework

Why?

Sometimes we want a prototype, or quick/simple operations, or solutions for building fast web APIs and app backends. And having a full-blown frameworks will eventually weight you down with tons of unnecessary abstractions and dependencies.

Koded offers small footprint with a clean design that follows the HTTP and ReST architectural style. It tends to be very fast and easy to work with.

Minimum application

<?php
 use Koded\Framework\App;
 use Koded\Http\ServerResponse as R;
 
@@ -7,4 +7,4 @@
 ((new App)
     ->route('/', fn() => new R('Hello'))
 )();
-

This fits in a tweet.

Resource Method Representation

Koded Framework follows the RMR web architectural pattern, but the developer is free to use whatever it prefers and does not enforce any programming patterns in the application.

However, it is strongly recommended adopting and using RMR because it is aligned nicely with the HTTP ReST concepts. After all, web apps needs fundamental directions and principles, not "perfect" solutions so RMR fits here very well.

An object in the RESTful system identified by a URL that exposes methods that corresponds to the standard HTTP methods (GET, POST, PUT, etc) i.e. a business object (entity).

The HTTP request method that corresponds a Resource method that is executed by the request/URL, which returns a Representation for that resource.

Provides a Resource to clients in a readable format (JSON, XML, PDF, HTML, etc). It is the payload of the response object (processed by the HTTP Method that is sent back to the client).

\ No newline at end of file +

This fits in a tweet.

Resource Method Representation

Koded Framework follows the RMR web architectural pattern, but the developer is free to use whatever it prefers and does not enforce any programming patterns in the application.

However, it is strongly recommended adopting and using RMR because it is aligned nicely with the HTTP ReST concepts. After all, web apps needs fundamental directions and principles, not "perfect" solutions so RMR fits here very well.

An object in the RESTful system identified by a URL that exposes methods that corresponds to the standard HTTP methods (GET, POST, PUT, etc) i.e. a business object (entity).

The HTTP request method that corresponds a Resource method that is executed by the request/URL, which returns a Representation for that resource.

Provides a Resource to clients in a readable format (JSON, XML, PDF, HTML, etc). It is the payload of the response object (processed by the HTTP Method that is sent back to the client).

\ No newline at end of file diff --git a/middleware/builtin/index.html b/middleware/builtin/index.html index eed33bd..8c3dd5b 100644 --- a/middleware/builtin/index.html +++ b/middleware/builtin/index.html @@ -1 +1 @@ - Built-in - Koded Framework

Built-in middlewares

Out of the box Koded offers a curated list with common middleware functionality. The developers are encouraged to create their own (or use existing) PSR-15 middlewares to enhance the application features.

CorsMiddleware

(loaded by default)
Cross-Origin Request Sharing support for your JavaScript applications.

Please check the CORS middleware page to customize the behaviour of this middleware class.

GzipMiddleware

(loaded by default)
Compresses the response payload with gzencode.

AuthMiddleware

A basic mechanism to run the auth logic. Supports the Authorization header.

CallableMiddleware

Used internally by the framework to support closures for the responders, or any PHP callable resource.

HSTSMiddleware

HTTP Strict Transport Security (HSTS). Redirects the request URI from HTTP to HTTPS.

NoCacheMiddleware

Adds no-cache response headers.

XPoweredByMiddleware

Sets the X-Powered-By response header.

\ No newline at end of file + Built-in - Koded Framework

Built-in middlewares

Out of the box Koded offers a curated list with common middleware functionality. The developers are encouraged to create their own (or use existing) PSR-15 middlewares to enhance the application features.

CorsMiddleware

(loaded by default)
Cross-Origin Request Sharing support for your JavaScript applications.

Please check the CORS middleware page to customize the behaviour of this middleware class.

GzipMiddleware

(loaded by default)
Compresses the response payload with gzencode.

AuthMiddleware

A basic mechanism to run the auth logic. Supports the Authorization header.

CallableMiddleware

Used internally by the framework to support closures for the responders, or any PHP callable resource.

HSTSMiddleware

HTTP Strict Transport Security (HSTS). Redirects the request URI from HTTP to HTTPS.

NoCacheMiddleware

Adds no-cache response headers.

XPoweredByMiddleware

Sets the X-Powered-By response header.

\ No newline at end of file diff --git a/middleware/cors/index.html b/middleware/cors/index.html index d3d663e..02416e1 100644 --- a/middleware/cors/index.html +++ b/middleware/cors/index.html @@ -1,4 +1,4 @@ - Cors - Koded Framework

CORS middleware

Use the configuration to set the desired behavior of your application CORS middleware.

# config.php
+ CORS middleware - Koded Framework       

CORS middleware

Use the configuration to set the desired behavior of your application CORS middleware.

# config.php
 
 <?php
 
@@ -11,4 +11,4 @@
   'cors.disable' => false,
   ...
 ];
-

Configuration directives

CORS config directives will set the global behavior of the middleware. For example, if cors.methods has a value of GET, POST then only these two methods are allowed for all CORS requests. The same applies to all settings.

Directive Type Default
cors.origin string (empty) The server origin address as in schema://host:port (if the port is not a standard port)
cors.methods string (empty) Comma-separated list of supported HTTP methods
cors.headers string (empty) Comma-separated list of supported headers
cors.expose string Authorization, X-Forwarded-With Comma-separated list of exposed headers
cors.maxAge int 0 max-age header for OPTIONS request (before the actual request is called)
cors.disable bool false This directive will completely disable the middleware
\ No newline at end of file +

Configuration directives

CORS config directives will set the global behavior of the middleware. For example, if cors.methods has a value of GET, POST then only these two methods are allowed for all CORS requests. The same applies to all settings.

Directive Type Default
cors.origin string (empty) The server origin address as in schema://host:port (if the port is not a standard port)
cors.methods string (empty) Comma-separated list of supported HTTP methods
cors.headers string (empty) Comma-separated list of supported headers
cors.expose string Authorization, X-Forwarded-With Comma-separated list of exposed headers
cors.maxAge int 0 max-age header for OPTIONS request (before the actual request is called)
cors.disable bool false This directive will completely disable the middleware
\ No newline at end of file diff --git a/middleware/psr-15/index.html b/middleware/psr-15/index.html index 34b8666..cc7c9e2 100644 --- a/middleware/psr-15/index.html +++ b/middleware/psr-15/index.html @@ -1,4 +1,4 @@ - PSR-15 - Koded Framework

Middleware stack

The PSR-15 method signature for processing the incoming server request is very simple:

process(request, handler): response
+ PSR-15 - Koded Framework       

Middleware stack

The PSR-15 method signature for processing the incoming server request is very simple:

process(request, handler): response
 

request is the server side incoming request object, handler is the object that receive the request, do some work and returns a response.

It is important to keep in mind when creating a custom middleware class for your app, where the processing of the ServerRequestInterface and ResponseInterface instances will take place.

Workflow

It is an "onion layer".

request
     MW1 (request)
         MW2 (request)
@@ -55,4 +55,4 @@
             ->withHeader('X-Req-ID', $request->getAttribute('req-id'));
     }
 }
-
\ No newline at end of file +
\ No newline at end of file diff --git a/routing/caching/index.html b/routing/caching/index.html index b770397..55b6718 100644 --- a/routing/caching/index.html +++ b/routing/caching/index.html @@ -1 +1 @@ - Routes caching - Koded Framework

Routes caching

The caching for routes is the same as for anything else and it is a "global" application caching.

Setup cache

Caching configuration is done by setting up the configuration for the App instance.

Cache invalidation

At this time there are no tools or a mechanism to invalidate the router cache.

\ No newline at end of file + Routes caching - Koded Framework

Routes caching

The caching for routes is the same as for anything else and it is a "global" application caching.

Setup cache

Caching configuration is done by setting up the configuration for the App instance.

Cache invalidation

At this time there are no tools or a mechanism to invalidate the router cache.

\ No newline at end of file diff --git a/routing/index.html b/routing/index.html index a36b72f..7492270 100644 --- a/routing/index.html +++ b/routing/index.html @@ -1,4 +1,4 @@ - Basics - Koded Framework

The Router

URI routing in Koded is easy. When dealing with routes keep in mind the following

  • the router does not care about the HTTP method for the endpoint (this is resolved internally by the App instance)
  • the HTTP method for the route (endpoint) is implemented in the resource object (or function)
  • this framework does not offer a "Route" object
  • parsed URI parameters are stored in the Psr\Http\Message\ServerRequestInterface@attributes as name => value pairs
  • all routes are cached for better performance

Routes are implemented in your execution script(s), ie. index.php, or any other PHP file that is part of the HTTP request/URI.

route() method

<?php
+ Basics - Koded Framework       

The Router

URI routing in Koded is easy. When dealing with routes keep in mind the following

  • the router does not care about the HTTP method for the endpoint (this is resolved internally by the App instance)
  • the HTTP method for the route (endpoint) is implemented in the resource object (or function)
  • this framework does not offer a "Route" object
  • parsed URI parameters are stored in the Psr\Http\Message\ServerRequestInterface@attributes as name => value pairs
  • all routes are cached for better performance

Routes are implemented in your execution script(s), ie. index.php, or any other PHP file that is part of the HTTP request/URI.

route() method

<?php
 
 public function route(
   string $uriTemplate,
@@ -44,4 +44,4 @@
  * /v2/add
  * /v2/delete
  */
-

$routes

is a list of routes arrays where the array elements must match the route() signature.

$middleware

This list of middlewares is an additional list that is merged with each route in the $routes list and differ from the $middleware list in the route method in this manner.

\ No newline at end of file +

$routes

is a list of routes arrays where the array elements must match the route() signature.

$middleware

This list of middlewares is an additional list that is merged with each route in the $routes list and differ from the $middleware list in the route method in this manner.

\ No newline at end of file diff --git a/routing/parameters/index.html b/routing/parameters/index.html index 4220432..e9d44bc 100644 --- a/routing/parameters/index.html +++ b/routing/parameters/index.html @@ -1,4 +1,4 @@ - URI parameters - Koded Framework

URI parameters

The URI parameters are named variables wrapped with curly braces {} in the URI template string.

  ->route('/{param1}/{param2}/{param3}/...')
+ URI parameters - Koded Framework       

URI parameters

The URI parameters are named variables wrapped with curly braces {} in the URI template string.

  ->route('/{param1}/{param2}/{param3}/...')
 

If the URI matches the route template, the values of the parameters will be stored in the Psr\Http\Message\ServerRequestInterface@attributes and available through the request object with the getAttribute() method

<?php
 
   public function put(ServerRequestInterface $request) {
@@ -32,4 +32,4 @@
 
   // ie. "/123" 
   // 'something' => 123
-

Keep it simple

Try to not overcomplicate your regular expressions, in most cases a simple type is sufficient.

\ No newline at end of file +

Keep it simple

Try to not overcomplicate your regular expressions, in most cases a simple type is sufficient.

\ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json index 5b44923..3aefe4f 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"The Koded Web Framework","text":""},{"location":"#why","title":"Why?","text":"

Sometimes we want a prototype, or quick/simple operations, or solutions for building fast web APIs and app backends. And having a full-blown frameworks will eventually weight you down with tons of unnecessary abstractions and dependencies.

Koded offers small footprint with a clean design that follows the HTTP and ReST architectural style. It tends to be very fast and easy to work with.

"},{"location":"#minimum-application","title":"Minimum application","text":"
<?php\nuse Koded\\Framework\\App;\nuse Koded\\Http\\ServerResponse as R;\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\n((new App)\n    ->route('/', fn() => new R('Hello'))\n)();\n

This fits in a tweet.

"},{"location":"#resource-method-representation","title":"Resource Method Representation","text":"

Koded Framework follows the RMR web architectural pattern, but the developer is free to use whatever it prefers and does not enforce any programming patterns in the application.

However, it is strongly recommended adopting and using RMR because it is aligned nicely with the HTTP ReST concepts. After all, web apps needs fundamental directions and principles, not \"perfect\" solutions so RMR fits here very well.

Resource

An object in the RESTful system identified by a URL that exposes methods that corresponds to the standard HTTP methods (GET, POST, PUT, etc) i.e. a business object (entity).

Method

The HTTP request method that corresponds a Resource method that is executed by the request/URL, which returns a Representation for that resource.

Representation

Provides a Resource to clients in a readable format (JSON, XML, PDF, HTML, etc). It is the payload of the response object (processed by the HTTP Method that is sent back to the client).

"},{"location":"default-dependencies/","title":"Default framework dependencies","text":""},{"location":"default-dependencies/#framework-di-module","title":"Framework DI module","text":"

It loads the following dependencies by default:

"},{"location":"default-dependencies/#interfaces","title":"Interfaces","text":"

See implementations and shared instances.

"},{"location":"default-dependencies/#http","title":"HTTP","text":"
  • PSR-7 Psr\\Http\\Message\\ServerRequestInterface
  • PSR-7 Psr\\Http\\Message\\ResponseInterface
  • Koded\\Framework\\Request
  • Koded\\Framework\\Response
"},{"location":"default-dependencies/#authentication","title":"Authentication","text":"
  • Framework\\Auth\\AuthBackend

    Used in the AuthMiddleware - Framework\\Auth\\AuthProcessor

    Executes the authenticate() in the AuthMiddleware

Application should override the AuthBackend to provide its own authentication storage and logic (i.e. database).

The AuthProcessor parses the credentials and may be overridden to support different types of auth mechanisms (i.e. JWT)

"},{"location":"default-dependencies/#utility","title":"Utility","text":"
  • PSR-16 Psr\\SimpleCache\\CacheInterface
  • PSR-3 Psr\\Log\\LoggerInterface
  • Koded\\Stdlib\\Configuration
"},{"location":"default-dependencies/#middleware","title":"Middleware","text":"

Koded uses PSR-15 middlewares. More details on the middleware page

"},{"location":"default-dependencies/#bindings","title":"Bindings","text":"

Anything that's bound by the default DI module can be replaced with custom implementation(s) in your application DI module(s). The default classes are from the Koded libraries.

"},{"location":"exception-handlers/","title":"Exception handling","text":"

By default, the following exceptions are caught and handled

  • \\Exception
  • \\Error
  • Koded\\Http\\HTTPError

The error response payload follows the RFC-7807 specification.

If you wish to capture specific exception types and/or return custom error messages, create your own class and register it with the method App::withErrorHandler() to convert the exceptions into the desired HTTP responses.

handle() method signature

handle(request, response, exception): void

If app does not use 3rd party PSR-7 library, then Koded will use its own implementations from the Koded HTTP library.

<?php\n\n# CustomExceptionHandler.php\n\nuse Koded\\Framework\\App;\nuse Koded\\Http\\HTTPError;\nuse Koded\\Http\\Interfaces\\{Request, Response};\n\nclass CustomExceptionHandler {\n\n    public static function handle(\n        Request $request,\n        Response $response,\n        HTTPError $exception): void\n    {\n        // do something with $exception\n        // i.e. re-format the error message and set it in the response\n    }\n}\n
"},{"location":"exception-handlers/#register-handler","title":"Register handler","text":"

Register before routes

Order matters. It is required to register the custom exception handlers BEFORE the routes, otherwise they won't be handled if the request/route has been resolved.

<?php\n\n# index.php\n\n((new App)\n->withErrorHandler(CustomExceptionHandler::class)\n->route(/* ... */)\n)();\n

This implementation is using a PSR-7 compatible library and a standard PHP Throwable class.

<?php\n\nuse Psr\\Http\\Message\\{ServerRequestInterface, ResponseInterface};\n\nclass PDOExceptionHandler {\n\n    public static function handle(\n        ServerRequestInterface $request,\n        ResponseInterface $response,\n        \\Throwable $exception): void\n    {\n        if ($exception instanceof \\PDOException) {\n            // do something about it\n        }\n    }\n}\n
"},{"location":"exception-handlers/#unregister-handler","title":"Unregister handler","text":"

Use App::withoutErrorHandler() method to remove the error handler.

Unregister after custom handlers

Order matters. Unregister the exception handlers AFTER the custom registration, otherwise they won't be removed.

<?php\n\n# index.php\n\n((new App)\n    ->withErrorHandler(SomeCustomErrorHandler::class)\n\n->withoutErrorHandler(AnotherCustomExceptionHandler::class)\n->route(/* ... */)\n)();\n
"},{"location":"get-started/","title":"Requirements","text":""},{"location":"get-started/#installation","title":"Installation","text":""},{"location":"get-started/#using-composer","title":"Using composer","text":"
composer require koded/framework\n

No composer?

If you don't have composer please follow the download instructions how to install it on your OS.

For manual install on Linux you may run this command:

curl https://getcomposer.org/download/latest-stable/composer.phar -o /usr/local/bin/composer \\\n&& chmod +x /usr/local/bin/composer\n
"},{"location":"get-started/#app-basics","title":"App basics","text":""},{"location":"get-started/#files-structure","title":"Files structure","text":"

It is up to you how you're going to structure your project. A simple and clear structuring is essential for great development, on a long run (or short too), but that is up to developer needs, or based on the app complexity, team decisions, or various other reasons.

Let's look at something that is good in general as a startup,

app/\n    .env\nhtml/\n    .htaccess\n    index.php\nvendor/\ncomposer.json\n

Everything regarding your application goes into the app/ folder. This is an example, app is not a requirement and it can be anything you want.

Protect your code!

It is important to keep everything outside the html/ folder (app/, vendor/ or anything that is app related and may expose the code). Make sure the app code is not accessible from the outside.

"},{"location":"get-started/#composerjson","title":"composer.json","text":"

A composer.json scaffold for your project. Run composer update every time a new class is added, or use psr-4 in autoload section while you develop the app, whatever you prefer most.

{\n\"require\": {\n\"koded/framework\": \"^1\"\n},\n\"autoload\": {\n\"classmap\": [\n\"app\"\n],\n\"exclude-from-classmap\": [\n\"html\"\n]\n},\n\"config\": {\n\"optimize-autoloader\": true\n}\n}\n
"},{"location":"get-started/#docker-quick-example","title":"Docker (quick example)","text":"

You can jumpstart the development with docker and docker-compose with the above app file structure.

# docker-compose.yaml\n\nversion: '3'\n\nservices:\nphp:\nimage: php:8-apache\nports:\n- 8080:80\nvolumes:\n- .:/var/www\n

Adjust the volumes, or the host port if it's already taken. Run docker-compose up -d and open your browser at 127.0.0.1:8080

"},{"location":"get-started/#app-entry-point","title":"App entry point","text":"

Create the \"entry script\" for all HTTP requests. There we create an instance of App and define the URI routes.

# /var/www/html/index.php\n\n<?php\n\nuse Koded\\Framework\\App;\nuse Koded\\Http\\Interfaces\\Response;\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\n((new App)\n    ->route('/', function(Response $response): Response {\n        $response->getBody()->write('Work In Progress...');\n        return $response;\n    })\n)();\n

Now point your browser to app address. It should print \"Work in Progress...\" with status code 200.

From here on add more routes and resources to your API, but keep in mind that using closures as resources is NOT the recommended way to build the application. For more on this please follow the documentation further.

"},{"location":"shared-instances/","title":"Shared instances","text":"spec interface default implementation PSR-3 Psr\\Log\\LoggerInterface Koded\\Logging\\Log PSR-16 Psr\\SimpleCache\\CacheInterface Koded\\Caching\\Client\\MemoryClient Koded\\Stdlib\\Configuration Koded\\Stdlib\\Config N/A Koded\\Framework\\Router"},{"location":"shared-instances/#default-bindings","title":"Default bindings","text":"spec interface default implementation PSR-7 Psr\\Http\\Message\\ServerRequestInterface Koded\\Http\\ServerRequest PSR-7 Psr\\Http\\Message\\ResponseInterface Koded\\Http\\ServerResponse PSR-7 Koded\\Http\\Interfaces\\Request Koded\\Http\\ServerRequest PSR-7 Koded\\Http\\Interfaces\\Response Koded\\Http\\ServerResponse Koded\\Framework\\Auth\\AuthBackend Koded\\Framework\\Auth\\SessionAuthBackend Koded\\Framework\\Auth\\AuthProcessor Koded\\Framework\\Auth\\TokenAuthProcessor"},{"location":"configure/","title":"App class configuration","text":"

The configuration object is created by setting the config directives in the Koded\\Framework\\App constructor.

Argument Type Required Default modules array no [] A list of DIModules for the app middleware array no [] The app middleware stack config object, string no '' A path to a config file, FQCN of the configuration class, or an instance of a config object renderer string no 'start_response' Response renderer function/method

All directives are optional.

<?php\n\nnew App(\n    modules: [\n        // a list of DIModule implementation(s)\n    ],\n    middleware: [\n        // a list of PSR-15 middleware classes (global)\n    ],\n    config: __DIR__ . '/path/to/my/config.php', \n    // or\n    config: '.env', \n    // or\n    config: new MyConfig(),\n\n    renderer: MyRenderer::class,\n    // or\n    renderer: 'my_renderer_function'\n);\n
"},{"location":"configure/#constructor-arguments","title":"Constructor arguments","text":""},{"location":"configure/#modules","title":"modules","text":"

optional

This argument accepts a list of DIModule instances that configures the Dependency Injection Container for your app. See more about DI modules.

Example:

[\n    new My\\App\\Module(),\n]\n

"},{"location":"configure/#middleware","title":"middleware","text":"

optional

A list of PSR-15 middleware classes for your application.

Example:

[\n    // ... your middleware classes\n    My\\App\\Middleware\\CustomMiddleware::class,\n    new My\\App\\Middleware\\Other(),\n]\n

Check the PSR-15 middleware stack page for details how Koded framework implements this functionality.

"},{"location":"configure/#config","title":"config","text":"

optional

Configuration values for your application. It supports

  • .php file that returns an array, or
  • .env file, or
  • instance of Koded\\Stdlib\\Configuration object.

Examples:

<?php\n\n# conf.php\nreturn [\n    'key' => 'value',\n];\n\n# /var/www/public/index.php\nnew App(config: '/path/to/conf.php');\n
# .env is always loaded (if exist)\nkey=value\n
<?php\nuse Koded\\Framework\\App;\nuse Koded\\Stdlib\\{Config, Immutable}\n\n$config = new Config('/path/to/app/dir', new Immutable([\n    'key' => 'value',\n]));\n\nnew App(config: $config);\n

.env support

Make sure .env file is not accessible from the outside.

"},{"location":"configure/#renderer","title":"renderer","text":"

optional

A custom renderer for the processed ResponseInterface object. This method/function is executed by the DI container, meaning the depencencies can be anything that the container is able to resolve.

default 'start_response'

<?php\nnew App(renderer: MyCustomRenderer::class);\n\nnew App(renderer: 'My\\App\\custom_renderer');\n

The purpose of this class method (or function) is to provide a custom processing of the ServerRequestInterface and ResponseInterface objects BEFORE it is finally sent to the client, for example a custom-made HTML renderer for server-side template engines, or a response streaming, etc.

"},{"location":"configure/modules/","title":"DIModule interface","text":"
The purpose of this feature is to setup the DI container in your app.\n

Once your app stops being \"Hello World\" and assuming you are going to follow some SOLID principles (or maybe not), the application may end up with lots of interfaces and concrete implementations. All of them can be mapped in the app module.

Koded Container supports multiple modules, but one is sufficient for the container's configuration. Again, it is up to you how you are going to structure the application, so sometimes it makes sense to create multiple modules.

Lets say you want to provide a database for the auth backend instance. Because Koded already has one registered by default (Koded\\Framework\\Auth\\SessionAuthBackend) you can easily configure it in your DI module.

<?php\n\n# /opt/my-app/MyAppModule.php\n\nnamespace My\\App;\n\nuse Koded\\{DIContainer, DIModule};\nuse Koded\\Framework\\Auth\\AuthBackend;\nuse My\\App\\Auth\\DatabaseAuth;\n\nclass MyAppModule implements DIModule {\n\n    public function configure(DIContainer $container): void\n    {\n        $container->bind(AuthBackend::class, DatabaseAuth::class);\n    }\n}\n

Now register the module in the App constructor

<?php\n\n# index.php\n\nnew App(modules: [new MyAppModule]);\n

As you can see it's very easy to override anything in the Koded framework, including the default bindings.

"},{"location":"middleware/builtin/","title":"Built-in middlewares","text":"

Out of the box Koded offers a curated list with common middleware functionality. The developers are encouraged to create their own (or use existing) PSR-15 middlewares to enhance the application features.

"},{"location":"middleware/builtin/#corsmiddleware","title":"CorsMiddleware","text":"

(loaded by default) Cross-Origin Request Sharing support for your JavaScript applications.

Please check the CORS middleware page to customize the behaviour of this middleware class.

"},{"location":"middleware/builtin/#gzipmiddleware","title":"GzipMiddleware","text":"

(loaded by default) Compresses the response payload with gzencode.

"},{"location":"middleware/builtin/#authmiddleware","title":"AuthMiddleware","text":"

A basic mechanism to run the auth logic. Supports the Authorization header.

"},{"location":"middleware/builtin/#callablemiddleware","title":"CallableMiddleware","text":"

Used internally by the framework to support closures for the responders, or any PHP callable resource.

"},{"location":"middleware/builtin/#hstsmiddleware","title":"HSTSMiddleware","text":"

HTTP Strict Transport Security (HSTS). Redirects the request URI from HTTP to HTTPS.

"},{"location":"middleware/builtin/#nocachemiddleware","title":"NoCacheMiddleware","text":"

Adds no-cache response headers.

"},{"location":"middleware/builtin/#xpoweredbymiddleware","title":"XPoweredByMiddleware","text":"

Sets the X-Powered-By response header.

"},{"location":"middleware/cors/","title":"CORS middleware","text":"

Use the configuration to set the desired behavior of your application CORS middleware.

# config.php\n\n<?php\n\nreturn [\n  'cors.origin'  => '',\n  'cors.methods' => '',\n  'cors.headers' => '',\n  'cors.expose'  => 'Authorization, X-Forwarded-With',\n  'cors.maxAge'  => 0,\n  'cors.disable' => false,\n  ...\n];\n
"},{"location":"middleware/cors/#configuration-directives","title":"Configuration directives","text":"

CORS config directives will set the global behavior of the middleware. For example, if cors.methods has a value of GET, POST then only these two methods are allowed for all CORS requests. The same applies to all settings.

Directive Type Default cors.origin string (empty) The server origin address as in schema://host:port (if the port is not a standard port) cors.methods string (empty) Comma-separated list of supported HTTP methods cors.headers string (empty) Comma-separated list of supported headers cors.expose string Authorization, X-Forwarded-With Comma-separated list of exposed headers cors.maxAge int 0 max-age header for OPTIONS request (before the actual request is called) cors.disable bool false This directive will completely disable the middleware"},{"location":"middleware/psr-15/","title":"Middleware stack","text":"

The PSR-15 method signature for processing the incoming server request is very simple:

process(request, handler): response\n

request is the server side incoming request object, handler is the object that receive the request, do some work and returns a response.

It is important to keep in mind when creating a custom middleware class for your app, where the processing of the ServerRequestInterface and ResponseInterface instances will take place.

"},{"location":"middleware/psr-15/#workflow","title":"Workflow","text":"

It is an \"onion layer\".

request\n    MW1 (request)\n        MW2 (request)\n            MW3 (request)\n                    ((RESOURCE))\n            MW3 (response)\n        MW2 (response)\n    MW1 (response)\nresponse\n
  1. when request object (ServerRequestInterface) is passed into the first middleware in the stack, it propagates forward through all middleware classes by modifying the instance,

  2. up to the point where the middleware classes are exhausted, then request object enters the Resource

  3. at this point the request object has been modified by all middleware classes in the stack

  4. once the Resource object return the ResponseInterface instance, it continue to propagate backwards through the middleware stack, now modifying the response object

  5. and finally response exits the middleware stack, to be processed by the Koded renderer and sent back to the caller (ex. browser)

"},{"location":"middleware/psr-15/#example","title":"Example","text":"
<?php\n\nnamespace My\\App\\Middleware;\n\nuse Psr\\Http\\Message\\{ResponseInterface, ServerRequestInterface};\nuse Psr\\Http\\Server\\{MiddlewareInterface, RequestHandlerInterface};\n\nclass MyMiddleware implements MiddlewareInterface {\n\n    public function process(\n        ServerRequestInterface $request, \n        RequestHandlerInterface $handler): ResponseInterface\n    {\n        // --> Everything here is about the $request object\n\n        $response = $handler->handle($request);\n\n        // <-- Everything here is about the $response object\n\n        return $response;\n    }\n}\n

Lets set a random string for the request and pass it in the response header:

<?php\n\nnamespace My\\App\\Middleware;\n\nuse Psr\\Http\\Message\\{ResponseInterface, ServerRequestInterface};\nuse Psr\\Http\\Server\\{MiddlewareInterface, RequestHandlerInterface};\n\nclass RequestIdMiddleware implements MiddlewareInterface {\n\n    public function process(\n        ServerRequestInterface $request, \n        RequestHandlerInterface $handler): ResponseInterface\n    {\n        $request = $request\n            ->withAttribute('req-id', bin2hex(random_bytes(8)));\n\n        // [NOTE]: the next middleware (or the resource object)\n        // will have the ID in the request \"attributes\" property \n\n        $response = $handler->handle($request);\n\n        return $response\n            ->withHeader('X-Req-ID', $request->getAttribute('req-id'));\n    }\n}\n
"},{"location":"routing/","title":"The Router","text":"

URI routing in Koded is easy. When dealing with routes keep in mind the following

  • the router does not care about the HTTP method for the endpoint (this is resolved internally by the App instance)
  • the HTTP method for the route (endpoint) is implemented in the resource object (or function)
  • this framework does not offer a \"Route\" object
  • parsed URI parameters are stored in the Psr\\Http\\Message\\ServerRequestInterface@attributes as name => value pairs
  • all routes are cached for better performance

Routes are implemented in your execution script(s), ie. index.php, or any other PHP file that is part of the HTTP request/URI.

"},{"location":"routing/#route-method","title":"route() method","text":"
<?php\n\npublic function route(\n  string $uriTemplate,\n  object|string $resource,\n  array $middleware = [],\n  bool $explicit = false): App;\n
"},{"location":"routing/#uritemplate","title":"$uriTemplate","text":"

REQUIRED - a route string that should match the request URI.

See URI parameters how to deal with named variables in the URI template.

The parameters are optional, you can normally set direct links, ie.

<?php\n$app->route('/about', About::class);\n
"},{"location":"routing/#resource","title":"$resource","text":"

REQUIRED - your resolver class (aka resource) where the matching HTTP methods are implemented.

This argument can be a FQCN, an instance of a class, a \\Closure, or PHP callable.

<?php\n\n$app\n    ->route('/one', ResourceOne::class)\n    ->route('/two', new ResourceTwo)\n    ->route('/three', function(Request $req, Response $res): Response {\n        // ...\n    })\n    ->route('/four', 'ResourceFour::methodName')\n    ->route('/five', 'some_function')\n
"},{"location":"routing/#middleware","title":"$middleware","text":"

is a list of middleware classes/objects for this particular route.

This argument solves the need to attach any functionalities with middlewares to the route. A good example is to provide an AuthMiddleware to protect the route(s) with authentication/authorization logic:

<?php\n$app->route('/protected', Resource::class, [AuthMiddleware::class]);\n
"},{"location":"routing/#explicit","title":"$explicit","text":"

if set to TRUE it will override the global middleware stack (App::$middleware) and use only the middlewares in the $middleware list.

In this example no middleware runs for this route, because $explicit = true and the $middleware = [] is empty:

<?php\n$app->route('/', Resource::class, [], true);\n
"},{"location":"routing/#group-method","title":"group() method","text":"

This method adds a prefix to all routes in the $routes list.

<?php\n\npublic function group(\n  string $prefix,\n  array $routes,\n  array $middleware = []): App;\n
"},{"location":"routing/#prefix","title":"$prefix","text":"

this prefix is prepended to all $uriTemplate strings in the $routes list

<?php\n\n$app->group('/v2', [\n    ['/read', Resource::class],\n    ['/edit', Resource::class],\n    ['/add', Resource::class],\n    ['/delete', Resource::class],\n]);\n\n/*\n * results in:\n * \n * /v2/read\n * /v2/edit\n * /v2/add\n * /v2/delete\n */\n

"},{"location":"routing/#routes","title":"$routes","text":"

is a list of routes arrays where the array elements must match the route() signature.

"},{"location":"routing/#middleware_1","title":"$middleware","text":"

This list of middlewares is an additional list that is merged with each route in the $routes list and differ from the $middleware list in the route method in this manner.

"},{"location":"routing/caching/","title":"Routes caching","text":"

The caching for routes is the same as for anything else and it is a \"global\" application caching.

"},{"location":"routing/caching/#setup-cache","title":"Setup cache","text":"

Caching configuration is done by setting up the configuration for the App instance.

"},{"location":"routing/caching/#cache-invalidation","title":"Cache invalidation","text":"

At this time there are no tools or a mechanism to invalidate the router cache.

"},{"location":"routing/parameters/","title":"URI parameters","text":"

The URI parameters are named variables wrapped with curly braces {} in the URI template string.

  ->route('/{param1}/{param2}/{param3}/...')\n

If the URI matches the route template, the values of the parameters will be stored in the Psr\\Http\\Message\\ServerRequestInterface@attributes and available through the request object with the getAttribute() method

<?php\n\n  public function put(ServerRequestInterface $request) {\n    $param1 = $request->getAttribute('param1');\n    $param2 = $request->getAttribute('param2');\n    $param3 = $request->getAttribute('param3');\n    ...\n  }\n
"},{"location":"routing/parameters/#parameter-types","title":"Parameter types","text":"

The router supports simple types for automatic value typecasts.

The values are strings by default

  • str
  • int
  • float
  • uuid
  • path
  • regex
"},{"location":"routing/parameters/#str","title":"str","text":"
  ->route('/{id}') or\n  ->route('/{id:str}')\n\n  // ie. \"/123\" \n  // the value is STRING ('id' => '123')\n
"},{"location":"routing/parameters/#int","title":"int","text":"
  ->route('/{id:int}')\n\n  // ie. \"/123\" \n  // the value is INTEGER ('id' => 123)\n
"},{"location":"routing/parameters/#float","title":"float","text":"
  ->route('/{lon:float}/{lat:float}')\n\n  // ie. \"/41.9973/21.4325\" \n  // the values are FLOATS ('lon' => 41.9973, 'lat' => 21.4325)\n
"},{"location":"routing/parameters/#uuid","title":"uuid","text":"
  ->route('/{ident:uuid}')\n\n  // ie. \"/7eacf466-321f-4ceb-914e-e525987e7804\" \n  // the value is STRING ('ident' => '7eacf466-321f-4ceb-914e-e525987e7804')\n
"},{"location":"routing/parameters/#path","title":"path","text":"
  ->route('/collection/{dir:path}')\n\n  // ie. \"/collection/deeper/subgroup/name\" \n  // the value is STRING ('dir' => 'deeper/subgroup/name')\n

Only one :path parameter is supported

"},{"location":"routing/parameters/#regex","title":"regex","text":"
  ->route('/{something:regex:\\d+}')\n\n  // ie. \"/123\" \n  // 'something' => 123\n

Keep it simple

Try to not overcomplicate your regular expressions, in most cases a simple type is sufficient.

"},{"location":"translation/","title":"I18n setup","text":"

The world is multilingual and Koded provides a simple I18n implementation.

Translation strings are stored on a disk, processed by a \"catalog\" and accessed with translation function __().

<?php\n\nfunction __(\n  string $string,\n  array $arguments = [],\n  string $locale = ''\n): string\n
"},{"location":"translation/#configuration","title":"Configuration","text":"

I18n is set in the Koded DIModule and defaults to

  • DefaultCatalog and
  • DefaultFormatter

Both can be changed in your app configuration:

<?php\n\nreturn [\n  'translation.catalog' => \\Koded\\I18n\\GettextCatalog::class,\n  'translation.formatter' => \\Koded\\I18n\\StrtrFormatter::class,\n  'translation.dir' => __DIR__ . '/locale/',\n  'translation.locale' => 'mk_MK',\n];\n
"},{"location":"translation/catalogs/","title":"Storages for translation strings","text":"

Koded offers two different catalogs out of the box

  • GettextCatalog
  • DefaultCatalog

Both are doing the same thing and the difference is how the translated strings are stored.

Which one to use?

  • GettextCatalog for projects with lots of languages (therefore lots of translators).
  • DefaultCatalog for simple projects or relatively small amount of strings or languages support.

There is no right or wrong choice, just pick one that you think is easy to work with or is suitable for your project.

"},{"location":"translation/catalogs/#defaultcatalog","title":"DefaultCatalog","text":"

The strings are stored in the /locale/ application directory in a .php file with a locale name

locale\n\u251c\u2500\u2500 de_DE.php\n\u251c\u2500\u2500 en_US.php\n\u2514\u2500\u2500 mk_MK.php\n\netc.\n

The structure for the en_US.php translation file is

<?php\n\nreturn [\n    'language' => 'English',\n    'messages' => [\n    ]\n];\n
and all locales are expected to be of the same format.

"},{"location":"translation/catalogs/#examples","title":"Examples","text":"
# locale/en_US.php\n\n<?php\n\nreturn [\n    'language' => 'English',\n    'messages' => [\n      'original string' => 'translated string',\n      'pagination.pages' => 'page {0} of {1}',\n    ]\n];\n\n# somewhere in your code\n\n__('original string') // outputs: \"translated string\"\n__('pagination.pages', [1, 42]) // outputs: \"page 1 of 42\"\n
"},{"location":"translation/catalogs/#gettextcatalog","title":"GettextCatalog","text":"

This catalog requires the gettext PHP extension and uses the excellent translation functionality provided by it.

The strings are stored in .po/.mo files within a proper directory structure:

locale\n\u2514\u2500\u2500 en_US\n    \u2514\u2500\u2500 LC_MESSAGES\n        \u251c\u2500\u2500 messages.mo\n        \u2514\u2500\u2500 messages.po\netc.\n
The recommended translation editor is Poedit that supports this kind of translations.

"},{"location":"translation/formatters/","title":"Formatters","text":"

String formatters are used to replace the translation arguments provided in the __(string, arguments) function. The default formatter is DefaultFormatter and it's set in the Koded DIModule.

Choose one

Once you start with translations, the format of the strings with arguments matters, because the argument replacement is different for DefaultFormatter and StrtrFormatter.

"},{"location":"translation/formatters/#defaultformatter","title":"DefaultFormatter","text":"

This formatter uses the vsprintf PHP function to replace the string arguments with values.

"},{"location":"translation/formatters/#strtrformatter","title":"StrtrFormatter","text":"

This formatter uses the strtr PHP function to replace the string arguments with values.

"},{"location":"utils/caching/","title":"Application caching (PSR-16)","text":"

The (app) caching is bolted-in and always on. The default cache library is Koded Simple Cache.

Application caching

The cache functionality is available in the user app by simply demanding an instance of PSR-16 Psr\\SimpleCache\\CacheInterface instance. The DI container will inject the configured cache client.

<?php\n\n...\n  public function something(CacheInterface $cache) {\n    // ...\n  }\n...\n
"},{"location":"utils/caching/#setup-cache","title":"Setup cache","text":"

Caching configuration is done in the application configuration for the App instance.

<?php\n\nreturn [\n    'caching' => [\n        //\n    ]\n];\n
<?php\n\nnew App(config: 'config.php');\n
"},{"location":"utils/caching/#memory","title":"Memory","text":"

Default cache is Memory and does not require a special treatment. It will provide caching for the duration of the request which is useful for development and unit testing.

Memory (default)

No need to set anything in the configuration. This is the default caching client.

"},{"location":"utils/logger/","title":"Logging (PSR-5)","text":"

Default logging library is Koded Logging.

Logging

Demand an instance of PSR-5 Psr\\Log\\LoggerInterface in your methods, DI container will inject the configured log instance.

<?php\n\n...\n  public function something(LoggerInterface $log) {\n    // ...\n  }\n...\n
"},{"location":"utils/logger/#setup-logging","title":"Setup logging","text":"

Logger library is configured in the \\ application configuration for the App instance.

<?php\n\nreturn [\n  'logging' => [\n    'timezone' => 'UTC',\n    'dateformat' => 'Y-m-d H:i:s.u'\n    'loggers' => [\n      [\n        'class' => \\Koded\\Logging\\Processors\\Cli::class,\n        'format' => '[levelname] message',\n        'levels' => -1\n      ]\n    ]\n  ],\n];\n

The conf key loggers is a list of Koded\\Logging\\Log log processor implementations. By default only one log processor is registered that processes all log levels.

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"The Koded Web Framework","text":""},{"location":"#why","title":"Why?","text":"

Sometimes we want a prototype, or quick/simple operations, or solutions for building fast web APIs and app backends. And having a full-blown frameworks will eventually weight you down with tons of unnecessary abstractions and dependencies.

Koded offers small footprint with a clean design that follows the HTTP and ReST architectural style. It tends to be very fast and easy to work with.

"},{"location":"#minimum-application","title":"Minimum application","text":"
<?php\nuse Koded\\Framework\\App;\nuse Koded\\Http\\ServerResponse as R;\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\n((new App)\n    ->route('/', fn() => new R('Hello'))\n)();\n

This fits in a tweet.

"},{"location":"#resource-method-representation","title":"Resource Method Representation","text":"

Koded Framework follows the RMR web architectural pattern, but the developer is free to use whatever it prefers and does not enforce any programming patterns in the application.

However, it is strongly recommended adopting and using RMR because it is aligned nicely with the HTTP ReST concepts. After all, web apps needs fundamental directions and principles, not \"perfect\" solutions so RMR fits here very well.

Resource

An object in the RESTful system identified by a URL that exposes methods that corresponds to the standard HTTP methods (GET, POST, PUT, etc) i.e. a business object (entity).

Method

The HTTP request method that corresponds a Resource method that is executed by the request/URL, which returns a Representation for that resource.

Representation

Provides a Resource to clients in a readable format (JSON, XML, PDF, HTML, etc). It is the payload of the response object (processed by the HTTP Method that is sent back to the client).

"},{"location":"default-dependencies/","title":"Default framework dependencies","text":""},{"location":"default-dependencies/#framework-di-module","title":"Framework DI module","text":"

It loads the following dependencies by default:

"},{"location":"default-dependencies/#interfaces","title":"Interfaces","text":"

See implementations and shared instances.

"},{"location":"default-dependencies/#http","title":"HTTP","text":"
  • PSR-7 Psr\\Http\\Message\\ServerRequestInterface
  • PSR-7 Psr\\Http\\Message\\ResponseInterface
  • Koded\\Framework\\Request
  • Koded\\Framework\\Response
"},{"location":"default-dependencies/#authentication","title":"Authentication","text":"
  • Framework\\Auth\\AuthBackend

    Used in the AuthMiddleware - Framework\\Auth\\AuthProcessor

    Executes the authenticate() in the AuthMiddleware

Application should override the AuthBackend to provide its own authentication storage and logic (i.e. database).

The AuthProcessor parses the credentials and may be overridden to support different types of auth mechanisms (i.e. JWT)

"},{"location":"default-dependencies/#utility","title":"Utility","text":"
  • PSR-16 Psr\\SimpleCache\\CacheInterface
  • PSR-3 Psr\\Log\\LoggerInterface
  • Koded\\Stdlib\\Configuration
"},{"location":"default-dependencies/#middleware","title":"Middleware","text":"

Koded uses PSR-15 middlewares. More details on the middleware page

"},{"location":"default-dependencies/#bindings","title":"Bindings","text":"

Anything that's bound by the default DI module can be replaced with custom implementation(s) in your application DI module(s). The default classes are from the Koded libraries.

"},{"location":"exception-handlers/","title":"Exception handling","text":"

By default, the following exceptions are caught and handled

  • \\Exception
  • \\Error
  • Koded\\Http\\HTTPError

The error response payload follows the RFC-7807 specification.

If you wish to capture specific exception types and/or return custom error messages, create your own class and register it with the method App::withErrorHandler() to convert the exceptions into the desired HTTP responses.

handle() method signature

handle(request, response, exception): void

If app does not use 3rd party PSR-7 library, then Koded will use its own implementations from the Koded HTTP library.

<?php\n\n# CustomExceptionHandler.php\n\nuse Koded\\Framework\\App;\nuse Koded\\Http\\HTTPError;\nuse Koded\\Http\\Interfaces\\{Request, Response};\n\nclass CustomExceptionHandler {\n\n    public static function handle(\n        Request $request,\n        Response $response,\n        HTTPError $exception): void\n    {\n        // do something with $exception\n        // i.e. re-format the error message and set it in the response\n    }\n}\n
"},{"location":"exception-handlers/#register-handler","title":"Register handler","text":"

Register before routes

Order matters. It is required to register the custom exception handlers BEFORE the routes, otherwise they won't be handled if the request/route has been resolved.

<?php\n\n# index.php\n\n((new App)\n    ->withErrorHandler(CustomExceptionHandler::class)\n    ->route(/* ... */)\n)();\n

This implementation is using a PSR-7 compatible library and a standard PHP Throwable class.

<?php\n\nuse Psr\\Http\\Message\\{ServerRequestInterface, ResponseInterface};\n\nclass PDOExceptionHandler {\n\n    public static function handle(\n        ServerRequestInterface $request,\n        ResponseInterface $response,\n        \\Throwable $exception): void\n    {\n        if ($exception instanceof \\PDOException) {\n            // do something about it\n        }\n    }\n}\n
"},{"location":"exception-handlers/#unregister-handler","title":"Unregister handler","text":"

Use App::withoutErrorHandler() method to remove the error handler.

Unregister after custom handlers

Order matters. Unregister the exception handlers AFTER the custom registration, otherwise they won't be removed.

<?php\n\n# index.php\n\n((new App)\n    ->withErrorHandler(SomeCustomErrorHandler::class)\n\n    ->withoutErrorHandler(AnotherCustomExceptionHandler::class)\n    ->route(/* ... */)\n)();\n
"},{"location":"get-started/","title":"Requirements","text":""},{"location":"get-started/#installation","title":"Installation","text":""},{"location":"get-started/#using-composer","title":"Using composer","text":"
composer require koded/framework\n

No composer?

If you don't have composer please follow the download instructions how to install it on your OS.

For manual install on Linux you may run this command:

curl https://getcomposer.org/download/latest-stable/composer.phar -o /usr/local/bin/composer \\\n    && chmod +x /usr/local/bin/composer\n
"},{"location":"get-started/#app-basics","title":"App basics","text":""},{"location":"get-started/#files-structure","title":"Files structure","text":"

It is up to you how you're going to structure your project. A simple and clear structuring is essential for great development, on a long run (or short too), but that is up to developer needs, or based on the app complexity, team decisions, or various other reasons.

Let's look at something that is good in general as a startup,

app/\n    .env\nhtml/\n    .htaccess\n    index.php\nvendor/\ncomposer.json\n

Everything regarding your application goes into the app/ folder. This is an example, app is not a requirement and it can be anything you want.

Protect your code!

It is important to keep everything outside the html/ folder (app/, vendor/ or anything that is app related and may expose the code). Make sure the app code is not accessible from the outside.

"},{"location":"get-started/#composerjson","title":"composer.json","text":"

A composer.json scaffold for your project. Run composer update every time a new class is added, or use psr-4 in autoload section while you develop the app, whatever you prefer most.

{\n    \"require\": {\n        \"koded/framework\": \"^1\"\n    },\n    \"autoload\": {\n      \"classmap\": [\n        \"app\"\n      ],\n      \"exclude-from-classmap\": [\n        \"html\"\n      ]\n    },\n    \"config\": {\n      \"optimize-autoloader\": true\n    }\n}\n
"},{"location":"get-started/#docker-quick-example","title":"Docker (quick example)","text":"

You can jumpstart the development with docker and docker-compose with the above app file structure.

# docker-compose.yaml\n\nversion: '3'\n\nservices:\n    php:\n        image: php:8-apache\n        ports:\n            - 8080:80\n        volumes:\n            - .:/var/www\n

Adjust the volumes, or the host port if it's already taken. Run docker-compose up -d and open your browser at 127.0.0.1:8080

"},{"location":"get-started/#app-entry-point","title":"App entry point","text":"

Create the \"entry script\" for all HTTP requests. There we create an instance of App and define the URI routes.

# /var/www/html/index.php\n\n<?php\n\nuse Koded\\Framework\\App;\nuse Koded\\Http\\Interfaces\\Response;\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\n((new App)\n    ->route('/', function(Response $response): Response {\n        $response->getBody()->write('Work In Progress...');\n        return $response;\n    })\n)();\n

Now point your browser to app address. It should print \"Work in Progress...\" with status code 200.

From here on add more routes and resources to your API, but keep in mind that using closures as resources is NOT the recommended way to build the application. For more on this please follow the documentation further.

"},{"location":"shared-instances/","title":"Shared instances","text":"spec interface default implementation PSR-3 Psr\\Log\\LoggerInterface Koded\\Logging\\Log PSR-16 Psr\\SimpleCache\\CacheInterface Koded\\Caching\\Client\\MemoryClient Koded\\Stdlib\\Configuration Koded\\Stdlib\\Config N/A Koded\\Framework\\Router"},{"location":"shared-instances/#default-bindings","title":"Default bindings","text":"spec interface default implementation PSR-7 Psr\\Http\\Message\\ServerRequestInterface Koded\\Http\\ServerRequest PSR-7 Psr\\Http\\Message\\ResponseInterface Koded\\Http\\ServerResponse PSR-7 Koded\\Http\\Interfaces\\Request Koded\\Http\\ServerRequest PSR-7 Koded\\Http\\Interfaces\\Response Koded\\Http\\ServerResponse Koded\\Framework\\Auth\\AuthBackend Koded\\Framework\\Auth\\SessionAuthBackend Koded\\Framework\\Auth\\AuthProcessor Koded\\Framework\\Auth\\TokenAuthProcessor"},{"location":"configure/","title":"App class configuration","text":"

The configuration object is created by setting the config directives in the Koded\\Framework\\App constructor.

Argument Type Required Default modules array no [] A list of DIModules for the app middleware array no [] The app middleware stack config object, string no '' A path to a config file, FQCN of the configuration class, or an instance of a config object renderer string no 'start_response' Response renderer function/method

All directives are optional.

<?php\n\nnew App(\n    modules: [\n        // a list of DIModule implementation(s)\n    ],\n    middleware: [\n        // a list of PSR-15 middleware classes (global)\n    ],\n    config: __DIR__ . '/path/to/my/config.php', \n    // or\n    config: '.env', \n    // or\n    config: new MyConfig(),\n\n    renderer: MyRenderer::class,\n    // or\n    renderer: 'my_renderer_function'\n);\n
"},{"location":"configure/#constructor-arguments","title":"Constructor arguments","text":""},{"location":"configure/#modules","title":"modules","text":"

optional

This argument accepts a list of DIModule instances that configures the Dependency Injection Container for your app. See more about DI modules.

Example:

[\n    new My\\App\\Module(),\n]\n

"},{"location":"configure/#middleware","title":"middleware","text":"

optional

A list of PSR-15 middleware classes for your application.

Example:

[\n    // ... your middleware classes\n    My\\App\\Middleware\\CustomMiddleware::class,\n    new My\\App\\Middleware\\Other(),\n]\n

Check the PSR-15 middleware stack page for details how Koded framework implements this functionality.

"},{"location":"configure/#config","title":"config","text":"

optional

Configuration values for your application. It supports

  • .php file that returns an array, or
  • .env file, or
  • instance of Koded\\Stdlib\\Configuration object.

Examples:

<?php\n\n# conf.php\nreturn [\n    'key' => 'value',\n];\n\n# /var/www/public/index.php\nnew App(config: '/path/to/conf.php');\n
# .env is always loaded (if exist)\nkey=value\n
<?php\nuse Koded\\Framework\\App;\nuse Koded\\Stdlib\\{Config, Immutable}\n\n$config = new Config('/path/to/app/dir', new Immutable([\n    'key' => 'value',\n]));\n\nnew App(config: $config);\n

.env support

Make sure .env file is not accessible from the outside.

"},{"location":"configure/#renderer","title":"renderer","text":"

optional

A custom renderer for the processed ResponseInterface object. This method/function is executed by the DI container, meaning the depencencies can be anything that the container is able to resolve.

default 'start_response'

<?php\nnew App(renderer: MyCustomRenderer::class);\n\nnew App(renderer: 'My\\App\\custom_renderer');\n

The purpose of this class method (or function) is to provide a custom processing of the ServerRequestInterface and ResponseInterface objects BEFORE it is finally sent to the client, for example a custom-made HTML renderer for server-side template engines, or a response streaming, etc.

"},{"location":"configure/modules/","title":"DIModule interface","text":"
The purpose of this feature is to setup the DI container in your app.\n

Once your app stops being \"Hello World\" and assuming you are going to follow some SOLID principles (or maybe not), the application may end up with lots of interfaces and concrete implementations. All of them can be mapped in the app module.

Koded Container supports multiple modules, but one is sufficient for the container's configuration. Again, it is up to you how you are going to structure the application, so sometimes it makes sense to create multiple modules.

Lets say you want to provide a database for the auth backend instance. Because Koded already has one registered by default (Koded\\Framework\\Auth\\SessionAuthBackend) you can easily configure it in your DI module.

<?php\n\n# /opt/my-app/MyAppModule.php\n\nnamespace My\\App;\n\nuse Koded\\{DIContainer, DIModule};\nuse Koded\\Framework\\Auth\\AuthBackend;\nuse My\\App\\Auth\\DatabaseAuth;\n\nclass MyAppModule implements DIModule {\n\n    public function configure(DIContainer $container): void\n    {\n        $container->bind(AuthBackend::class, DatabaseAuth::class);\n    }\n}\n

Now register the module in the App constructor

<?php\n\n# index.php\n\nnew App(modules: [new MyAppModule]);\n

As you can see it's very easy to override anything in the Koded framework, including the default bindings.

"},{"location":"middleware/builtin/","title":"Built-in middlewares","text":"

Out of the box Koded offers a curated list with common middleware functionality. The developers are encouraged to create their own (or use existing) PSR-15 middlewares to enhance the application features.

"},{"location":"middleware/builtin/#corsmiddleware","title":"CorsMiddleware","text":"

(loaded by default) Cross-Origin Request Sharing support for your JavaScript applications.

Please check the CORS middleware page to customize the behaviour of this middleware class.

"},{"location":"middleware/builtin/#gzipmiddleware","title":"GzipMiddleware","text":"

(loaded by default) Compresses the response payload with gzencode.

"},{"location":"middleware/builtin/#authmiddleware","title":"AuthMiddleware","text":"

A basic mechanism to run the auth logic. Supports the Authorization header.

"},{"location":"middleware/builtin/#callablemiddleware","title":"CallableMiddleware","text":"

Used internally by the framework to support closures for the responders, or any PHP callable resource.

"},{"location":"middleware/builtin/#hstsmiddleware","title":"HSTSMiddleware","text":"

HTTP Strict Transport Security (HSTS). Redirects the request URI from HTTP to HTTPS.

"},{"location":"middleware/builtin/#nocachemiddleware","title":"NoCacheMiddleware","text":"

Adds no-cache response headers.

"},{"location":"middleware/builtin/#xpoweredbymiddleware","title":"XPoweredByMiddleware","text":"

Sets the X-Powered-By response header.

"},{"location":"middleware/cors/","title":"CORS middleware","text":"

Use the configuration to set the desired behavior of your application CORS middleware.

# config.php\n\n<?php\n\nreturn [\n  'cors.origin'  => '',\n  'cors.methods' => '',\n  'cors.headers' => '',\n  'cors.expose'  => 'Authorization, X-Forwarded-With',\n  'cors.maxAge'  => 0,\n  'cors.disable' => false,\n  ...\n];\n
"},{"location":"middleware/cors/#configuration-directives","title":"Configuration directives","text":"

CORS config directives will set the global behavior of the middleware. For example, if cors.methods has a value of GET, POST then only these two methods are allowed for all CORS requests. The same applies to all settings.

Directive Type Default cors.origin string (empty) The server origin address as in schema://host:port (if the port is not a standard port) cors.methods string (empty) Comma-separated list of supported HTTP methods cors.headers string (empty) Comma-separated list of supported headers cors.expose string Authorization, X-Forwarded-With Comma-separated list of exposed headers cors.maxAge int 0 max-age header for OPTIONS request (before the actual request is called) cors.disable bool false This directive will completely disable the middleware"},{"location":"middleware/psr-15/","title":"Middleware stack","text":"

The PSR-15 method signature for processing the incoming server request is very simple:

process(request, handler): response\n

request is the server side incoming request object, handler is the object that receive the request, do some work and returns a response.

It is important to keep in mind when creating a custom middleware class for your app, where the processing of the ServerRequestInterface and ResponseInterface instances will take place.

"},{"location":"middleware/psr-15/#workflow","title":"Workflow","text":"

It is an \"onion layer\".

request\n    MW1 (request)\n        MW2 (request)\n            MW3 (request)\n                    ((RESOURCE))\n            MW3 (response)\n        MW2 (response)\n    MW1 (response)\nresponse\n
  1. when request object (ServerRequestInterface) is passed into the first middleware in the stack, it propagates forward through all middleware classes by modifying the instance,

  2. up to the point where the middleware classes are exhausted, then request object enters the Resource

  3. at this point the request object has been modified by all middleware classes in the stack

  4. once the Resource object return the ResponseInterface instance, it continue to propagate backwards through the middleware stack, now modifying the response object

  5. and finally response exits the middleware stack, to be processed by the Koded renderer and sent back to the caller (ex. browser)

"},{"location":"middleware/psr-15/#example","title":"Example","text":"
<?php\n\nnamespace My\\App\\Middleware;\n\nuse Psr\\Http\\Message\\{ResponseInterface, ServerRequestInterface};\nuse Psr\\Http\\Server\\{MiddlewareInterface, RequestHandlerInterface};\n\nclass MyMiddleware implements MiddlewareInterface {\n\n    public function process(\n        ServerRequestInterface $request, \n        RequestHandlerInterface $handler): ResponseInterface\n    {\n        // --> Everything here is about the $request object\n\n        $response = $handler->handle($request);\n\n        // <-- Everything here is about the $response object\n\n        return $response;\n    }\n}\n

Lets set a random string for the request and pass it in the response header:

<?php\n\nnamespace My\\App\\Middleware;\n\nuse Psr\\Http\\Message\\{ResponseInterface, ServerRequestInterface};\nuse Psr\\Http\\Server\\{MiddlewareInterface, RequestHandlerInterface};\n\nclass RequestIdMiddleware implements MiddlewareInterface {\n\n    public function process(\n        ServerRequestInterface $request, \n        RequestHandlerInterface $handler): ResponseInterface\n    {\n        $request = $request\n            ->withAttribute('req-id', bin2hex(random_bytes(8)));\n\n        // [NOTE]: the next middleware (or the resource object)\n        // will have the ID in the request \"attributes\" property \n\n        $response = $handler->handle($request);\n\n        return $response\n            ->withHeader('X-Req-ID', $request->getAttribute('req-id'));\n    }\n}\n
"},{"location":"routing/","title":"The Router","text":"

URI routing in Koded is easy. When dealing with routes keep in mind the following

  • the router does not care about the HTTP method for the endpoint (this is resolved internally by the App instance)
  • the HTTP method for the route (endpoint) is implemented in the resource object (or function)
  • this framework does not offer a \"Route\" object
  • parsed URI parameters are stored in the Psr\\Http\\Message\\ServerRequestInterface@attributes as name => value pairs
  • all routes are cached for better performance

Routes are implemented in your execution script(s), ie. index.php, or any other PHP file that is part of the HTTP request/URI.

"},{"location":"routing/#route-method","title":"route() method","text":"
<?php\n\npublic function route(\n  string $uriTemplate,\n  object|string $resource,\n  array $middleware = [],\n  bool $explicit = false): App;\n
"},{"location":"routing/#uritemplate","title":"$uriTemplate","text":"

REQUIRED - a route string that should match the request URI.

See URI parameters how to deal with named variables in the URI template.

The parameters are optional, you can normally set direct links, ie.

<?php\n$app->route('/about', About::class);\n
"},{"location":"routing/#resource","title":"$resource","text":"

REQUIRED - your resolver class (aka resource) where the matching HTTP methods are implemented.

This argument can be a FQCN, an instance of a class, a \\Closure, or PHP callable.

<?php\n\n$app\n    ->route('/one', ResourceOne::class)\n    ->route('/two', new ResourceTwo)\n    ->route('/three', function(Request $req, Response $res): Response {\n        // ...\n    })\n    ->route('/four', 'ResourceFour::methodName')\n    ->route('/five', 'some_function')\n
"},{"location":"routing/#middleware","title":"$middleware","text":"

is a list of middleware classes/objects for this particular route.

This argument solves the need to attach any functionalities with middlewares to the route. A good example is to provide an AuthMiddleware to protect the route(s) with authentication/authorization logic:

<?php\n$app->route('/protected', Resource::class, [AuthMiddleware::class]);\n
"},{"location":"routing/#explicit","title":"$explicit","text":"

if set to TRUE it will override the global middleware stack (App::$middleware) and use only the middlewares in the $middleware list.

In this example no middleware runs for this route, because $explicit = true and the $middleware = [] is empty:

<?php\n$app->route('/', Resource::class, [], true);\n
"},{"location":"routing/#group-method","title":"group() method","text":"

This method adds a prefix to all routes in the $routes list.

<?php\n\npublic function group(\n  string $prefix,\n  array $routes,\n  array $middleware = []): App;\n
"},{"location":"routing/#prefix","title":"$prefix","text":"

this prefix is prepended to all $uriTemplate strings in the $routes list

<?php\n\n$app->group('/v2', [\n    ['/read', Resource::class],\n    ['/edit', Resource::class],\n    ['/add', Resource::class],\n    ['/delete', Resource::class],\n]);\n\n/*\n * results in:\n * \n * /v2/read\n * /v2/edit\n * /v2/add\n * /v2/delete\n */\n

"},{"location":"routing/#routes","title":"$routes","text":"

is a list of routes arrays where the array elements must match the route() signature.

"},{"location":"routing/#middleware_1","title":"$middleware","text":"

This list of middlewares is an additional list that is merged with each route in the $routes list and differ from the $middleware list in the route method in this manner.

"},{"location":"routing/caching/","title":"Routes caching","text":"

The caching for routes is the same as for anything else and it is a \"global\" application caching.

"},{"location":"routing/caching/#setup-cache","title":"Setup cache","text":"

Caching configuration is done by setting up the configuration for the App instance.

"},{"location":"routing/caching/#cache-invalidation","title":"Cache invalidation","text":"

At this time there are no tools or a mechanism to invalidate the router cache.

"},{"location":"routing/parameters/","title":"URI parameters","text":"

The URI parameters are named variables wrapped with curly braces {} in the URI template string.

  ->route('/{param1}/{param2}/{param3}/...')\n

If the URI matches the route template, the values of the parameters will be stored in the Psr\\Http\\Message\\ServerRequestInterface@attributes and available through the request object with the getAttribute() method

<?php\n\n  public function put(ServerRequestInterface $request) {\n    $param1 = $request->getAttribute('param1');\n    $param2 = $request->getAttribute('param2');\n    $param3 = $request->getAttribute('param3');\n    ...\n  }\n
"},{"location":"routing/parameters/#parameter-types","title":"Parameter types","text":"

The router supports simple types for automatic value typecasts.

The values are strings by default

  • str
  • int
  • float
  • uuid
  • path
  • regex
"},{"location":"routing/parameters/#str","title":"str","text":"
  ->route('/{id}') or\n  ->route('/{id:str}')\n\n  // ie. \"/123\" \n  // the value is STRING ('id' => '123')\n
"},{"location":"routing/parameters/#int","title":"int","text":"
  ->route('/{id:int}')\n\n  // ie. \"/123\" \n  // the value is INTEGER ('id' => 123)\n
"},{"location":"routing/parameters/#float","title":"float","text":"
  ->route('/{lon:float}/{lat:float}')\n\n  // ie. \"/41.9973/21.4325\" \n  // the values are FLOATS ('lon' => 41.9973, 'lat' => 21.4325)\n
"},{"location":"routing/parameters/#uuid","title":"uuid","text":"
  ->route('/{ident:uuid}')\n\n  // ie. \"/7eacf466-321f-4ceb-914e-e525987e7804\" \n  // the value is STRING ('ident' => '7eacf466-321f-4ceb-914e-e525987e7804')\n
"},{"location":"routing/parameters/#path","title":"path","text":"
  ->route('/collection/{dir:path}')\n\n  // ie. \"/collection/deeper/subgroup/name\" \n  // the value is STRING ('dir' => 'deeper/subgroup/name')\n

Only one :path parameter is supported

"},{"location":"routing/parameters/#regex","title":"regex","text":"
  ->route('/{something:regex:\\d+}')\n\n  // ie. \"/123\" \n  // 'something' => 123\n

Keep it simple

Try to not overcomplicate your regular expressions, in most cases a simple type is sufficient.

"},{"location":"translation/","title":"I18n setup","text":"

The world is multilingual and Koded provides a simple I18n implementation.

Translation strings are stored on a disk, processed by a \"catalog\" and accessed with translation function __().

<?php\n\nfunction __(\n  string $string,\n  array $arguments = [],\n  string $locale = ''\n): string\n
"},{"location":"translation/#configuration","title":"Configuration","text":"

I18n is set in the Koded DIModule and defaults to

  • DefaultCatalog and
  • DefaultFormatter

Both can be changed in your app configuration:

<?php\n\nreturn [\n  'translation.catalog' => \\Koded\\I18n\\GettextCatalog::class,\n  'translation.formatter' => \\Koded\\I18n\\StrtrFormatter::class,\n  'translation.dir' => __DIR__ . '/locale/',\n  'translation.locale' => 'mk_MK',\n];\n
"},{"location":"translation/catalogs/","title":"Storages for translation strings","text":"

Koded offers two different catalogs out of the box

  • GettextCatalog
  • DefaultCatalog

Both are doing the same thing and the difference is how the translated strings are stored.

Which one to use?

  • GettextCatalog for projects with lots of languages (therefore lots of translators).
  • DefaultCatalog for simple projects or relatively small amount of strings or languages support.

There is no right or wrong choice, just pick one that you think is easy to work with or is suitable for your project.

"},{"location":"translation/catalogs/#defaultcatalog","title":"DefaultCatalog","text":"

The strings are stored in the /locale/ application directory in a .php file with a locale name

locale\n\u251c\u2500\u2500 de_DE.php\n\u251c\u2500\u2500 en_US.php\n\u2514\u2500\u2500 mk_MK.php\n\netc.\n

The structure for the en_US.php translation file is

<?php\n\nreturn [\n    'language' => 'English',\n    'messages' => [\n    ]\n];\n
and all locales are expected to be of the same format.

"},{"location":"translation/catalogs/#examples","title":"Examples","text":"
# locale/en_US.php\n\n<?php\n\nreturn [\n    'language' => 'English',\n    'messages' => [\n      'original string' => 'translated string',\n      'pagination.pages' => 'page {0} of {1}',\n    ]\n];\n\n# somewhere in your code\n\n__('original string') // outputs: \"translated string\"\n__('pagination.pages', [1, 42]) // outputs: \"page 1 of 42\"\n
"},{"location":"translation/catalogs/#gettextcatalog","title":"GettextCatalog","text":"

This catalog requires the gettext PHP extension and uses the excellent translation functionality provided by it.

The strings are stored in .po/.mo files within a proper directory structure:

locale\n\u2514\u2500\u2500 en_US\n    \u2514\u2500\u2500 LC_MESSAGES\n        \u251c\u2500\u2500 messages.mo\n        \u2514\u2500\u2500 messages.po\netc.\n
The recommended translation editor is Poedit that supports this kind of translations.

"},{"location":"translation/formatters/","title":"Formatters","text":"

String formatters are used to replace the translation arguments provided in the __(string, arguments) function. The default formatter is DefaultFormatter and it's set in the Koded DIModule.

Choose one

Once you start with translations, the format of the strings with arguments matters, because the argument replacement is different for DefaultFormatter and StrtrFormatter.

"},{"location":"translation/formatters/#defaultformatter","title":"DefaultFormatter","text":"

This formatter uses the vsprintf PHP function to replace the string arguments with values.

"},{"location":"translation/formatters/#strtrformatter","title":"StrtrFormatter","text":"

This formatter uses the strtr PHP function to replace the string arguments with values.

"},{"location":"utils/caching/","title":"Application caching (PSR-16)","text":"

The (app) caching is bolted-in and always on. The default cache library is Koded Simple Cache.

Application caching

The cache functionality is available in the user app by simply demanding an instance of PSR-16 Psr\\SimpleCache\\CacheInterface instance. The DI container will inject the configured cache client.

<?php\n\n...\n  public function something(CacheInterface $cache) {\n    // ...\n  }\n...\n
"},{"location":"utils/caching/#setup-cache","title":"Setup cache","text":"

Caching configuration is done in the application configuration for the App instance.

<?php\n\nreturn [\n    'caching' => [\n        //\n    ]\n];\n
<?php\n\nnew App(config: 'config.php');\n
"},{"location":"utils/caching/#memory","title":"Memory","text":"

Default cache is Memory and does not require a special treatment. It will provide caching for the duration of the request which is useful for development and unit testing.

Memory (default)

No need to set anything in the configuration. This is the default caching client.

"},{"location":"utils/logger/","title":"Logging (PSR-5)","text":"

Default logging library is Koded Logging.

Logging

Demand an instance of PSR-5 Psr\\Log\\LoggerInterface in your methods, DI container will inject the configured log instance.

<?php\n\n...\n  public function something(LoggerInterface $log) {\n    // ...\n  }\n...\n
"},{"location":"utils/logger/#setup-logging","title":"Setup logging","text":"

Logger library is configured in the \\ application configuration for the App instance.

<?php\n\nreturn [\n  'logging' => [\n    'timezone' => 'UTC',\n    'dateformat' => 'Y-m-d H:i:s.u'\n    'loggers' => [\n      [\n        'class' => \\Koded\\Logging\\Processors\\Cli::class,\n        'format' => '[levelname] message',\n        'levels' => -1\n      ]\n    ]\n  ],\n];\n

The conf key loggers is a list of Koded\\Logging\\Log log processor implementations. By default only one log processor is registered that processes all log levels.

"}]} \ No newline at end of file diff --git a/shared-instances/index.html b/shared-instances/index.html index ea69074..d283135 100644 --- a/shared-instances/index.html +++ b/shared-instances/index.html @@ -1 +1 @@ - Shared instances - Koded Framework

Shared instances

spec interface default implementation
PSR-3 Psr\Log\LoggerInterface Koded\Logging\Log
PSR-16 Psr\SimpleCache\CacheInterface Koded\Caching\Client\MemoryClient
Koded\Stdlib\Configuration Koded\Stdlib\Config
N/A Koded\Framework\Router

Default bindings

spec interface default implementation
PSR-7 Psr\Http\Message\ServerRequestInterface Koded\Http\ServerRequest
PSR-7 Psr\Http\Message\ResponseInterface Koded\Http\ServerResponse
PSR-7 Koded\Http\Interfaces\Request Koded\Http\ServerRequest
PSR-7 Koded\Http\Interfaces\Response Koded\Http\ServerResponse
Koded\Framework\Auth\AuthBackend Koded\Framework\Auth\SessionAuthBackend
Koded\Framework\Auth\AuthProcessor Koded\Framework\Auth\TokenAuthProcessor
\ No newline at end of file + Shared instances - Koded Framework

Shared instances

spec interface default implementation
PSR-3 Psr\Log\LoggerInterface Koded\Logging\Log
PSR-16 Psr\SimpleCache\CacheInterface Koded\Caching\Client\MemoryClient
Koded\Stdlib\Configuration Koded\Stdlib\Config
N/A Koded\Framework\Router

Default bindings

spec interface default implementation
PSR-7 Psr\Http\Message\ServerRequestInterface Koded\Http\ServerRequest
PSR-7 Psr\Http\Message\ResponseInterface Koded\Http\ServerResponse
PSR-7 Koded\Http\Interfaces\Request Koded\Http\ServerRequest
PSR-7 Koded\Http\Interfaces\Response Koded\Http\ServerResponse
Koded\Framework\Auth\AuthBackend Koded\Framework\Auth\SessionAuthBackend
Koded\Framework\Auth\AuthProcessor Koded\Framework\Auth\TokenAuthProcessor
\ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 4016bb7..f8998f9 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,92 +2,92 @@ https://kodeart.github.io/koded/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/default-dependencies/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/exception-handlers/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/get-started/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/shared-instances/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/configure/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/configure/modules/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/middleware/builtin/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/middleware/cors/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/middleware/psr-15/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/routing/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/routing/caching/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/routing/parameters/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/translation/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/translation/catalogs/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/translation/formatters/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/utils/caching/ - 2023-04-05 + 2023-10-14 daily https://kodeart.github.io/koded/utils/logger/ - 2023-04-05 + 2023-10-14 daily \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 5a94a87879b1d8a5589d1f876baf3682e3ea8683..3bb9d4e9ec3b828cb287be26b287fcd0255bae67 100644 GIT binary patch delta 342 zcmV-c0jd7&0__4OABzYGF2gEi0{?SqbY*Q}a4vXlYyj1l!EVDK42JJ}iYRwN(ru9@ zB7gR98h!Bv30(~PvZ?B_fS{vs!S2iVw^#909=f}BP9A{@b`H2N2cqdk%4Jz9bOfWC zu!_k_)B{gYUX8M@p3Cm8;I;<1jLxm~QFm(;`54yAQ(=U1I(8e zP8ud+c?HKco~6#0)dC45UddU098>}$Twv0mbCzDm&^tRP*a9M-Jo}upIXc77XJPT> zadw_u*aWMNX&XtMFp1_dU(^(5*4*X~sFfZz0XnIqMxd-q5+xPTtF?e`15zteJ}26Z oq_mG|RFwA>;LGYU|5tUvWa%xW|ED~6{3qys0M|VzGD8di0E>&Eq5uE@ delta 342 zcmV-c0jd7&0__4OABzYG?M5wR0{?SqbY*Q}a4vXlYyj1l!EVDK42JK0iYRwN+O3f$ zB7f$19(?fy30(|_a$D790YOLOf<2V)Z?EF1Jhq!=N*;j{S9ITwv0mbCzDm&^bFLSOX%TJo%inIXJ`5XJ+x` zX>y)iSOu$&X&p%&F^TOyU(^&Q*4*X~sFm(l0XnLr2B54<5+xPTtEGUh15zteJ}26h oq_m4^P?Yx-;LGYU|5tUzWa%xW|EIik{3mFC0Ki*u0YeM`0JQR-VE_OC diff --git a/translation/catalogs/index.html b/translation/catalogs/index.html index a1633ef..dfdec26 100644 --- a/translation/catalogs/index.html +++ b/translation/catalogs/index.html @@ -1,4 +1,4 @@ - Catalogs - Koded Framework

Storages for translation strings

Koded offers two different catalogs out of the box

  • GettextCatalog
  • DefaultCatalog

Both are doing the same thing and the difference is how the translated strings are stored.

Which one to use?

  • GettextCatalog for projects with lots of languages (therefore lots of translators).
  • DefaultCatalog for simple projects or relatively small amount of strings or languages support.

There is no right or wrong choice, just pick one that you think is easy to work with or is suitable for your project.

DefaultCatalog

The strings are stored in the /locale/ application directory in a .php file with a locale name

locale
+ Catalogs - Koded Framework       

Storages for translation strings

Koded offers two different catalogs out of the box

  • GettextCatalog
  • DefaultCatalog

Both are doing the same thing and the difference is how the translated strings are stored.

Which one to use?

  • GettextCatalog for projects with lots of languages (therefore lots of translators).
  • DefaultCatalog for simple projects or relatively small amount of strings or languages support.

There is no right or wrong choice, just pick one that you think is easy to work with or is suitable for your project.

DefaultCatalog

The strings are stored in the /locale/ application directory in a .php file with a locale name

locale
 ├── de_DE.php
 ├── en_US.php
 └── mk_MK.php
@@ -33,4 +33,4 @@
         ├── messages.mo
         └── messages.po
 etc.
-
The recommended translation editor is Poedit that supports this kind of translations.

\ No newline at end of file +
The recommended translation editor is Poedit that supports this kind of translations.

\ No newline at end of file diff --git a/translation/formatters/index.html b/translation/formatters/index.html index cacac20..d1e2c5b 100644 --- a/translation/formatters/index.html +++ b/translation/formatters/index.html @@ -1 +1 @@ - Formatters - Koded Framework

Formatters

String formatters are used to replace the translation arguments provided in the __(string, arguments) function. The default formatter is DefaultFormatter and it's set in the Koded DIModule.

Choose one

Once you start with translations, the format of the strings with arguments matters, because the argument replacement is different for DefaultFormatter and StrtrFormatter.

DefaultFormatter

This formatter uses the vsprintf PHP function to replace the string arguments with values.

StrtrFormatter

This formatter uses the strtr PHP function to replace the string arguments with values.

\ No newline at end of file + Formatters - Koded Framework

Formatters

String formatters are used to replace the translation arguments provided in the __(string, arguments) function. The default formatter is DefaultFormatter and it's set in the Koded DIModule.

Choose one

Once you start with translations, the format of the strings with arguments matters, because the argument replacement is different for DefaultFormatter and StrtrFormatter.

DefaultFormatter

This formatter uses the vsprintf PHP function to replace the string arguments with values.

StrtrFormatter

This formatter uses the strtr PHP function to replace the string arguments with values.

\ No newline at end of file diff --git a/translation/index.html b/translation/index.html index eeb411d..8951df3 100644 --- a/translation/index.html +++ b/translation/index.html @@ -1,4 +1,4 @@ - Setup - Koded Framework

I18n setup

The world is multilingual and Koded provides a simple I18n implementation.

Translation strings are stored on a disk, processed by a "catalog" and accessed with translation function __().

<?php
+ Setup - Koded Framework       

I18n setup

The world is multilingual and Koded provides a simple I18n implementation.

Translation strings are stored on a disk, processed by a "catalog" and accessed with translation function __().

<?php
 
 function __(
   string $string,
@@ -13,4 +13,4 @@
   'translation.dir' => __DIR__ . '/locale/',
   'translation.locale' => 'mk_MK',
 ];
-
\ No newline at end of file +
\ No newline at end of file diff --git a/utils/caching/index.html b/utils/caching/index.html index 7ae6b01..9fcd9e1 100644 --- a/utils/caching/index.html +++ b/utils/caching/index.html @@ -1,4 +1,4 @@ - Caching - Koded Framework

Application caching (PSR-16)

The (app) caching is bolted-in and always on. The default cache library is Koded Simple Cache.

Application caching

cache-simple-status

The cache functionality is available in the user app by simply demanding an instance of PSR-16 Psr\SimpleCache\CacheInterface instance. The DI container will inject the configured cache client.

<?php
+ Caching - Koded Framework       

Application caching (PSR-16)

The (app) caching is bolted-in and always on. The default cache library is Koded Simple Cache.

Application caching

cache-simple-status

The cache functionality is available in the user app by simply demanding an instance of PSR-16 Psr\SimpleCache\CacheInterface instance. The DI container will inject the configured cache client.

<?php
 
 ...
   public function something(CacheInterface $cache) {
@@ -22,4 +22,4 @@
 !!! abstract "Memcached"
 
 ### File
-!!! abstract "File" --> 
\ No newline at end of file +!!! abstract "File" -->
\ No newline at end of file diff --git a/utils/logger/index.html b/utils/logger/index.html index c3f83ab..e495b41 100644 --- a/utils/logger/index.html +++ b/utils/logger/index.html @@ -1,4 +1,4 @@ - Logging - Koded Framework

Logging (PSR-5)

Default logging library is Koded Logging.

Logging

cache-log-status

Demand an instance of PSR-5 Psr\Log\LoggerInterface in your methods, DI container will inject the configured log instance.

<?php
+ Logging - Koded Framework       

Logging (PSR-5)

Default logging library is Koded Logging.

Logging

cache-log-status

Demand an instance of PSR-5 Psr\Log\LoggerInterface in your methods, DI container will inject the configured log instance.

<?php
 
 ...
   public function something(LoggerInterface $log) {
@@ -20,4 +20,4 @@
     ]
   ],
 ];
-

The conf key loggers is a list of Koded\Logging\Log log processor implementations. By default only one log processor is registered that processes all log levels.

\ No newline at end of file +

The conf key loggers is a list of Koded\Logging\Log log processor implementations. By default only one log processor is registered that processes all log levels.

\ No newline at end of file