-
Posts
4,705 -
Joined
-
Last visited
-
Days Won
179
Everything posted by kicken
-
I missed a comma between zip and the distance calculation in the inner select query. As a result it's parsing it as a function call instead of as separate columns. Add a comma between zip and ( in the inner select.
-
Don't do that. Not in the actual table at least. Some people recommend this stupidity to try and avoid name collisions in their queries (such as two tables have a Label column) but such issues can be easily handled using the table.column syntax in your query rather than cluttering up column names in the table. SELECT o.Label as o_label, s.Label as s_label FROM order o INNER JOIN status s ON s.Id=o.Status One of the applications I work on was original designed using a scheme like that where every column has a table specific prefix to it and it's super annoying (long names, broken autocomplete) for no real benefit. I've been slowly undoing that when I can and just giving the columns nice simple names. I'd also suggest just using the full table name in your constraint names rather than some alias. It makes things very clear when someone 6 months later needs to decipher things.
-
There are a few reasons you might want to know the name (index hints, changing the index, etc), but all are fairly rare so it's not really important that you name them explicitly. If you need the name, you could always look it up later. I tend to name my indexes and constraints just to be explicit. I use the format IX_table_name_column_name to keep things simple.
-
Maybe just adding another join to your "complicated" query that connects the articles table.
-
If it's a problem, I'd probably just drop the constraint. If it's important, handle it in your code otherwise don't worry about it. I'm not sure what a different database might provide as solutions to your issue. Only other one I really use is SQL Server and haven't looked into anything like this. The closest situation I have is with a DisplayOrder column which is typically set to a unique 1..n range value by the application. Having a duplicate isn't really a problem though so I don't bother enforcing that with a constraint or code.
-
Is closure and an arrow function just about the same?
kicken replied to NotionCommotion's topic in Javascript Help
More or less for most use cases, as far as I understand them. Arrow functions do not create their own this and arguments variables so they will inherit those of the parent context. This is usually what a person wants to happen anyway so it makes them more convenient to use and their syntax is shorter. There are a couple other things they handle differently according to MDN. new.target and super are also not defined, neither of which are likely to be used all that often imo. I didn't even know new.target was a thing until I read that page. Not having their own this property makes them not useful in some situations, and apparently they can't be used as a generator using yield for some reason. -
Move your distance calculation into the SELECT list so it's a field and remove the WHERE clause. Then wrap your query in an outer query and apply your WHERE and ORDER BY clauses to that query. While your at it switch to a prepared query with parameters rather than concatenation to prevent and potential user input problems. Looks like maybe your using mysqli so I showed that option below. $sql = ' SELECT DISTINCT zip FROM ( SELECT zip (3958*3.1415926*sqrt((latitude-?)*(latitude-?) + cos(latitude/57.29578)*cos(?/57.29578)*(longitude-?)*(longitude-?))/180) as distance FROM zip_codes ) r WHERE r.distance < ? ORDER BY r.distance '; $stmt = $this->db->prepare($sql); $stmt->bind_param('dddddd', $zip->latitude, $zip->latitude, $zip->latitude, $zip->longitude, $zip->longitude, $radius); $stmt->bind_result($rowZip); $stmt->execute(); $zip_codes = []; while ($stmt->fetch()){ $zip_codes[] = $rowZip; } return $zip_codes;
-
PHP pages updates slow to reflect in browser
kicken replied to PatRoy's topic in PHP Installation and Configuration
Open your browsers development console (F12) and disable the use of cache (Network tab -> [X] Disable Cache) and see if that resolves the problem. If it does, you know it's a browser cache issue. If it doesn't, you know the problem lies elsewhere. Have you experienced the issue in multiple browsers, or have you only been using one? -
Yes. Instead of selecting the ID you could select the is_free field (if you do that) to make the permission check simple in that case. You could just select the article content too if you wanted. Unless your going to have really big articles it probably wouldn't make a difference doing it at the start vs after the permission check. You'd save a query at the expense of more memory usage.
-
The issue I think requinix is trying to point to is that if the article doesn't even exist, you don't want to show a 'This is a premium article.' error, you want to display a proper 404/article not found error. If you just display 'This is a premium article' in both cases, that will lead to confusion and frustration for your users. If the article doesn't exist there's no need to do any extra work for determining if the user has a matching entitlement to go with that article so you can cut all that processing out and not waste time on it. Only if the article exists do you then need to worry about processing entitlements. Whether or not the article being free involves entitlement checks depends on what kind of solution you end up using for determining free. If it's just a flag on the article then you can skip the entitlements checks if that flag is true. If you do some free plan that everyone is a member of by default then you will need to do the checks. So your logic structure would essentially be best setup as Does article exist? No? Display 404 and quit Is the article free? Yes? Display article and quit Is the user entitled to the article? No? Display entitlement error and quit Display article. Unless you have a compelling reason to do otherwise, I'd probably just start out with requinix's is_free column suggestion to make things easy. If in the future you decide that's not good enough then you can refactor it into something else. I spent a few minutes trying to think of a reason not to do a simple is_free column and couldn't really come up with any scenario where more than that would be necessary. Don't nitpick your query count. One vs two queries will make absolutely zero noticible difference in the runtime of your code. If two queries make the program flow nicer, use two queries.
-
How do you sync PHP code with Social Media, to show 'followers'?
kicken replied to simona6's topic in PHP Coding Help
https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-users-lookup You have to authenticate your request, you can't just load up the URL. -
How do I submit a single form from multiple forms on the same page.
kicken replied to imgrooot's topic in PHP Coding Help
Just make sure each form has it's own set of <form> tags and inputs. Add hidden inputs that you can use to differentiate the forms if necessary. -
Because it highlights better specifically what you feel was missed. Maybe the person thought they answered your question but you're still unsure. It's better to just quote the relevant question/comment and ask for a clear answer, maybe with additional details. Post numbers might be nice, but there's plenty of ways to deal with not having them. I'm sure someone here could if motivated to, but it's not always that easy to just hack the code to some third-party application. Since a lack of post numbers is not a huge problem, there's not much motivation to spend a bunch of time fixing it.
-
query may be used when there is no need to prepare because there are no bound parameters. In your cases though your $sqlFilters variable is being used in the query and it does potentially have bound parameters defined in it. As such, you need to use prepare instead of query. Note you can always use prepare/execute if you want and not worry about the difference. query is just a handy shortcut for simple queries.
-
Forget variable-variables even exist, you pretty much never want to use them. While your at it, forget that global exists, you shouldn't be using that either, convert those values to extra parameters. Your function should accept the data it needs as parameters, then return a result. Your query should also use prepared statements where possible. Your sid value can be bound as a parameter to protect against injection. Your column and table names cannot be bound so you need to ensure your safe there via other means. If all those values are hard-coded by you and not accepted as user input then that's fine. If they come from user input you need to make changes such as using a whitelist. function gimme($connection, $table, $column, $sid){ $sql - 'SELECT '.$column.' FROM '.$table.' WHERE sid=?'; $stmt = $connection->prepare($sql); $stmt->bind_param('s', $sid); $stmt->execute(); $stmt->bind_result($result); if ($stmt->fetch()){ return $result; } return null; } Now with that function, you just call it with the relevant parameters and assign the result to whatever variable you want. $name = gimme($mysqli, 'users', 'name', $sid); echo $name;
-
No, it doesn't. addStringAttachment $mailer->addStringAttachment($adfData, 'lead.adf');
-
Easy Question (I hope): PHP Include File with Links
kicken replied to MysticKnight's topic in PHP Coding Help
Use root-relative links. <a href="/primary-directory/subsection-one/">Subsection One</a> The leading slash means the link is relative to the root of the domain name so it doesn't matter which directory you're in. -
2020 LDAP channel binding and LDAP signing requirement for Windows
kicken replied to FredPenner's topic in PHP Coding Help
We have an app that supports LDAP auth (but we don't use it) so I tried to setup an active directory VM and apply those settings to test this. Assuming I did it right, then it seems that so long as you connect to the LDAP server using TLS then everything is fine. This means either using a ldaps:// url in ldap_connect or using ldap_start_tls after connecting. Only unencrypted connections stopped working for me when applying those configuration changes. I'm not well-versed in LDAP or Active Directory so there's a chance I didn't do something right in the setup/configuration but I think I got it done right so I'd probably not be too worried about the change, just make sure you use an encrypted connection to the ldap server. -
Best way to set up a "Checkout Transaction"?
kicken replied to SaranacLake's topic in PHP Coding Help
You just do whatever you need to do to log them in as part of the member creation step. There's no need to make the user go through some login process. $userId = createMemberRecord(); $_SESSION['activeUser'] = $userId; After that, create your order and process the payment. Every member on your site should have some sort of page they can access that will show them their order/payment history and allow them to update their payment details / change subscription. If the payment fails, you send them to that page with an appropriate error message and a form to re-enter their payment details. -
Best way to set up a "Checkout Transaction"?
kicken replied to SaranacLake's topic in PHP Coding Help
If this is true, then why is there a problem? Make a users account, process their payment, and move on. If the payment fails just don't grant them any entitlement. Just because you put everything on one page for the user to enter their information doesn't mean you have to process it as an all-or-nothing thing. You can still process it as separate actions and handle any problems appropriately. For example, if the payment fails keep the account information and just send them to a payment form (similar to what you'd do for a member who's subscription is expired). You need a way for someone to be registered without an active subscription. Whether or not that user can do anything is up to you. Allowing a little bit of bonus abilities for simply registering is a common and good way to get people to hop on board. Registered users are easier to track and manage than a bunch of anonymous people. If you ever want to do something like sell ads or potentially the website as a whole having statistics about usage will be very beneficial and registered users can give you some good statistics. -
Best way to set up a "Checkout Transaction"?
kicken replied to SaranacLake's topic in PHP Coding Help
Free doesn't mean anonymous though, and that's the point. You should have a way for members to create an account without having paid. You kind of need one anyway for a scenario that it seems you haven't thought about yet. What happens when someone does sign up and become a member and pays your $50/year fee, but then next year decides they don't want to pay again. Now they are in exactly the situation you're trying to avoid. They have a member account but haven't paid you. Can't login because apparently that isn't allowed and can't re-register because their email is already in use. You need to accept that once you decide to start charging people money things will get more complex and you have to deal with it. The simplest thing to do is like requinix is saying and separate your registrations from your subscriptions. Even if you don't want to do that middle tier as he suggestion and keep your cut off, make the determination of what a user has access to based on whether they have paid, not on whether they have registered. This could be something simple like a HasPaid flag on their account (limited use) or more complex like a separate table to track subscription data (more flexible but more complex). -
I've not used any of the listed options. The couple API's I've worked on have just used Symfony since that's what I typically use for other applications. It's worked fine for me for the most part. There are various bundles dedicated to implementing REST API's that may be useful (ie, FOSRestBundle).
-
Run their input through strtolower before comparing. You may also want to trim.
-
How does PHP determine to create a new copy of a copied array?
kicken replied to NotionCommotion's topic in PHP Coding Help
I re-wrote your test script in a way that provides data that is easier to interpret (IMO) and doesn't require a database: <?php function display($memory){ $s = ''; $prev = array_fill(0, 5, 0); $headers = ['Message', 'Usage', 'Peak', 'Usage (Real)', 'Peak (Real)', 'Usage Delta', 'Peak Delta', 'Usage Delta (Real)', 'Peak Delta (Real)']; foreach ($memory as $m){ $data = [ $m[0] , fmt($m[1]) , fmt($m[2]) , fmt($m[3]) , fmt($m[4]) , fmt($m[1] - $prev[1]) //Usage delta , fmt($m[2] - $prev[2]) //Peak delta , fmt($m[3] - $prev[3]) // Usage delta (real) , fmt($m[4] - $prev[4]) // Peak delta (real) ]; $s .= '<tr><td>' . implode('</td><td>', $data) . '</td></tr>'; $prev = $m; } echo '<table><thead><tr><th>' . implode('</th><th>', $headers) . '</th></tr></thead>'; echo '<tbody>' . $s . '</tbody></table>'; } function logger($msg){ global $memory; $usage = round(memory_get_usage()); $peak = round(memory_get_peak_usage()); $usageReal = round(memory_get_usage(true)); $peakReal = round(memory_get_peak_usage(true)); $m = [$msg, $usage, $peak, $usageReal, $peakReal]; $memory[] = $m; } function fmt($size){ static $suffix = ['B', 'KB', 'MB', 'GB']; $idx = 0; $negative = $size < 0; $size = abs($size); while ($size > 1024 && $idx < 3){ $size /= 1024; $idx++; } return sprintf('%0.2f %s', $size * ($negative ? -1 : 1), $suffix[$idx]); } function getData(){ $data = []; for ($i = 0; $i < 11; $i++){ $data[] = file_get_contents('1kb.txt'); } return $data; } class DataHolder { private $data; public function __construct(array $data){ $this->data = $data; //$this->data[]='bla'; <-- I believe this will force PHP to make a new copy of the array but I am not doing so } } $memory = []; logger('initial'); $bigArray1 = getData(); logger('after creating first data set'); $bidArrayCopy = $bigArray1; logger('after copying first data set'); $bigArray1[] = 'hello'; logger('after modifying first data set'); $dataHolders = []; for ($i = 1; $i <= 5; $i++){ logger("Copy Loop $i - Before"); $bigArray = getData(); $dataHolders[] = new DataHolder($bigArray); logger("Copy Loop $i - After"); } for ($i = 1; $i <= 5; $i++){ logger("Set Loop $i - Before"); $dataHolders[] = new DataHolder(getData()); logger("Set Loop $i - After"); } display($memory); Here's the results using values similar to the 11MB size you got. +--------------------------------+-----------+-----------+--------------+-------------+-------------+------------+--------------------+-------------------+ | Message | Usage | Peak | Usage (Real) | Peak (Real) | Usage Delta | Peak Delta | Usage Delta (Real) | Peak Delta (Real) | +--------------------------------+-----------+-----------+--------------+-------------+-------------+------------+--------------------+-------------------+ | initial | 419.97 KB | 456.24 KB | 2.00 MB | 2.00 MB | 419.97 KB | 456.24 KB | 2.00 MB | 2.00 MB | | after creating first data set | 11.45 MB | 11.47 MB | 22.00 MB | 22.00 MB | 11.04 MB | 11.03 MB | 20.00 MB | 20.00 MB | | after copying first data set | 11.45 MB | 11.47 MB | 22.00 MB | 22.00 MB | 376.00 B | 0.00 B | 0.00 B | 0.00 B | | after modifying first data set | 11.46 MB | 11.47 MB | 22.00 MB | 22.00 MB | 1.05 KB | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 1 - Before | 11.46 MB | 11.47 MB | 22.00 MB | 22.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 1 - After | 22.50 MB | 22.52 MB | 44.00 MB | 44.00 MB | 11.04 MB | 11.05 MB | 22.00 MB | 22.00 MB | | Copy Loop 2 - Before | 22.50 MB | 22.52 MB | 44.00 MB | 44.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 2 - After | 33.55 MB | 33.56 MB | 66.00 MB | 66.00 MB | 11.04 MB | 11.04 MB | 22.00 MB | 22.00 MB | | Copy Loop 3 - Before | 33.55 MB | 33.56 MB | 66.00 MB | 66.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 3 - After | 44.59 MB | 44.61 MB | 88.00 MB | 88.00 MB | 11.04 MB | 11.04 MB | 22.00 MB | 22.00 MB | | Copy Loop 4 - Before | 44.59 MB | 44.61 MB | 88.00 MB | 88.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 4 - After | 55.63 MB | 55.65 MB | 110.00 MB | 110.00 MB | 11.04 MB | 11.04 MB | 22.00 MB | 22.00 MB | | Copy Loop 5 - Before | 55.64 MB | 55.65 MB | 110.00 MB | 110.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 5 - After | 66.68 MB | 66.69 MB | 132.00 MB | 132.00 MB | 11.04 MB | 11.04 MB | 22.00 MB | 22.00 MB | | Set Loop 1 - Before | 66.68 MB | 66.69 MB | 132.00 MB | 132.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Set Loop 1 - After | 77.72 MB | 77.74 MB | 154.00 MB | 154.00 MB | 11.04 MB | 11.04 MB | 22.00 MB | 22.00 MB | | Set Loop 2 - Before | 77.72 MB | 77.74 MB | 154.00 MB | 154.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Set Loop 2 - After | 88.77 MB | 88.78 MB | 176.00 MB | 176.00 MB | 11.04 MB | 11.05 MB | 22.00 MB | 22.00 MB | | Set Loop 3 - Before | 88.77 MB | 88.78 MB | 176.00 MB | 176.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Set Loop 3 - After | 99.81 MB | 99.83 MB | 198.00 MB | 198.00 MB | 11.04 MB | 11.04 MB | 22.00 MB | 22.00 MB | | Set Loop 4 - Before | 99.81 MB | 99.83 MB | 198.00 MB | 198.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Set Loop 4 - After | 110.86 MB | 110.87 MB | 220.00 MB | 220.00 MB | 11.04 MB | 11.04 MB | 22.00 MB | 22.00 MB | | Set Loop 5 - Before | 110.86 MB | 110.87 MB | 220.00 MB | 220.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Set Loop 5 - After | 121.90 MB | 121.92 MB | 242.00 MB | 242.00 MB | 11.04 MB | 11.04 MB | 22.00 MB | 22.00 MB | +--------------------------------+-----------+-----------+--------------+-------------+-------------+------------+--------------------+-------------------+ The reason it only goes up a bit is because the individual items between the two arrays are shared. As such the new copy only needs enough memory to store pointers to all the original row zvals + 1 more. If you did a change that would affect every row then you'd end up with more usage. The size grows by 11MB because you're calling getData and generating a new 11MB data array. When you pass that to your DataHolder object all that happens is a new pointer to the existing zval is created. On the next loop cycle getData generates a new 11MB array with it's own zval and the $bigArray pointer is updated to point to that new zval (and no longer to the one being used in the previous DataHolder object. The exact same thing happens in the second block where you pass the data directly to the constructor, you just eliminate the bigArray variable. getData generates a new zval for the array and the new DataHolder object gets a pointer to it. Notice how both blocks see a growth of 11MB per loop. The memory usage keeps growing as the script runs because your keeping all your zvals alive by way of the DataHolder objects stored in the $dataHolders array. If you were not appending them to an array then PHP would end up just re-using the memory and your usage would stay more constant at 22MB. 11MBish for $bigArray1 and $bigArrayCopy and 11MB for $bigArray. +--------------------------------+-----------+-----------+--------------+-------------+-------------+------------+--------------------+-------------------+ | Message | Usage | Peak | Usage (Real) | Peak (Real) | Usage Delta | Peak Delta | Usage Delta (Real) | Peak Delta (Real) | +--------------------------------+-----------+-----------+--------------+-------------+-------------+------------+--------------------+-------------------+ | initial | 419.97 KB | 456.24 KB | 2.00 MB | 2.00 MB | 419.97 KB | 456.24 KB | 2.00 MB | 2.00 MB | | after creating first data set | 11.45 MB | 11.47 MB | 22.00 MB | 22.00 MB | 11.04 MB | 11.03 MB | 20.00 MB | 20.00 MB | | after copying first data set | 11.45 MB | 11.47 MB | 22.00 MB | 22.00 MB | 376.00 B | 0.00 B | 0.00 B | 0.00 B | | after modifying first data set | 11.46 MB | 11.47 MB | 22.00 MB | 22.00 MB | 1.05 KB | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 1 - Before | 11.46 MB | 11.47 MB | 22.00 MB | 22.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 1 - After | 22.50 MB | 22.52 MB | 44.00 MB | 44.00 MB | 11.04 MB | 11.05 MB | 22.00 MB | 22.00 MB | | Copy Loop 2 - Before | 22.50 MB | 22.52 MB | 44.00 MB | 44.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 2 - After | 22.50 MB | 33.56 MB | 44.00 MB | 66.00 MB | 424.00 B | 11.04 MB | 0.00 B | 22.00 MB | | Copy Loop 3 - Before | 22.50 MB | 33.56 MB | 44.00 MB | 66.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 3 - After | 22.50 MB | 33.56 MB | 44.00 MB | 66.00 MB | 744.00 B | 1.14 KB | 0.00 B | 0.00 B | | Copy Loop 4 - Before | 22.50 MB | 33.56 MB | 44.00 MB | 66.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 4 - After | 22.50 MB | 33.56 MB | 44.00 MB | 66.00 MB | 424.00 B | 848.00 B | 0.00 B | 0.00 B | | Copy Loop 5 - Before | 22.50 MB | 33.56 MB | 44.00 MB | 66.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Copy Loop 5 - After | 22.50 MB | 33.56 MB | 44.00 MB | 66.00 MB | 424.00 B | 848.00 B | 0.00 B | 0.00 B | | Set Loop 1 - Before | 22.50 MB | 33.56 MB | 44.00 MB | 66.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Set Loop 1 - After | 22.50 MB | 33.56 MB | 44.00 MB | 66.00 MB | 424.00 B | 904.00 B | 0.00 B | 0.00 B | | Set Loop 2 - Before | 22.51 MB | 33.56 MB | 44.00 MB | 66.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Set Loop 2 - After | 22.51 MB | 33.57 MB | 44.00 MB | 66.00 MB | 1.04 KB | 1.45 KB | 0.00 B | 0.00 B | | Set Loop 3 - Before | 22.51 MB | 33.57 MB | 44.00 MB | 66.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Set Loop 3 - After | 22.51 MB | 33.57 MB | 44.00 MB | 66.00 MB | 424.00 B | 848.00 B | 0.00 B | 0.00 B | | Set Loop 4 - Before | 22.51 MB | 33.57 MB | 44.00 MB | 66.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Set Loop 4 - After | 22.51 MB | 33.57 MB | 44.00 MB | 66.00 MB | 424.00 B | 848.00 B | 0.00 B | 0.00 B | | Set Loop 5 - Before | 22.51 MB | 33.57 MB | 44.00 MB | 66.00 MB | 424.00 B | 0.00 B | 0.00 B | 0.00 B | | Set Loop 5 - After | 22.51 MB | 33.57 MB | 44.00 MB | 66.00 MB | 424.00 B | 848.00 B | 0.00 B | 0.00 B | +--------------------------------+-----------+-----------+--------------+-------------+-------------+------------+--------------------+-------------------+ I'm not sure why the (Real) values do what they do. Their value depends on whether your running the script in a fresh instance of PHP (ie in CLI/CGI) or reusing an existing instance (ie, FastCGI/FPM). -
It is, as that's the only way you'd get that error. Your other call just may not be explicit which is why you can't find it. It's also why I provided a few hints toward what the problem most likely is, but you haven't seemed to pick up on them yet. So l let's repeat them here so maybe you can get it figured out. Hint the first: Hint the second (with more emphesis):