Register Arama Bugünkü Mesajlar Tüm Forumu Okundu Say

Reply
 
Thread Tools
[Part 3] Learning the Basics of Coding: Best Practices
vB.Org Poster
vB.Org Poster has disabled reputation
Kayıt Tarihi: Jul 2018
Mesajlar: 298

Show Printable Version Email this Page
yazan vB.Org Poster 28 Nov 2017, 15:43
Oylama: (1 oy - 4.00 ortalama)

The third part of my four part article will focus on better coding: Improving the efficiency and logic of your existing code. This skill literally makes the difference between your novice vb.org hack and a full-fledged modification that people will pay for.

The Basics
I'm going to fully assume you have read or already have an understanding of [Part 1] Learning the Basics of Coding and [Part 2] Learning the Basics of Coding: Database Interaction. I'm going to assume you have knowledge of types, variables, constants, objects, resources, classes, methods, properties, table structures, etc.,. If you don't, you're probably going to get lost.

PHP
PHP is a fast language, capable of performing countless operators in microseconds. However, when poorly coded, something that should have executed in .006 seconds may end up taking .06 seconds. Don't think that's a big difference? Then leave the article - because that's slower by a factor of 10.

loops
Knowing which type of loop to use may seem trivial, but it's like using the wrong type of screwdriver or wrench. Sure, it'll get the job done, but it's going to take longer and waste more energy.
Here are some general rules when trying to figure out what type of loop to use:

for is for when you know how many times you want to execute a loop.

Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.



while
is when you aren't sure how many times, but you want to loop until a condition is met.

Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

Don't know what var_dump is? Google it. It's very useful.

foreach is generally just for looping through an array.

Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

Multiple parameters > string concatenation.

Though all loops have their differences, they all have their similarities too:

Do not use count() or its alias sizeof() in any loop expression.

Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

Why are you calling count() at the beginning of each iteration?


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

count() is called once instead of 100 times. I just saved you some processing time.

Now you may have noticed I am consistently pre-incrementing instead of post-incrementing my $i. Why? It's about 10% faster. Pre-increment where possible!

Let's move on to another common loop problem. Can you spot it?

Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

The problem is the loop-invariant code. A loop-invariant is code that's true outside of the loop and, subsequently, true in each loop iteration. What does this mean? It means you're executing code multiple times when it only needs to be executed once!


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

By moving $i * 10, a value which will not change within the second loop, we are saving processing time. Why calculate ($i * 10) ten times when you can calculate it once? It's like not having memory and asked to do the same equation over and over. You just answered it, so why are you trying to find out the answer again?

functions
Calling user-defined functions is expensive. Whenever possible, don't be afraid to inline a function.


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

You're calling a function a thousand times. Remember when I said count() inside a for was bad? This is even worse! Not just because it's being called 1000 times but because a user-defined function is much slower than a built-in function!


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

This will be exponentially faster.

Now I feel obligated to mention that you shouldn't inline all user-defined functions you possibly can. This is just silly. You should only do this in obvious cases where the function is either hardly used, or is so short it just doesn't make sense. Functions centralize your code and make life easier. Don't throw away a cup because you can drink from the tap! But DO get rid of that old cup your great-aunt gave you that no one ever uses yet it takes up the room of 3 normal cups. Secondly, don't get cocky and try to inline built-in functions, either, because that will be counter-productive. Built-in functions use optimized C code and you're not going to beat that with PHP.

Another problem is sometimes you don't need to use a function at all! You can use a language construct which are faster than functions!

Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

While this code is correct and gets the job done, one must wonder why a function is being used?


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

There we go. Drop the function and use a language construct instead. Not only is it faster but, in my opinion, it looks nicer, too.

use the right function
Don't use preg_split instead of explode when you don't need regular expressions. This goes for any function that supports regex. If you aren't using regex then use an alternative. e.g: str_replace instead of preg_replace.

freeing memory
This should be obvious, but it's something that isn't done as often as it should (sometimes I find code with arrays that should have been unset a long time ago, but that's not even the bad part - the bad part is it's my code.).


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

I've already got all the data from $array into $a so it's no longer needed. Why keep $array around?


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

