These are programming principles that, when applied, increase cohesion, loose coupling and encapsulation.
The principles are:
S - The "single responsibility principle" states that each class should have a single responsibility. This makes it easier to understand and modify the code. Imagine a billing procedure that does just the job of billing, rather than one that also sends email notifications.
O - The "open-closed principle" states that a class can be extended, without having to modify it. It's open because you can extend it, but close because you don't have to know the internals but just the interface. In this way, it's possible to add functionality, without breaking the client already using the original service. Imagine an Operation class that defines the interface for an operation and then can be extended to perform sum or multiplication, versus an Operation Class that initially does only sums and then need to be modified to do multiplications as well.
A library will clarify this further: the code cannot be changed (so, it's closed), but you can extend classes from it.
L - The "Liskov substitution principle" states that references to base classes should be able to use derived objects, without knowing it. Imagine a Square class that inherits a Rectangle class. If the inherited methods are setWidth() and setHeight(), do you agree that the Square might have some weird behaviours?
There are some rules: the inherited methods must follow the same invariants, the return types can only restrict and arguments can only widen.
In other words, this is the same as the IS-A relationship.
I - The "interface segregation principle" states that a class should not depend on interfaces it doesn't need. So you should make your interfaces small and targeted, rather than general-purpose. This helps with cohesion and makes the code easy to understand.
D - The "dependency inversion principle" states that you should depend on abstractions. This makes it easier to modify implementations without breaking the code.
It's called inversion because high-level code depends on abstraction rather than low-level code and low-level code depends on the abstraction used by the high-level code. When we modify the low-level code, we don't break the high-level code (the caller), so this favour loose coupling.