PHP oci_bind_by_name Caveat (a.k.a. a reminder to read the documentation)

This is readily apparent when you actually read the documentation for oci_bind_by_name … but, if you quickly skim through the doc and get mostly what it’s telling you, you can lose data when implementing oci_bind_by_name in your code. That’s because the variable that gets bound into the name is a reference. It’s right there in the description

And again in the more verbose part of the description

Well? They really mean It!

I usually iterate through arrays with for $x=0; $x<count($arrayOfData);$x++ — which is fine because $arrayOfData[$x] is a valid location in memory that contains the data I want. I had a program, though, that accepted user input. Input that frequently contains a bunch of extraneous newline characters in the middle of the data. To avoid making the users clean up their input, I just threw an array_filter on the input. But I don’t need the reorganized data, so I didn’t “waste” the cycles to re-index the filtered array. Instead, I used

$iIterator=0;
for( $itemInput in array_filter( $arrayOfData ) ) {
     oci_bind_by_name($stmt, ‘:placeholder’.$iIterator++, $itemInput);
}

Which is fine in most circumstances. But, with oci_bind_by_name … the memory address where $inputItem happened to be stashed is bound to the names :placeholder0, :placeholder1, …, :placeholdern – that memory address happened to still contain the last element of arrayOfData I happened to stash there because the server isn’t so busy that memory is being reallocated a fraction of a second later. But this loop does not bind each element of arrayOfData to its corresponding placeholder — instead of saying “select * from MyTable where ID IN (1,2,3,4,5)” … I had “select * from MyTable where ID IN (5,5,5,5,5)”.

In this case you need to use the array index

for( $x = 0; $x < count( $arrayOfData ); $x++ ) {
     $strPlaceholderName = ":inputclli".$x;
     oci_bind_by_name($stmt, $strPlaceholderName, $arrayOfData[$x]);
}

So each unique memory address is bound to a placeholder.

PHP Curl and Mangled Headers

I have PHP code that calls out to a SOAP endpoint, gets the response XML, and then does stuff. Well … that was the plan anyway. What actually happened was nothing. No error set, but the response string was null. Even adding curl debugging produced nothing. Fortunately, the endpoint is a sandbox one and thus available on http without encryption. I was going to do a network trace, so I needed to run the script from my computer. Aaaand STDOUT is where the curl debugging was going, not (as I assumed) STDERR. And look, there actually was an error. 400 bad request (how did that not come through to curl_error?!?) — and it became immediately obvious what my problem was — the content length is a quoted string instead of an integer.

Sigh! Copy/paste error where I built out my header. Cleared the escaped quotes from around the strlen call and, voila, my code works perfectly.

But a note to my future self — when PHP’s curl calls aren’t producing any output … try running it from the CLI. Or remember to add the instruction to have verbose output in STDERR!

curl_setopt($ch, CURLOPT_STDERR, $verbose);

 

Minimum wage profit sharing experiment

A pizza joint shared its profits with its employees and that meant the employees made $78 an hour. I’m curious if the pizzeria used a realistic calculation for profit. If so? This makes the “I cannot afford to increase my starting pay rate to attract employees, the problem is the gov’t is making people all lazy and stuff” argument clearly disingenuous.

Profits are what you make after paying for the business’s expenses — so the ingredients, power, water, advertising, insurance, employee benefits, real estate, business loans, taxes, and such are all taken out before you call it a profit. Good accounting includes future predictable expenses as well — the facility is going to require occasional sprucing up, maintenance expenses pop up, at some point they’ll need to replace the pizza oven or refrigeration units. Just like a personal budget should include “replacement car, every 10 years, that means I need to accrue $5 a day to fund that replacement” … these expenses should be estimated out and included in the net/profit calculations. It’s possible they used a far simpler algorithm for computing profits — net proceeds minus food cost (which is something most restaurants track very well) … which would render my calculations here meaningless. But I’m going to assume “profits” actually means profits in the accounting sense.

In a lot of businesses, the owner takes a salary too — no idea if owner, well, takes a salary in the first place but if they took their salary out before calculating the daily profit. I am going to assume the owner’s salary was not already deducted. Then $78 an hour isn’t sustainable because the owner needs to eat too, but the owner could take $50 an hour per employee and still pay everyone $28 an hour.