There we go. Memory freed!

initialize!
This is such an underestimated coding practice. Whenever you are using a variable or an array element you should always be sure it exists before doing anything besides assigning a value to it! Why? Keep reading!


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

It should also be mentioned that initializing your variables can play an importance in security as well. More in [Part 4]. Furthermore, attempting to do something to an undefined variable can also result in: Notice: Undefined variable: $variable_name with the right php.ini configuration (you shouldn't be outputting errors on a live site though).

logic
One thing novice coders often do is making a mess of their logic (I do that a lot, too, actually). Examples include evaluating the same expression multiple times in a script, within an if condition, redundant code, etc. There's a popular principle called "DRY"; it stands for don't repeat yourself.


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

$j was checked twice. Why? Poor logic. Not only is poor logic a waste of processing time, but it's also harder to read and takes longer to type. There's not one benefit to checking $j twice so break a bad habit and rewrite that code.


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

Much better! Cleaner. Faster. Logicaler.

Let's take it up a notch:

Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

How can I write this code better? I want to give admins access, prevent blacklisted access unless they're an admin, and give access to people that have access unless they're blacklisted.

Let's look at a few things first. We have $access = true twice! Certainly we can refactor the code to remove this redundancy? Secondly, it seems we still have not initialized $access! If a user meets none of those conditions then there's a possibility he may get unauthorized access!


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

Because NULL is not identical to false, this person has gained access to a place he shouldn't have!

Thirdly, look at the nesting. For such simple checks do we really need double nested code? Certainly not! So how would you rewrite this code better?


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

That's how I would do it. Satisfies all the requirements and solves all the problems. I've taken it a step further and used identical operators because it's faster and more fool-proof.

misconceptions
I've taken the time to find out many optimization tips for PHP and have found that a few of the more popular ones are absolutely false. I'm going to include them in here because part of writing good code actually involves knowing what good code is and is not.

single quotes vs. double quotes
There's no noticeable difference in performance between using single and double quotes. Single quotes is indeed a bit faster but the percentage is < .01%. If you want to shave a nano-second in parsing time then by all means convert all double quotes to single quotes. Personally, I'm not going to. As a side note: I use single quotes whenever possible but if double quotes make my life easier I will not hesitate to use them.


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

get rid of comments and whitespaces
Don't. You'll waste far more time pressing the backspace button then the amount of processing time saved will ever come close to. In fact, I encourage consistent indentation, spacing and commenting. Keep your code clean, neat, and understandable.


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

obfuscation
The practice of rewriting variables, constants, functions, classes, etc,. to use as little characters as possible. Similar to the above: Don't. "Keep your code clean, neat, and understandable."

general tips
Contrary to the above, I've also taken the time to confirm many common optimization tips.
  • Declare methods static when possible. Improves speed by a factor of 4.
  • Use echo and take advantage of its multiple parameters. In other words, use a comma instead of a period.
  • $row[id] is 7 times faster than $row[id]. Copypasta because everyone's read this one.
  • Never use short tags. Always use <?php and ?
  • For PHP > 5, unset() is faster and uses less memory than type casting null.
MySQL
Going to assume we're in a vB enviornment and that $db is an object, yes? Also going to assume youve read [Part 2] Learning the Basics of Coding: Database Interaction, yes? Good!

query_first
Remember that this doesn't add LIMIT 1. This means that unless querying only one result your query will be inefficient. Why? Imagine you want to buy a bottle of honey. You drive to the database (store), and then you go to the table (aisle), and then you SELECT honey. But wait, you didn't limit yourself to one bottle of honey; you grabbed ALL the bottles of honey. So now you're walking to the check-out with your arm full of bottles of honey and proceed to buy them. Then you drive back home with your bounty of honey and while putting away your groceries you throw out all those bottles of honey and put one inside your cabinet. Understand? If you're going to get more than one result add LIMIT 1!


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

WRONG! What if this thread had tens of thousands of posts? Your server is going to hang! Hope you aren't using bluehost (zing!).


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

Fast!

But to be terribly honest, if you want to select the last post in a thread you should be doing this:

Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

This eliminates the need to sort through the posts and since `threadid` is the primary key in table thread it's going to be extremely fast and efficient.

avoid filesort
Sometimes it's unavoidable, but most of the times it is (avoidable).

Not sure what I mean? Enable debug mode and look in the extra column. It may take you awhile, but you're bound to see "using filesort" or "using temporary".

using temporary
The temporary table where rows are being put in has gotten so big that it needs to be sorted on a disk.

using filesort
A sort that isn't being performed on indexes. Yes, it is horribly named.

To avoid these, in 99% of the cases, you need to either fix your query or fix your table structure. You should never be sorting on non-indexes unless the amount of rows in question is a relatively small amount: say < 1000. This is because with so relatively few rows it's going to be faster sorting 1 by 1 then hitting the indexes anyway.


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

MySQL is going to have to go through ALL of those threads you know...


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

You're hitting the index so you're good.

normalization
This is, truthfully, deserving of an article of its own. I may someday decide to make one, but, for now, I'll touch the basics of this VERY IMPORTANT practice.

What is normalization?

redundancy
Take, for example, the following table structure:
STUDENT (student_id, name, birthday, gender, class)

This is horrible. Multiple classes = multiple nearly identical rows. We want each table to be responsible for one set of data. Lets rework this a bit.

STUDENT (student_id, name, birthday, gender)
CLASS (name, student_id)

This is better, but still terrible. What if the name changes? What if we want to add more data about the class?

We need three tables:
STUDENT (student_id, name, birthday, gender)
CLASS (id, name)
CLASS_STUDENT (class_id, student_id)

num_rows
Like in part 2, I mentioned that you should always check to make sure you have a mysql resource before doing anything to it. This is the ONLY way I know of to do this without causing a warning, notice, fatal error, blackhole, etc.

UPDATE: It seems the fetch_array function actually suppresses errors so num_rows is not TECHNICALLY needed to avoid that nasty error being shown. In fact, I now think it is more efficient to avoid num_rows in cases except where you wish to error out early. Reasoning? It's unnecessary overhead without any difference in actual results. If we weren't within a vB environment I'd fully recommend it but since we are there's no reason to call a function to avoid an error that would be suppressed anyway!


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

This is perfectly acceptable.


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

Contrary to what I thought, this isn't necessary to avoid a nasty error.


Kod Blok Kilitli:      (Kayıt veya giriş yapmalısın)  
Engellenmiş, kayıtlı olmayan veya onay bekleyen kişiler kodlara erişemezler.

Why error out? To avoid running code that has no purpose. Running code that has no purpose is a waste of resources and may even lead to unexpected results.

[Legacy info]
Now why would you want to use num_rows before fetch_array? Besides the error message, let's dig a bit deeper into these functions.

PHP: mysql_num_rows - Manual
As you can see, num_rows either returns the number of rows in the result set or false on failure.

Now what about fetch_array?
PHP: mysql_fetch_array - Manual
It returns an array. There is no false on failure. If you use this function on a non-result set you will get: Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in path/to/script.php on line x

It should be noted that part of being a good coder is to avoid notices, warnings, fatal errors, database errors, etc.
[/Legacy Info]

Welp! Thats about it. I probably should dive into MySQL more but nothing else comes to mind at this ungodly hour. Let me know what you think! Hopefully a few of the more infamous coders around here read this and soak it in.

Source
Views: 126
Reply With Quote
Reply

Thread Tools

Şunları Yapabilirsin
Yeni Konu Açmak
Konuya Cevap Yazmak
Eklenti Eklemek
Düzenleme Yapabilmek

Forum Atla


LD'de Yeni misin? Yardıma mı ihtiyacın var?

All times are GMT +3. The time now is 20:57.

Tasarım Özelliği | Genişlik: Geniş Renk: [Part 3] Learning the Basics of Coding: Best Practices [Part 3] Learning the Basics of Coding: Best Practices [Part 3] Learning the Basics of Coding: Best Practices [Part 3] Learning the Basics of Coding: Best Practices [Part 3] Learning the Basics of Coding: Best Practices