<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kimsal Blog</title>
	<atom:link href="https://kimsal.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://kimsal.com/blog</link>
	<description>putting the diva in software divalopment</description>
	<lastBuildDate>Fri, 20 Mar 2026 15:03:04 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>
	<item>
		<title>A bit of feedback&#8230;</title>
		<link>https://kimsal.com/blog/2026/03/20/a-bit-of-feedback/</link>
					<comments>https://kimsal.com/blog/2026/03/20/a-bit-of-feedback/#respond</comments>
		
		<dc:creator><![CDATA[kimsal]]></dc:creator>
		<pubDate>Fri, 20 Mar 2026 15:03:04 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://kimsal.com/blog/?p=181</guid>

					<description><![CDATA[A small bit of feedback&#8230; that&#8217;s often what a mobile user is looking for. Haptic feedback &#8211; a quick device vibration &#8211; is great. It&#8217;s subtle, quick, doesn&#8217;t interrupt, but gives an actual *feeling* that something happened. And&#8230; on iOS, it&#8217;s harder to do without building a full &#8216;native mobile app&#8217;. iOS Safari doesn&#8217;t support...]]></description>
										<content:encoded><![CDATA[
<p>A small bit of feedback&#8230; that&#8217;s often what a mobile user is looking for.   Haptic feedback &#8211; a quick device vibration &#8211; is great.  It&#8217;s subtle, quick, doesn&#8217;t interrupt, but gives an actual *feeling* that something happened.  And&#8230; on iOS, it&#8217;s harder to do without building a full &#8216;native mobile app&#8217;.   iOS Safari doesn&#8217;t support the &#8216;vibrate&#8217; method.  Apple does do some things quite well, but holding off on browser support for this has just been&#8230; annoying.<br><br>Some time ago Apple introduced a &#8216;toggle switch&#8217; attribute on the &#8216;checkbox&#8217; input type.  And clicking it&#8230; provides small haptic feedback.  And this morning I found a small <a href="https://github.com/posaune0423/use-haptic">React hook</a> that will trigger this for you.</p>



<p>I created a similar version for Vue 3</p>



<pre class="wp-block-code"><code>import { ref, computed, onMounted, onUnmounted } from 'vue'

const HAPTIC_DURATION = 10

function detectIOS(): boolean {
  return /iPad|iPhone|iPod/.test(navigator.userAgent) &amp;&amp; !(window as any).MSStream
}

export function useHaptic(duration = HAPTIC_DURATION) {
  const inputRef = ref&lt;HTMLInputElement | null>(null)
  const labelRef = ref&lt;HTMLLabelElement | null>(null)
  const isIOS = computed(() => detectIOS())

  onMounted(() => {
    const input = document.createElement('input')
    input.type = 'checkbox'
    input.id = 'haptic-switch'
    input.setAttribute('switch', '')
    input.style.display = 'none'
    document.body.appendChild(input)
    inputRef.value = input

    const label = document.createElement('label')
    label.htmlFor = 'haptic-switch'
    label.style.display = 'none'
    document.body.appendChild(label)
    labelRef.value = label
  })

  onUnmounted(() => {
    if (inputRef.value) document.body.removeChild(inputRef.value)
    if (labelRef.value) document.body.removeChild(labelRef.value)
  })

  function triggerHaptic() {
    if (!isIOS.value &amp;&amp; navigator?.vibrate) {
      navigator.vibrate(duration)
    } else {
      labelRef.value?.click()
    }
  }

  return { triggerHaptic }
}</code></pre>



<p>Which can then be used as so&#8230;</p>



<pre class="wp-block-code"><code>&lt;script setup lang="ts">
import { useHaptic } from '@/composables/useHaptic';

const { triggerHaptic } = useHaptic();
....

function handleClick(type: string) {
    triggerHaptic();
// other code here
}
...</code></pre>



<p>Simple, and it works just fine.</p>



<p>Maybe in 2027 we&#8217;ll get the ability to provide haptic feedback in regular web apps natively without hacks?</p>
]]></content:encoded>
					
					<wfw:commentRss>https://kimsal.com/blog/2026/03/20/a-bit-of-feedback/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHP Quality Tools &#8211; phpinsights</title>
		<link>https://kimsal.com/blog/2025/12/22/php-quality-tools-phpinsights/</link>
					<comments>https://kimsal.com/blog/2025/12/22/php-quality-tools-phpinsights/#respond</comments>
		
		<dc:creator><![CDATA[kimsal]]></dc:creator>
		<pubDate>Mon, 22 Dec 2025 10:40:00 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://kimsal.com/blog/?p=169</guid>

					<description><![CDATA[phpinsights is a tool you can use to get a nice overview of the quality state of a PHP project. The tool will analyze the code, complexity, architecture and some code style rules, then present four corresponding scores. Measuring over time will give you a sense of which areas of your code are improving, and...]]></description>
										<content:encoded><![CDATA[
<p><a href="https://github.com/nunomaduro/phpinsights">phpinsights</a> is a tool you can use to get a nice overview of the quality state of a PHP project. The tool will analyze the code, complexity, architecture and some code style rules, then present four corresponding scores.</p>



<p>Measuring over time will give you a sense of which areas of your code are improving, and which areas might need some more attention.</p>



<p>As with many quality tools, the measurements and grading are opinions, but those opinions are generally based on industry-accepted views.  </p>



<ul class="wp-block-list">
<li>Following a single standard coding styles is preferable to each file being formatted differently.  </li>



<li>Smaller code blocks are preferable to larger code blocks</li>



<li>and so on&#8230;</li>
</ul>



<p>phpinsights uses php_codesniffer under the hood, and provides its own out of the box default set of configurations (in other words, its own opinions).</p>



<p>It&#8217;s a nice and easy package to get a quick overview of the state of things.  Folks with more time on their hands can dig in to its configuration and set things up the way they&#8217;d like, or may wish to dig in to <a href="https://github.com/PHPCSStandards/PHP_CodeSniffer/">php_codesniffer</a> directly.</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="441" src="https://kimsal.com/blog/wp-content/uploads/2025/12/preview-1024x441.png" alt="" class="wp-image-171" srcset="https://kimsal.com/blog/wp-content/uploads/2025/12/preview-1024x441.png 1024w, https://kimsal.com/blog/wp-content/uploads/2025/12/preview-300x129.png 300w, https://kimsal.com/blog/wp-content/uploads/2025/12/preview-768x331.png 768w, https://kimsal.com/blog/wp-content/uploads/2025/12/preview-1536x662.png 1536w, https://kimsal.com/blog/wp-content/uploads/2025/12/preview.png 1764w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p></p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="893" src="https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phpinsights-summary-1024x893.png" alt="" class="wp-image-170" srcset="https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phpinsights-summary-1024x893.png 1024w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phpinsights-summary-300x261.png 300w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phpinsights-summary-768x669.png 768w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phpinsights-summary-1536x1339.png 1536w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phpinsights-summary.png 1838w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://kimsal.com/blog/2025/12/22/php-quality-tools-phpinsights/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHP Quality Tools &#8211; phploc lines of code</title>
		<link>https://kimsal.com/blog/2025/12/18/php-quality-tools-phploc-lines-of-code/</link>
					<comments>https://kimsal.com/blog/2025/12/18/php-quality-tools-phploc-lines-of-code/#respond</comments>
		
		<dc:creator><![CDATA[kimsal]]></dc:creator>
		<pubDate>Thu, 18 Dec 2025 22:21:46 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://kimsal.com/blog/?p=166</guid>

					<description><![CDATA[Need to check the lines of code in a PHP codebase? The phploc tool has you covered. This will give a summary of lines of code, separating out comments and non-comments, then gives you a whole lot more. The cyclocmatic complexity measure is possible one of the more useful tools. Understanding that you have classes...]]></description>
										<content:encoded><![CDATA[
<p>Need to check the lines of code in a PHP codebase?  The phploc tool has you covered.</p>



<pre class="wp-block-code"><code>$ phpqa phploc ./app</code></pre>



<p>This will give a summary of lines of code, separating out comments and non-comments, then gives you a whole lot more.</p>



<p>The cyclocmatic complexity measure is possible one of the more useful tools.  Understanding that you have classes and methods that might be &#8216;too complex&#8217; is the first step in dealing with that.  </p>



<p>Why should you care about complexity?  Less complex code provides</p>



<ul class="wp-block-list">
<li>Ease of long-term maintenance </li>



<li>Ability for new folks to understand the code more quickly</li>



<li>Smaller focused methods to test</li>
</ul>



<p></p>



<p></p>



<figure class="wp-block-image size-large"><img decoding="async" width="477" height="1024" src="https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phploc-1-477x1024.png" alt="" class="wp-image-167" srcset="https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phploc-1-477x1024.png 477w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phploc-1-140x300.png 140w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phploc-1-768x1649.png 768w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phploc-1-716x1536.png 716w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phploc-1-954x2048.png 954w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-phploc-1-scaled.png 1193w" sizes="(max-width: 477px) 100vw, 477px" /></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://kimsal.com/blog/2025/12/18/php-quality-tools-phploc-lines-of-code/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHP Quality Tools &#8211; magic number detector</title>
		<link>https://kimsal.com/blog/2025/12/16/php-quality-tools-magic-number-detector/</link>
					<comments>https://kimsal.com/blog/2025/12/16/php-quality-tools-magic-number-detector/#respond</comments>
		
		<dc:creator><![CDATA[kimsal]]></dc:creator>
		<pubDate>Tue, 16 Dec 2025 11:21:00 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://kimsal.com/blog/?p=175</guid>

					<description><![CDATA[Got magic numbers in your PHP? Find &#8217;em fast with PHP Magic Number Detector tool. probably seems OK to start with, but the next person (including yourself!) coming at this later will wonder what the heck &#8216;8&#8217; is. And&#8230; if you want to search for any other reference to this &#8216;magic&#8217; location ID&#8230; good luck...]]></description>
										<content:encoded><![CDATA[
<p>Got magic numbers in your PHP? Find &#8217;em fast with <a href="https://github.com/povils/phpmnd">PHP Magic Number Detector</a> tool.</p>



<pre class="wp-block-code"><code>if ($locationId === 8) {
  // do something awesome
}</code></pre>



<p>probably seems OK to start with, but the next person (including yourself!) coming at this later will wonder what the heck &#8216;8&#8217; is. And&#8230; if you want to search for any other reference to this &#8216;magic&#8217; location ID&#8230; good luck searching your code for the number 8&#8230;</p>



<p>Collapsed output in the image below, just to fit it in the picture.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="985" height="1024" src="https://kimsal.com/blog/wp-content/uploads/2025/12/php-magic-number-detector-985x1024.png" alt="" class="wp-image-176" srcset="https://kimsal.com/blog/wp-content/uploads/2025/12/php-magic-number-detector-985x1024.png 985w, https://kimsal.com/blog/wp-content/uploads/2025/12/php-magic-number-detector-288x300.png 288w, https://kimsal.com/blog/wp-content/uploads/2025/12/php-magic-number-detector-768x799.png 768w, https://kimsal.com/blog/wp-content/uploads/2025/12/php-magic-number-detector-1477x1536.png 1477w, https://kimsal.com/blog/wp-content/uploads/2025/12/php-magic-number-detector.png 1956w" sizes="auto, (max-width: 985px) 100vw, 985px" /></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://kimsal.com/blog/2025/12/16/php-quality-tools-magic-number-detector/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHP Quality Tools</title>
		<link>https://kimsal.com/blog/2025/12/15/php-quality-tools/</link>
					<comments>https://kimsal.com/blog/2025/12/15/php-quality-tools/#respond</comments>
		
		<dc:creator><![CDATA[kimsal]]></dc:creator>
		<pubDate>Mon, 15 Dec 2025 22:21:43 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://kimsal.com/blog/?p=163</guid>

					<description><![CDATA[Curious about checking out the quality of your PHP project, but don&#8217;t know where to start? https://github.com/jakzal/phpqa is a project providing docker images of various tools to help measure aspects of your PHP code. will run the phploc tool on your current folder But&#8230; you can alias the tool, then simply run $ phpqa &#60;toolname>...]]></description>
										<content:encoded><![CDATA[
<p>Curious about checking out the quality of your PHP project, but don&#8217;t know where to start?<br><br><a href="https://github.com/jakzal/phpqa">https://github.com/jakzal/phpqa</a> is a project providing docker images of various tools to help measure aspects of your PHP code.</p>



<pre class="wp-block-code"><code>docker run --init -it --rm -v "$(pwd):/project" -v "$(pwd)/tmp-phpqa:/tmp" -w /project jakzal/phpqa phploc ./</code></pre>



<p><br>will run the phploc tool on your current folder<br><br>But&#8230; you can alias the tool, then simply run<br><br>$ phpqa &lt;toolname> &lt;params><br><br>to run any of the tools on any code.<br><br>No having to add any new dependencies to your composer, no modifications to existing code.<br></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="351" src="https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-setup-2-1024x351.png" alt="" class="wp-image-164" srcset="https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-setup-2-1024x351.png 1024w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-setup-2-300x103.png 300w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-setup-2-768x263.png 768w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-setup-2-1536x527.png 1536w, https://kimsal.com/blog/wp-content/uploads/2025/12/phpqa-setup-2.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://kimsal.com/blog/2025/12/15/php-quality-tools/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CyclopsMonitor</title>
		<link>https://kimsal.com/blog/2025/12/02/cyclopsmonitor/</link>
					<comments>https://kimsal.com/blog/2025/12/02/cyclopsmonitor/#respond</comments>
		
		<dc:creator><![CDATA[kimsal]]></dc:creator>
		<pubDate>Tue, 02 Dec 2025 17:47:01 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://kimsal.com/blog/?p=160</guid>

					<description><![CDATA[I&#8217;ve been posting more about this new service over on linkedin, but haven&#8217;t posted much here. CyclopsMonitor is a web monitoring service &#8211; checking if a web address is up, how fast it responds, if specific content is available, when SSL/TLS certs expire, when domain name expires, and&#8230; sending you notifications when problems occur. Currently,...]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve been posting more about this new service over on <a href="https://www.linkedin.com/company/cyclopsmonitor">linkedin</a>, but haven&#8217;t posted much here.</p>



<p><a href="https://cyclopsmonitor.com">CyclopsMonitor</a> is a web monitoring service &#8211; checking if a web address is up, how fast it responds, if specific content is available, when SSL/TLS certs expire, when domain name expires, and&#8230; sending you notifications when problems occur.  Currently, this supports email, SMS and Slack notifications &#8211; general webhooks and voice calling are on the horizon (but not yet on the actual <a href="http://cyclopsmonitor.com/roadmap/idea-board">roadmap</a>!)</p>



<p>As with many services, this grew out of something I&#8217;d started to build for myself and then a bit later, a client.  Service has been up for a couple months at this point, and while there&#8217;s a &#8216;payment&#8217; system in place to charge for various levels of service, that&#8217;s not yet enabled.  It&#8217;s open for folks to use and test right now, so please have at it.  <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://kimsal.com/blog/2025/12/02/cyclopsmonitor/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHPStorm AI Assistant commit message mods</title>
		<link>https://kimsal.com/blog/2024/12/29/phpstorm-ai-assistant-commit-message-mods/</link>
					<comments>https://kimsal.com/blog/2024/12/29/phpstorm-ai-assistant-commit-message-mods/#respond</comments>
		
		<dc:creator><![CDATA[kimsal]]></dc:creator>
		<pubDate>Mon, 30 Dec 2024 04:23:57 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://kimsal.com/blog/?p=140</guid>

					<description><![CDATA[I&#8217;ve been testing out the Jetbrains AI service the past few months. Used Copilot for a while in 2023, then Codeium for about a year, and am now trying Jetbrains. I can&#8217;t say I notice much of a difference for most of the standard code I deal with, but I did recently start using Jetbrains...]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve been testing out the Jetbrains AI service the past few months. Used Copilot for a while in 2023, then Codeium for about a year, and am now trying Jetbrains. I can&#8217;t say I notice much of a difference for most of the standard code I deal with, but I did recently start using Jetbrains AI with their integrated git client.<br><br>I&#8217;m generally bad a commit messages, and &#8230; have periods of laziness. I use Tower for a lot of git work, and it&#8217;s great for some things &#8211; letting me &#8216;undo&#8217; was the thing that got me to buy it in the first place. But&#8230; it&#8217;s something to switch to, and I &#8230; sometimes don&#8217;t break out of the zone enough to structure good commits and document them.<br><br>The Jetbrains AI tool is OK for code, but has a hook to expose it to their inbuilt git client. Specifically&#8230; after you choose files, you can hit a button and it&#8217;ll write a commit message describing the changes. And&#8230; it&#8217;s *pretty good* at finding a decent balance between brevity and explanation. You can edit after it provides a message, but I haven&#8217;t found much of a need to.<br><br>Except&#8230; I picked up a habit of &#8216;conventional commit&#8217; prefixing in commit messages. The Jetbrains AI doesn&#8217;t do that, and&#8230; on a recent test project, I was getting used to not having that style. I then found the &#8216;AI prompts&#8217; section in the settings area, and&#8230; there&#8217;s a commit message prompt. And it&#8217;s editable!</p>



<p><img loading="lazy" decoding="async" width="500" height="360" class="wp-image-143" style="width: 500px;" src="https://kimsal.com/blog/wp-content/uploads/2024/12/Screenshot-2024-12-29-at-11.13.50 PM.jpg" alt="" srcset="https://kimsal.com/blog/wp-content/uploads/2024/12/Screenshot-2024-12-29-at-11.13.50 PM.jpg 1229w, https://kimsal.com/blog/wp-content/uploads/2024/12/Screenshot-2024-12-29-at-11.13.50 PM-300x216.jpg 300w, https://kimsal.com/blog/wp-content/uploads/2024/12/Screenshot-2024-12-29-at-11.13.50 PM-1024x737.jpg 1024w, https://kimsal.com/blog/wp-content/uploads/2024/12/Screenshot-2024-12-29-at-11.13.50 PM-768x552.jpg 768w" sizes="auto, (max-width: 500px) 100vw, 500px" /></p>



<p><img loading="lazy" decoding="async" width="500" height="360" class="wp-image-144" style="width: 500px;" src="https://kimsal.com/blog/wp-content/uploads/2024/12/Screenshot-2024-12-29-at-11.14.15 PM.jpg" alt="" srcset="https://kimsal.com/blog/wp-content/uploads/2024/12/Screenshot-2024-12-29-at-11.14.15 PM.jpg 1234w, https://kimsal.com/blog/wp-content/uploads/2024/12/Screenshot-2024-12-29-at-11.14.15 PM-300x216.jpg 300w, https://kimsal.com/blog/wp-content/uploads/2024/12/Screenshot-2024-12-29-at-11.14.15 PM-1024x738.jpg 1024w, https://kimsal.com/blog/wp-content/uploads/2024/12/Screenshot-2024-12-29-at-11.14.15 PM-768x553.jpg 768w" sizes="auto, (max-width: 500px) 100vw, 500px" /><br><br>I modified it to ask it to provide a prefix tag like &#8216;feat:&#8217; or &#8216;doc:&#8217; or &#8216;fix:&#8217; or similar, to summarize the type of commit, along with its regular summarization.  It worked like a champ the last few days, and I&#8217;ll be keeping it around.  I&#8217;ll be checking out more of the configurable prompts to see what other benefits I can wrangle from this AI service.  <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://kimsal.com/blog/2024/12/29/phpstorm-ai-assistant-commit-message-mods/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Open Source TechFinder</title>
		<link>https://kimsal.com/blog/2024/11/11/open-source-techfinder/</link>
					<comments>https://kimsal.com/blog/2024/11/11/open-source-techfinder/#respond</comments>
		
		<dc:creator><![CDATA[kimsal]]></dc:creator>
		<pubDate>Mon, 11 Nov 2024 21:55:54 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://kimsal.com/blog/?p=137</guid>

					<description><![CDATA[Inspired by the AUTM conference, I got inspired to look at some of the common processes techtransfer folks do. Main idea was to try to develop something relatively &#8216;standalone&#8217; that might address a use case I learned about, so I decided on building a web-based open source techfinder tool to publish licensable technologies. The notion...]]></description>
										<content:encoded><![CDATA[Inspired by the AUTM conference, I got inspired to look at some of the common processes techtransfer folks do. Main idea was to try to develop something relatively &#8216;standalone&#8217; that might address a use case I learned about, so I decided on building a web-based open source techfinder tool to publish licensable technologies.<br /><br />The notion of a &#8216;techfinder&#8217; isn&#8217;t specifically new &#8211; many departments have one &#8211; I think AUTM itself even has one. However, I decided I&#8217;d try my hand at putting something basic together, partially as a challenge to build something &#8216;start to finish&#8217; that could stand on its own, as opposed to just enhancements to current systems.<br /><br />A main question that has no simple answer: where to draw the lines around functionality? Having seen various systems, there&#8217;s common behaviour, but lots of variation &#8211; tag clouds vs nested hierarchies vs nothing, for example. Speed &#8211; some systems are fast, some slow. <br /><br />When deciding how much to put in a base system, I&#8217;m also aware that not everyone will have the skills to get everything set up, and I&#8217;m trying to figure out where to draw the line between simplicity and complexity.<br /><br />How important is full text search? Fuzzy searching (to handle spelling mistakes by the end user)? Semantic/AI-enhanced search? Searching of any provided support documents? Speed? Any type of analytics/usage tracking?<br /><br />How are you handling techfinder-type systems in your office operations? Would you use them more if they provided more functionality?<br /><br />If you&#8217;re interested in learning more about the system under development, let me know. I&#8217;ll likely post about it here in the coming weeks, but happy to discuss any specific details before then.]]></content:encoded>
					
					<wfw:commentRss>https://kimsal.com/blog/2024/11/11/open-source-techfinder/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Logging impersonation data</title>
		<link>https://kimsal.com/blog/2024/08/19/logging-impersonation-data/</link>
					<comments>https://kimsal.com/blog/2024/08/19/logging-impersonation-data/#respond</comments>
		
		<dc:creator><![CDATA[kimsal]]></dc:creator>
		<pubDate>Mon, 19 Aug 2024 12:01:01 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://kimsal.com/blog/?p=135</guid>

					<description><![CDATA[Two Laravel packages I use often are packages to impersonate a user and packages to log activity (including model changes). Both are useful, but there&#8217;s typically one piece missing. Activity log packages will log *who* initiated a change, but not who the person may have been impersonated by. If I log in as me, then...]]></description>
										<content:encoded><![CDATA[
<p>Two Laravel packages I use often are packages to impersonate a user and packages to log activity (including model changes).</p>



<p>Both are useful, but there&#8217;s typically one piece missing.  Activity log packages will log *who* initiated a change, but not who the person may have been impersonated by.  If I log in as me, then impersonate user X, then make changes, the activity log will typically just show &#8216;user X made these changes&#8217;.  However, it was really *me* doing it on behalf of person X.</p>



<p>There&#8217;s a few ways one could handle this, and I&#8217;d initially looked at request middleware (and still might move in that direction), but with the Spatie activitylog package, I added a &#8216;pipe&#8217; during the call to initialize logging options. </p>



<p>Here&#8217;s a bit of code I use with the <a href="https://spatie.be/docs/laravel-activitylog/v4/introduction">Spatie Activitylog package</a> and the <a href="https://github.com/404labfr/laravel-impersonate">laravel-impersonate package.</a></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>&lt;?php

namespace App\Services\ActivityLog\Pipes;

use Closure;
use Spatie\Activitylog\Contracts\LoggablePipe;
use Spatie\Activitylog\EventLogBag;

class AddImpersonatorPipe implements LoggablePipe
{
    public function __construct(protected string $field)
    {
    }

    public function handle(EventLogBag $event, Closure $next): EventLogBag
    {
        $manager = app(&#39;impersonate&#39;);
        if ($manager-&gt;isImpersonating()) {
            $impersonatorId = $manager-&gt;getImpersonatorId();
            $event-&gt;changes[$this-&gt;field] = $impersonatorId;
        }

        return $next($event);
    }
}
</code></pre></div>



<p>As you can see, I&#8217;m simply adding the originating impersonator ID to the &#8216;change&#8217; list.  It&#8217;s not a separated activity log property, nor a separate column in the database table where logged activity lives.  That&#8217;s certainly possible, but I was trying to handle this without any database changes at this point.</p>



<p>On each model I log changes on, I have a &#8216;getActivitylogOptions&#8217; method (via a trait) that adds the pipe code above to the logger&#8217;s pipeline.</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>    public function getActivitylogOptions(): LogOptions
    {
        self::addLogChange(new AddImpersonatorPipe(&#39;impersonator_id&#39;));

        return LogOptions::defaults()
            -&gt;logAll();
    }</code></pre></div>



<p>What I&#8217;ve not tested yet is if non-model &#8216;activity&#8217; log behaviour would still have the impersonation processing in place if it&#8217;s no model is invoked in the request.  For example, just logging that an api call was made.  However, pretty much every request has at least the User model invoked, which has the getActivitylogOptions() call in place, so I&#8217;m not sure there&#8217;s ever a time that would not be invoked.</p>



<p>Also&#8230; reviewing this out loud&#8230; the self::addLogChange() call may be being invoked once per object instantiation (or at least initial model instantiation) which may be redundant.  I&#8217;ll need to investigate more.</p>



<p>There doesn&#8217;t seem to be a standard process for this, but it&#8217;s definitely useful information to log, even if you choose to log it a separate way.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://kimsal.com/blog/2024/08/19/logging-impersonation-data/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Laravel query performance &#8211; toBase</title>
		<link>https://kimsal.com/blog/2024/02/02/laravel-query-performance-tobase/</link>
					<comments>https://kimsal.com/blog/2024/02/02/laravel-query-performance-tobase/#respond</comments>
		
		<dc:creator><![CDATA[kimsal]]></dc:creator>
		<pubDate>Fri, 02 Feb 2024 16:11:03 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://kimsal.com/blog/?p=129</guid>

					<description><![CDATA[I&#8217;ve scoured many of the top results for &#8220;Laravel performance&#8221; in google, bing, duck duck go, etc&#8230; and am flummoxed that one of the key things to help boost Laravel performance is almost never mentioned. Specifically, I&#8217;m talking about the toBase() method in the query builder. The toBase() method will instruct the query builder to...]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve scoured many of the top results for &#8220;Laravel performance&#8221; in google, bing, duck duck go, etc&#8230; and am flummoxed that one of the key things to help boost Laravel performance is almost never mentioned.  Specifically, I&#8217;m talking about the toBase() method in the query builder.</p>



<p>The toBase() method will instruct the query builder to skip the step of turning the query results in to full blown Eloquent objects, and instead, return standard PHP classes &#8211; the &#8216;stdClass&#8217;.</p>



<p>The resulting objects are bare.  You can&#8217;t call any methods on them, because there are none to call.</p>



<pre class="wp-block-code"><code>Client::query()-&gt;where('owner_id', 7)-&gt;get();</code></pre>



<p>let&#8217;s say this returns 70 client records.  Each of those needs to be instantiated in to an Eloquent object.  If you *need* that functionality &#8211; for example, you&#8217;re going to loop through this client list and call some operation on each client (sending a notification, or attaching resources, or whatnot..) the full Eloquent objects may be needed.</p>



<p>More often than not, however, I&#8217;ll see </p>



<pre class="wp-block-code"><code>$clients = Client::query()-&gt;where('owner_id',7)-&gt;get();
return response()-&gt;json(&#91;'clients'=&gt;$clients);</code></pre>



<p>You&#8217;ve just iterated through 70 records, created new Eloquent objects, then immediately set about converting them to JSON.</p>



<p>toBase() saves a tremendous amount of CPU time in the case above</p>



<pre class="wp-block-code"><code>$clients = Client::toBase()-&gt;query()-&gt;where('owner_id',7)-&gt;get();
return response()-&gt;json(&#91;'clients'=&gt;$clients);</code></pre>



<p>I&#8217;m not going to give full detailed benchmarks here &#8211; you can simply try this out for yourself.</p>



<p>I can say that with a minimally complex &#8216;Client&#8217; object and about 600 records in the database:</p>



<pre class="wp-block-code"><code>$c = Client::query()-&gt;toBase()-&gt;get();</code></pre>



<p>is roughly twice as fast as</p>



<pre class="wp-block-code"><code>$c = Client::query()-&gt;get(); </code></pre>



<p>A 50% speed up is nothing to sneeze at.</p>



<p>I recently took an API endpoint having to do with retrieving client records and associated addresses.  With a little bit of rewriting &#8211; including toBase() &#8211; this went from 1.1 seconds down to ~200ms.  This gets called multiple times per minute at heavy times, and having faster response times for the end users has been duly appreciated.</p>



<p>I believe this can be achieved in Doctrine with &#8220;<a href="https://www.doctrine-project.org/projects/doctrine-orm/en/2.17/reference/dql-doctrine-query-language.html#hydration-modes" data-type="link" data-id="https://www.doctrine-project.org/projects/doctrine-orm/en/2.17/reference/dql-doctrine-query-language.html#hydration-modes">HYDRATE_ARRAY</a>&#8221; mode, and likely similar in other tech stacks (hibernate, etc).</p>
]]></content:encoded>
					
					<wfw:commentRss>https://kimsal.com/blog/2024/02/02/laravel-query-performance-tobase/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