Not that the owner gets $50 an hour for being open, but $50 a man-hour worked by any employee. Think about that for a minute — say they’ve got three people doing prep from 1P-3P and five people working when open from 3p-10p, then thee people staying on for close from 10-11P … that’s 44 man-hours worked that the owner’s keeping at $50 a man-hour. Owner keeps $2,200 each day, plus has his business has all of its expenses covered. For each of the 300 days a year they are open (since they’re open 7 days a week, this is a low estimate too), that’s $660,000.

Say I’m overestimating the owner’s share a lot — let’s cut that in half. Maybe they did the profit sharing on an unusually profitable day. Maybe they did it on a weekend day where they’re open a few more hours. Let’s say the owner can keep $1,000 a day . That means the owner pays the staff $28 an hour, pays for all of the business expenses, *and* has absolute minimum $300,000 in profit.

Sustainability and meat

I’ve seen a lot of info on the incredible (bad) environmental impact of meat production — the amount of land and water it takes to grow a cow is staggering. Something like 77% of the world’s land that is used for agriculture is used to graze livestock. Lamb/mutton, beef, and cheese (mostly cows still) top the list of inefficient ways to produce a gram of protein. I see plant-based fake meat (Beyond, Impossible, etc) marketing toward this — a lower impact way to enjoy a burger. I’d like to see more focus on using existing food sources to reduce the amount of meat contained in meals — rewriting recipes to reduce meat consumption.

I make a lot of meals where meat is a small component of the dish — additions instead of subtractions from the normal recipe. Enhancements instead of restrictions. Turkey burgers with lots of spinach, some feta, and garlic. Stroganoff with three different types of mushrooms, plenty of onions, and a bit of beef. Tacos and wraps loaded with rice, beans, tomatoes, onions, avocado, cheese, grilled corn, and a little grilled chicken. Sloppy joe sandwiches where half of the ground beef is replaced with red lentils. Pasta salad that’s more salad than pasta with a little bit of diced pepperoni. We have completely vegetarian meals, and I use the Beyond/Impossible substitutes to make meatball subs or sausage pizza. That all balances out the grilled steak or rack of ribs some other day.

New Hatchling Countdown

One of our chickens, Astra, has become broody. We had been getting her out of the nest once a day to eat/drink/defecate and collecting the eggs. But it’s getting on in the year, and we wanted to raise more broilers. We decided to take this opportunity to hatch some new chickens — not all 100% American Bresse, but still chickens. It seems like the chickens have a really cool agreement that she’s in charge of incubating eggs. She sits on the nest all day, but seemingly gets up and allows other chickens to lay eggs that she’ll keep safe.

Anya counted 12 eggs under Astra — 2 from Sunshine (Buff Orpington), 4 from Queenington (Green Queen), and 6 from the Bresse. She’s got each egg marked so we can collect any newly laid eggs … and we should have new chicks in about 21 days — around August 3rd. We’re bringing her food and water a few times a day, so (hopefully) she’ll stay healthy over the next couple of weeks.

Towel Money

People seem to assume the fact you’ve managed to amass money to be a fact that vouches for you … like you cannot be inept / senseless / bad at managing money because, look at that, you’ve got money. Doesn’t matter if you inherited (and subsequently lost much of) a bigger load of money, managed to injure yourself in some stunningly original way that requires some company to fork over millions, tripped over your untied shoelaces and discovered the lost Civil War gold. You have money, so you’re awesome at life.
It makes me think of the towel in the Hitchhiker’s Guide series — encounter someone while you’re wandering and, if they’ve managed to keep track of something so trivial as their towel, then they’ve obviously got it together.

Docker and Windows — Unable to Allocate Port

On the most recent iteration of Windows (20H2 build 19042.1052) and Docker Desktop (20.10.7 built Wed Jun 2 11:54:58 2021), I found myself unable to launch my Oracle container. The error indicated that the binding was forbidden.

 

C:\WINDOWS\system32>docker start oracleDB
Error response from daemon: Ports are not available: listen tcp 0.0.0.0:1521: bind: An attempt was made to access a socket in a way forbidden by its access permissions.
Error: failed to start containers: oracleDB

Forbidden by whom?! Windows, it seems. Checking excluded ports using netsh:

netsh int ipv4 show excludedportrange protocol=tcp

Shows that there are all sorts of ports being forbidden — Hyper-V is grabbing a lot of ports when it starts. To avoid that, you’ve got to add a manual excluded port for the one you want to use.

To reserve the port for your own use, disable Hyper-V (reboot), add a port exclusion, and enable Hyper-V (reboot)

REM Disable Hyper-V
dism.exe /Online /Disable-Feature:Microsoft-Hyper-V 
REM REBOOT ... then add an exclusion for the Oracle DB Port
netsh int ipv4 add excludedportrange protocol=tcp startport=1521 numberofports=1 
dism.exe /Online /Enable-Feature:Microsoft-Hyper-V /All
REM REBOOT again

Now 1521 is reserved for Oracle

ADO – Migrating a Repository to Azure Repos (and keeping your commit history)

The most direct way to migrate a repo into Azure Repos is to create a new, blank repository. This may mean making a new project. From the organization’s main page, click “New project”

Or it may mean making a new repo in an existing project. From an existing repo, click the drown-down next to the repo name and select “New repository”

Name the repository but don’t add a README. We want a blank repo

Note the URL to the repository – in this case, it’s https://ado0255@dev.azure.com/ado0255/History%20Test/_git/Another%20History%20Test

Find the URL for your existing Git repo – if you cd into the project’s folder and run “git remote -v”, you will get a list of the repos. Make a new folder somewhere – this is a temporary staging area to move the data from your existing repo over to the new Azure Repo. Change directories into your new folder. Run git clone –mirror URLToOldRepo

You will see data being downloaded from your git server.

Change directories into the folder that just got downloaded. You won’t see your code like you normally do when you clone a git repo. You’re looking at the underlying git stuff that makes up the repo. You’re code is all in there, as are all of the branches and commit history.

Now add the new Azure Repo as a remote – in this case, I’m naming the remote “ado”. Then run “git push ado –all” to push everything up to the new Azure Repo.

Stuff will transfer – you may be prompted to log into your ADO repository first. Eventually, you’ll see new branches being created on the remote and the process will complete.

Refreshing the Azure Repo, you’ll now see the files.

Selecting “Commits” will display the commit history.

Anyone else using the repo will need to add the new remote. Use “git remote rm origin” to remove the existing origin, then use “git remote add origin url” to add the new Azure Repo as origin.

ADO – Cleaning up test repos and projects

I find the process to delete repositories and projects to be nonintuitive. Since I create a lot of projects and repos for testing and documentation, it’s nice to be able to clean them up when I’m done!

To delete an Azure Repo, navigate to a repo and select the drop-down next to the repo name. Select “Manage repositories”

With your mouse over a repository, there’s a hamburger menu at the right-hand side of the listing. Click it and select “Delete”

You’ll need to type the repository’s full name to activate the delete button.

To delete a project, go to the organization’s home page and select “Organization Settings” from the lower left-hand corner of the screen.

Select “Projects” from the left-hand navigation bar

With your mouse over the project listing, you’ll have a hamburger menu. Click it and select “Delete Project”

You’ll need to type the project name to activate the delete button.

 

County Building Department

It strikes me, every time I talk to someone from the auditor’s office or the building department, that county officials must talk to a lot of people after-the-fact … like they built a shed, someone noticed it, and now they’re going through the permitting process for that shed. Because they always seem surprised that I’m in the planning stages of a project and am ringing them up to make sure I’m doing all the right things in the right order.

My note-to-self for the day — while the Medina County Building Department does permit fences over 6′, they do not require anything for agricultural buildings and fences. If you’ve got an agricultural exemption from the Township for a building, they’ll happily agree that the fence around / next to that building is for agricultural use as well. (For non-agricultural fences, you fill out the residential building form and specify the perimeter of the fence for the sq ft area and not the square footage enclosed by the fence).

Thus, I’ve concluded that the steps to build a bigger chicken coop and a pasture are:

  1. Submit the agricultural exemption form to the township
  2. Once it is approved, e-mail a copy to the Medina County Building Department for their records (so when someone rings them up about some construction that doesn’t look like it should be there, the don’t have to waste a day driving out to look at a chicken coop)
  3. Build it

Our coop and greenhouse shouldn’t need a permit from the county because the size is under 200 sq ft.